From a1dd5dfba5ed87242ca6498a5baa8a105ee72a64 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Wed, 28 Jun 2023 15:05:32 -0400 Subject: [PATCH 1/3] android: Make MemoryUtil an object --- .../yuzu_emu/activities/EmulationActivity.kt | 5 ++--- .../org/yuzu/yuzu_emu/utils/MemoryUtil.kt | 20 +++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) 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..4052eead5 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 @@ -106,13 +106,12 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { inputHandler = InputHandler() inputHandler.initialize() - val memoryUtil = MemoryUtil(this) - if (memoryUtil.isLessThan(8, MemoryUtil.Gb)) { + if (MemoryUtil.isLessThan(8, MemoryUtil.Gb)) { Toast.makeText( this, getString( R.string.device_memory_inadequate, - memoryUtil.getDeviceRAM(), + MemoryUtil.getDeviceRAM(), "8 ${getString(R.string.memory_gigabyte)}" ), Toast.LENGTH_LONG 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..59e9f8f87 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 @@ -6,13 +6,22 @@ package org.yuzu.yuzu_emu.utils import android.app.ActivityManager import android.content.Context import org.yuzu.yuzu_emu.R +import org.yuzu.yuzu_emu.YuzuApplication import java.util.Locale -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()) + 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 + private fun bytesToSizeUnit(size: Long): String { return when { size < Kb -> "${size.floatForm} ${context.getString(R.string.memory_byte)}" @@ -47,13 +56,4 @@ class MemoryUtil(val context: Context) { 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 - } } From 11991fbd7f8b97a13bb315e7c035b900052df204 Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Wed, 28 Jun 2023 16:51:27 -0400 Subject: [PATCH 2/3] android: Rework MemoryUtil Uses string templates and rounds up memory amount for potentially inaccurate checks now --- .../yuzu_emu/activities/EmulationActivity.kt | 9 +- .../org/yuzu/yuzu_emu/utils/MemoryUtil.kt | 100 ++++++++++++++---- .../app/src/main/res/values/strings.xml | 1 + 3 files changed, 85 insertions(+), 25 deletions(-) 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 4052eead5..2c671fea3 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 @@ -47,6 +47,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,13 +107,17 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { inputHandler = InputHandler() inputHandler.initialize() - if (MemoryUtil.isLessThan(8, MemoryUtil.Gb)) { + if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) { Toast.makeText( this, getString( R.string.device_memory_inadequate, MemoryUtil.getDeviceRAM(), - "8 ${getString(R.string.memory_gigabyte)}" + getString( + R.string.memory_formatted, + NumberFormat.getInstance().format(MemoryUtil.REQUIRED_MEMORY), + getString(R.string.memory_gigabyte) + ) ), Toast.LENGTH_LONG ).show() 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 59e9f8f87..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,44 +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 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) - const val Kb: Long = 1024 + // 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: 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)}" + 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 @@ -51,9 +108,6 @@ object MemoryUtil { Eb -> totalMemory / Eb < minimum else -> totalMemory < Kb && totalMemory < minimum } - } - fun getDeviceRAM(): String { - return bytesToSizeUnit(totalMemory) - } + 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 From ff6d35f2c780df5e3604979eef0d5b7c194617ea Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Fri, 30 Jun 2023 13:46:35 -0400 Subject: [PATCH 3/3] android: Show memory warning once --- .../yuzu_emu/activities/EmulationActivity.kt | 35 ++++++++++++------- .../features/settings/model/Settings.kt | 2 ++ 2 files changed, 24 insertions(+), 13 deletions(-) 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 2c671fea3..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 @@ -107,20 +110,26 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { inputHandler = InputHandler() inputHandler.initialize() - if (MemoryUtil.isLessThan(MemoryUtil.REQUIRED_MEMORY, MemoryUtil.Gb)) { - Toast.makeText( - this, - getString( - R.string.device_memory_inadequate, - MemoryUtil.getDeviceRAM(), + 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.memory_formatted, - NumberFormat.getInstance().format(MemoryUtil.REQUIRED_MEMORY), - getString(R.string.memory_gigabyte) - ) - ), - Toast.LENGTH_LONG - ).show() + 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"