diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
index ae665ed2e..7461fb093 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt
@@ -34,11 +34,14 @@ import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.navigation.fragment.NavHostFragment
+import androidx.preference.PreferenceManager
import org.yuzu.yuzu_emu.NativeLibrary
import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.YuzuApplication
import org.yuzu.yuzu_emu.databinding.ActivityEmulationBinding
import org.yuzu.yuzu_emu.features.settings.model.BooleanSetting
import org.yuzu.yuzu_emu.features.settings.model.IntSetting
+import org.yuzu.yuzu_emu.features.settings.model.Settings
import org.yuzu.yuzu_emu.features.settings.model.SettingsViewModel
import org.yuzu.yuzu_emu.model.Game
import org.yuzu.yuzu_emu.utils.ControllerMappingHelper
@@ -47,6 +50,7 @@ import org.yuzu.yuzu_emu.utils.InputHandler
import org.yuzu.yuzu_emu.utils.MemoryUtil
import org.yuzu.yuzu_emu.utils.NfcReader
import org.yuzu.yuzu_emu.utils.ThemeHelper
+import java.text.NumberFormat
import kotlin.math.roundToInt
class EmulationActivity : AppCompatActivity(), SensorEventListener {
@@ -106,17 +110,26 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener {
inputHandler = InputHandler()
inputHandler.initialize()
- val memoryUtil = MemoryUtil(this)
- if (memoryUtil.isLessThan(8, MemoryUtil.Gb)) {
- Toast.makeText(
- this,
- getString(
- R.string.device_memory_inadequate,
- memoryUtil.getDeviceRAM(),
- "8 ${getString(R.string.memory_gigabyte)}"
- ),
- Toast.LENGTH_LONG
- ).show()
+ val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
+ if (!preferences.getBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, false)) {
+ if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) {
+ Toast.makeText(
+ this,
+ getString(
+ R.string.device_memory_inadequate,
+ MemoryUtil.getDeviceRAM(),
+ getString(
+ R.string.memory_formatted,
+ NumberFormat.getInstance().format(MemoryUtil.REQUIRED_MEMORY),
+ getString(R.string.memory_gigabyte)
+ )
+ ),
+ Toast.LENGTH_LONG
+ ).show()
+ preferences.edit()
+ .putBoolean(Settings.PREF_MEMORY_WARNING_SHOWN, true)
+ .apply()
+ }
}
// Start a foreground service to prevent the app from getting killed in the background
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
index 88afb2223..be6e17e65 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt
@@ -110,6 +110,8 @@ class Settings {
const val SECTION_THEME = "Theme"
const val SECTION_DEBUG = "Debug"
+ const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
+
const val PREF_OVERLAY_INIT = "OverlayInit"
const val PREF_CONTROL_SCALE = "controlScale"
const val PREF_CONTROL_OPACITY = "controlOpacity"
diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt
index 18e5fa0b0..aa4a5539a 100644
--- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt
+++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/utils/MemoryUtil.kt
@@ -5,35 +5,101 @@ package org.yuzu.yuzu_emu.utils
import android.app.ActivityManager
import android.content.Context
+import android.os.Build
import org.yuzu.yuzu_emu.R
+import org.yuzu.yuzu_emu.YuzuApplication
import java.util.Locale
+import kotlin.math.ceil
-class MemoryUtil(val context: Context) {
+object MemoryUtil {
+ private val context get() = YuzuApplication.appContext
- private val Long.floatForm: String
- get() = String.format(Locale.ROOT, "%.2f", this.toDouble())
+ private val Float.hundredths: String
+ get() = String.format(Locale.ROOT, "%.2f", this)
- private fun bytesToSizeUnit(size: Long): String {
- return when {
- size < Kb -> "${size.floatForm} ${context.getString(R.string.memory_byte)}"
- size < Mb -> "${(size / Kb).floatForm} ${context.getString(R.string.memory_kilobyte)}"
- size < Gb -> "${(size / Mb).floatForm} ${context.getString(R.string.memory_megabyte)}"
- size < Tb -> "${(size / Gb).floatForm} ${context.getString(R.string.memory_gigabyte)}"
- size < Pb -> "${(size / Tb).floatForm} ${context.getString(R.string.memory_terabyte)}"
- size < Eb -> "${(size / Pb).floatForm} ${context.getString(R.string.memory_petabyte)}"
- else -> "${(size / Eb).floatForm} ${context.getString(R.string.memory_exabyte)}"
+ // Required total system memory
+ const val REQUIRED_MEMORY = 8
+
+ const val Kb: Float = 1024F
+ const val Mb = Kb * 1024
+ const val Gb = Mb * 1024
+ const val Tb = Gb * 1024
+ const val Pb = Tb * 1024
+ const val Eb = Pb * 1024
+
+ private fun bytesToSizeUnit(size: Float): String =
+ when {
+ size < Kb -> {
+ context.getString(
+ R.string.memory_formatted,
+ size.hundredths,
+ context.getString(R.string.memory_byte)
+ )
+ }
+ size < Mb -> {
+ context.getString(
+ R.string.memory_formatted,
+ (size / Kb).hundredths,
+ context.getString(R.string.memory_kilobyte)
+ )
+ }
+ size < Gb -> {
+ context.getString(
+ R.string.memory_formatted,
+ (size / Mb).hundredths,
+ context.getString(R.string.memory_megabyte)
+ )
+ }
+ size < Tb -> {
+ context.getString(
+ R.string.memory_formatted,
+ (size / Gb).hundredths,
+ context.getString(R.string.memory_gigabyte)
+ )
+ }
+ size < Pb -> {
+ context.getString(
+ R.string.memory_formatted,
+ (size / Tb).hundredths,
+ context.getString(R.string.memory_terabyte)
+ )
+ }
+ size < Eb -> {
+ context.getString(
+ R.string.memory_formatted,
+ (size / Pb).hundredths,
+ context.getString(R.string.memory_petabyte)
+ )
+ }
+ else -> {
+ context.getString(
+ R.string.memory_formatted,
+ (size / Eb).hundredths,
+ context.getString(R.string.memory_exabyte)
+ )
+ }
}
- }
- private val totalMemory =
- with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
+ // Devices are unlikely to have 0.5GB increments of memory so we'll just round up to account for
+ // the potential error created by memInfo.totalMem
+ private val totalMemory: Float
+ get() {
val memInfo = ActivityManager.MemoryInfo()
- getMemoryInfo(memInfo)
- memInfo.totalMem
+ with(context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager) {
+ getMemoryInfo(memInfo)
+ }
+
+ return ceil(
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ memInfo.advertisedMem.toFloat()
+ } else {
+ memInfo.totalMem.toFloat()
+ }
+ )
}
- fun isLessThan(minimum: Int, size: Long): Boolean {
- return when (size) {
+ fun isLessThan(minimum: Int, size: Float): Boolean =
+ when (size) {
Kb -> totalMemory < Mb && totalMemory < minimum
Mb -> totalMemory < Gb && (totalMemory / Mb) < minimum
Gb -> totalMemory < Tb && (totalMemory / Gb) < minimum
@@ -42,18 +108,6 @@ class MemoryUtil(val context: Context) {
Eb -> totalMemory / Eb < minimum
else -> totalMemory < Kb && totalMemory < minimum
}
- }
- fun getDeviceRAM(): String {
- return bytesToSizeUnit(totalMemory)
- }
-
- companion object {
- const val Kb: Long = 1024
- const val Mb = Kb * 1024
- const val Gb = Mb * 1024
- const val Tb = Gb * 1024
- const val Pb = Tb * 1024
- const val Eb = Pb * 1024
- }
+ fun getDeviceRAM(): String = bytesToSizeUnit(totalMemory)
}
diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml
index af7450619..b3c737979 100644
--- a/src/android/app/src/main/res/values/strings.xml
+++ b/src/android/app/src/main/res/values/strings.xml
@@ -273,6 +273,7 @@
A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.
Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.
Device RAM: %1$s\nRecommended: %2$s
+ %1$s %2$s
Japan