diff options
Diffstat (limited to 'src/android')
15 files changed, 868 insertions, 214 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..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..a6251bafd 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,25 +110,38 @@ class Settings { const val SECTION_THEME = "Theme" const val SECTION_DEBUG = "Debug" - const val PREF_OVERLAY_INIT = "OverlayInit" + const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" + + const val PREF_OVERLAY_VERSION = "OverlayVersion" + const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion" + const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion" + const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion" + val overlayLayoutPrefs = listOf( + PREF_LANDSCAPE_OVERLAY_VERSION, + PREF_PORTRAIT_OVERLAY_VERSION, + PREF_FOLDABLE_OVERLAY_VERSION + ) + const val PREF_CONTROL_SCALE = "controlScale" const val PREF_CONTROL_OPACITY = "controlOpacity" const val PREF_TOUCH_ENABLED = "isTouchEnabled" - const val PREF_BUTTON_TOGGLE_0 = "buttonToggle0" - const val PREF_BUTTON_TOGGLE_1 = "buttonToggle1" - const val PREF_BUTTON_TOGGLE_2 = "buttonToggle2" - const val PREF_BUTTON_TOGGLE_3 = "buttonToggle3" - const val PREF_BUTTON_TOGGLE_4 = "buttonToggle4" - const val PREF_BUTTON_TOGGLE_5 = "buttonToggle5" - const val PREF_BUTTON_TOGGLE_6 = "buttonToggle6" - const val PREF_BUTTON_TOGGLE_7 = "buttonToggle7" - const val PREF_BUTTON_TOGGLE_8 = "buttonToggle8" - const val PREF_BUTTON_TOGGLE_9 = "buttonToggle9" - const val PREF_BUTTON_TOGGLE_10 = "buttonToggle10" - const val PREF_BUTTON_TOGGLE_11 = "buttonToggle11" - const val PREF_BUTTON_TOGGLE_12 = "buttonToggle12" - const val PREF_BUTTON_TOGGLE_13 = "buttonToggle13" - const val PREF_BUTTON_TOGGLE_14 = "buttonToggle14" + const val PREF_BUTTON_A = "buttonToggle0" + const val PREF_BUTTON_B = "buttonToggle1" + const val PREF_BUTTON_X = "buttonToggle2" + const val PREF_BUTTON_Y = "buttonToggle3" + const val PREF_BUTTON_L = "buttonToggle4" + const val PREF_BUTTON_R = "buttonToggle5" + const val PREF_BUTTON_ZL = "buttonToggle6" + const val PREF_BUTTON_ZR = "buttonToggle7" + const val PREF_BUTTON_PLUS = "buttonToggle8" + const val PREF_BUTTON_MINUS = "buttonToggle9" + const val PREF_BUTTON_DPAD = "buttonToggle10" + const val PREF_STICK_L = "buttonToggle11" + const val PREF_STICK_R = "buttonToggle12" + const val PREF_BUTTON_STICK_L = "buttonToggle13" + const val PREF_BUTTON_STICK_R = "buttonToggle14" + const val PREF_BUTTON_HOME = "buttonToggle15" + const val PREF_BUTTON_SCREENSHOT = "buttonToggle16" const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" @@ -143,6 +156,30 @@ class Settings { private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap() + val overlayPreferences = listOf( + PREF_OVERLAY_VERSION, + PREF_CONTROL_SCALE, + PREF_CONTROL_OPACITY, + PREF_TOUCH_ENABLED, + PREF_BUTTON_A, + PREF_BUTTON_B, + PREF_BUTTON_X, + PREF_BUTTON_Y, + PREF_BUTTON_L, + PREF_BUTTON_R, + PREF_BUTTON_ZL, + PREF_BUTTON_ZR, + PREF_BUTTON_PLUS, + PREF_BUTTON_MINUS, + PREF_BUTTON_DPAD, + PREF_STICK_L, + PREF_STICK_R, + PREF_BUTTON_HOME, + PREF_BUTTON_SCREENSHOT, + PREF_BUTTON_STICK_L, + PREF_BUTTON_STICK_R + ) + const val LayoutOption_Unspecified = 0 const val LayoutOption_MobilePortrait = 4 const val LayoutOption_MobileLandscape = 5 diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt index 09976db62..0e7c1ba88 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt @@ -212,9 +212,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { } if (!isInFoldableLayout) { if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { - binding.surfaceInputOverlay.orientation = InputOverlay.PORTRAIT + binding.surfaceInputOverlay.layout = InputOverlay.PORTRAIT } else { - binding.surfaceInputOverlay.orientation = InputOverlay.LANDSCAPE + binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE } } if (!binding.surfaceInputOverlay.isInEditMode) { @@ -260,7 +260,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { .remove(Settings.PREF_CONTROL_SCALE) .remove(Settings.PREF_CONTROL_OPACITY) .apply() - binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.resetButtonPlacement() } + binding.surfaceInputOverlay.post { + binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement() + } } private fun updateShowFpsOverlay() { @@ -337,7 +339,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { binding.inGameMenu.layoutParams.height = it.bounds.bottom isInFoldableLayout = true - binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE + binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE refreshInputOverlay() } } @@ -410,9 +412,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { R.id.menu_toggle_controls -> { val preferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) - val optionsArray = BooleanArray(15) - for (i in 0..14) { - optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 13) + val optionsArray = BooleanArray(Settings.overlayPreferences.size) + Settings.overlayPreferences.forEachIndexed { i, _ -> + optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 15) } val dialog = MaterialAlertDialogBuilder(requireContext()) @@ -436,7 +438,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback { dialog.getButton(AlertDialog.BUTTON_NEUTRAL) .setOnClickListener { val isChecked = !optionsArray[0] - for (i in 0..14) { + Settings.overlayPreferences.forEachIndexed { i, _ -> optionsArray[i] = isChecked dialog.listView.setItemChecked(i, isChecked) preferences.edit() diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt index 6251ec783..c055c2e35 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt @@ -51,15 +51,23 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : private lateinit var windowInsets: WindowInsets - var orientation = LANDSCAPE + var layout = LANDSCAPE override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { super.onLayout(changed, left, top, right, bottom) windowInsets = rootWindowInsets - if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) { - defaultOverlay() + val overlayVersion = preferences.getInt(Settings.PREF_OVERLAY_VERSION, 0) + if (overlayVersion != OVERLAY_VERSION) { + resetAllLayouts() + } else { + val layoutIndex = overlayLayouts.indexOf(layout) + val currentLayoutVersion = + preferences.getInt(Settings.overlayLayoutPrefs[layoutIndex], 0) + if (currentLayoutVersion != overlayLayoutVersions[layoutIndex]) { + resetCurrentLayout() + } } // Load the controls. @@ -266,10 +274,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) { // Persist button position by saving new place. saveControlPosition( - buttonBeingConfigured!!.buttonId, + buttonBeingConfigured!!.prefId, buttonBeingConfigured!!.bounds.centerX(), buttonBeingConfigured!!.bounds.centerY(), - orientation + layout ) buttonBeingConfigured = null } @@ -299,10 +307,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) { // Persist button position by saving new place. saveControlPosition( - dpadBeingConfigured!!.upId, + Settings.PREF_BUTTON_DPAD, dpadBeingConfigured!!.bounds.centerX(), dpadBeingConfigured!!.bounds.centerY(), - orientation + layout ) dpadBeingConfigured = null } @@ -330,10 +338,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP -> if (joystickBeingConfigured != null) { saveControlPosition( - joystickBeingConfigured!!.buttonId, + joystickBeingConfigured!!.prefId, joystickBeingConfigured!!.bounds.centerX(), joystickBeingConfigured!!.bounds.centerY(), - orientation + layout ) joystickBeingConfigured = null } @@ -343,9 +351,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : return true } - private fun addOverlayControls(orientation: String) { + private fun addOverlayControls(layout: String) { val windowSize = getSafeScreenSize(context) - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_0, true)) { + if (preferences.getBoolean(Settings.PREF_BUTTON_A, true)) { overlayButtons.add( initializeOverlayButton( context, @@ -353,11 +361,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.facebutton_a, R.drawable.facebutton_a_depressed, ButtonType.BUTTON_A, - orientation + Settings.PREF_BUTTON_A, + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_1, true)) { + if (preferences.getBoolean(Settings.PREF_BUTTON_B, true)) { overlayButtons.add( initializeOverlayButton( context, @@ -365,11 +374,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.facebutton_b, R.drawable.facebutton_b_depressed, ButtonType.BUTTON_B, - orientation + Settings.PREF_BUTTON_B, + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_2, true)) { + if (preferences.getBoolean(Settings.PREF_BUTTON_X, true)) { overlayButtons.add( initializeOverlayButton( context, @@ -377,11 +387,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.facebutton_x, R.drawable.facebutton_x_depressed, ButtonType.BUTTON_X, - orientation + Settings.PREF_BUTTON_X, + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_3, true)) { + if (preferences.getBoolean(Settings.PREF_BUTTON_Y, true)) { overlayButtons.add( initializeOverlayButton( context, @@ -389,11 +400,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.facebutton_y, R.drawable.facebutton_y_depressed, ButtonType.BUTTON_Y, - orientation + Settings.PREF_BUTTON_Y, + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_4, true)) { + if (preferences.getBoolean(Settings.PREF_BUTTON_L, true)) { overlayButtons.add( initializeOverlayButton( context, @@ -401,11 +413,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.l_shoulder, R.drawable.l_shoulder_depressed, ButtonType.TRIGGER_L, - orientation + Settings.PREF_BUTTON_L, + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_5, true)) { + if (preferences.getBoolean(Settings.PREF_BUTTON_R, true)) { overlayButtons.add( initializeOverlayButton( context, @@ -413,11 +426,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.r_shoulder, R.drawable.r_shoulder_depressed, ButtonType.TRIGGER_R, - orientation + Settings.PREF_BUTTON_R, + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_6, true)) { + if (preferences.getBoolean(Settings.PREF_BUTTON_ZL, true)) { overlayButtons.add( initializeOverlayButton( context, @@ -425,11 +439,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.zl_trigger, R.drawable.zl_trigger_depressed, ButtonType.TRIGGER_ZL, - orientation + Settings.PREF_BUTTON_ZL, + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_7, true)) { + if (preferences.getBoolean(Settings.PREF_BUTTON_ZR, true)) { overlayButtons.add( initializeOverlayButton( context, @@ -437,11 +452,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.zr_trigger, R.drawable.zr_trigger_depressed, ButtonType.TRIGGER_ZR, - orientation + Settings.PREF_BUTTON_ZR, + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_8, true)) { + if (preferences.getBoolean(Settings.PREF_BUTTON_PLUS, true)) { overlayButtons.add( initializeOverlayButton( context, @@ -449,11 +465,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.facebutton_plus, R.drawable.facebutton_plus_depressed, ButtonType.BUTTON_PLUS, - orientation + Settings.PREF_BUTTON_PLUS, + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_9, true)) { + if (preferences.getBoolean(Settings.PREF_BUTTON_MINUS, true)) { overlayButtons.add( initializeOverlayButton( context, @@ -461,11 +478,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.facebutton_minus, R.drawable.facebutton_minus_depressed, ButtonType.BUTTON_MINUS, - orientation + Settings.PREF_BUTTON_MINUS, + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_10, true)) { + if (preferences.getBoolean(Settings.PREF_BUTTON_DPAD, true)) { overlayDpads.add( initializeOverlayDpad( context, @@ -473,11 +491,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.dpad_standard, R.drawable.dpad_standard_cardinal_depressed, R.drawable.dpad_standard_diagonal_depressed, - orientation + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_11, true)) { + if (preferences.getBoolean(Settings.PREF_STICK_L, true)) { overlayJoysticks.add( initializeOverlayJoystick( context, @@ -487,11 +505,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.joystick_depressed, StickType.STICK_L, ButtonType.STICK_L, - orientation + Settings.PREF_STICK_L, + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_12, true)) { + if (preferences.getBoolean(Settings.PREF_STICK_R, true)) { overlayJoysticks.add( initializeOverlayJoystick( context, @@ -501,11 +520,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.joystick_depressed, StickType.STICK_R, ButtonType.STICK_R, - orientation + Settings.PREF_STICK_R, + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_13, false)) { + if (preferences.getBoolean(Settings.PREF_BUTTON_HOME, false)) { overlayButtons.add( initializeOverlayButton( context, @@ -513,11 +533,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.facebutton_home, R.drawable.facebutton_home_depressed, ButtonType.BUTTON_HOME, - orientation + Settings.PREF_BUTTON_HOME, + layout ) ) } - if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_14, false)) { + if (preferences.getBoolean(Settings.PREF_BUTTON_SCREENSHOT, false)) { overlayButtons.add( initializeOverlayButton( context, @@ -525,7 +546,34 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.drawable.facebutton_screenshot, R.drawable.facebutton_screenshot_depressed, ButtonType.BUTTON_CAPTURE, - orientation + Settings.PREF_BUTTON_SCREENSHOT, + layout + ) + ) + } + if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_L, true)) { + overlayButtons.add( + initializeOverlayButton( + context, + windowSize, + R.drawable.button_l3, + R.drawable.button_l3_depressed, + ButtonType.STICK_L, + Settings.PREF_BUTTON_STICK_L, + layout + ) + ) + } + if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_R, true)) { + overlayButtons.add( + initializeOverlayButton( + context, + windowSize, + R.drawable.button_r3, + R.drawable.button_r3_depressed, + ButtonType.STICK_R, + Settings.PREF_BUTTON_STICK_R, + layout ) ) } @@ -539,18 +587,18 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : // Add all the enabled overlay items back to the HashSet. if (EmulationMenuSettings.showOverlay) { - addOverlayControls(orientation) + addOverlayControls(layout) } invalidate() } - private fun saveControlPosition(sharedPrefsId: Int, x: Int, y: Int, orientation: String) { + private fun saveControlPosition(prefId: String, x: Int, y: Int, layout: String) { val windowSize = getSafeScreenSize(context) val min = windowSize.first val max = windowSize.second PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() - .putFloat("$sharedPrefsId-X$orientation", (x - min.x).toFloat() / max.x) - .putFloat("$sharedPrefsId-Y$orientation", (y - min.y).toFloat() / max.y) + .putFloat("$prefId-X$layout", (x - min.x).toFloat() / max.x) + .putFloat("$prefId-Y$layout", (y - min.y).toFloat() / max.y) .apply() } @@ -558,19 +606,31 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : inEditMode = editMode } - private fun defaultOverlay() { - if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) { - defaultOverlayByLayout(orientation) - } - - resetButtonPlacement() + private fun resetCurrentLayout() { + defaultOverlayByLayout(layout) + val layoutIndex = overlayLayouts.indexOf(layout) preferences.edit() - .putBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", true) + .putInt(Settings.overlayLayoutPrefs[layoutIndex], overlayLayoutVersions[layoutIndex]) .apply() } - fun resetButtonPlacement() { - defaultOverlayByLayout(orientation) + private fun resetAllLayouts() { + val editor = preferences.edit() + overlayLayouts.forEachIndexed { i, layout -> + defaultOverlayByLayout(layout) + editor.putInt(Settings.overlayLayoutPrefs[i], overlayLayoutVersions[i]) + } + editor.putInt(Settings.PREF_OVERLAY_VERSION, OVERLAY_VERSION) + editor.apply() + } + + fun resetLayoutVisibilityAndPlacement() { + defaultOverlayByLayout(layout) + val editor = preferences.edit() + Settings.overlayPreferences.forEachIndexed { _, pref -> + editor.remove(pref) + } + editor.apply() refreshControls() } @@ -604,7 +664,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.integer.SWITCH_STICK_R_X, R.integer.SWITCH_STICK_R_Y, R.integer.SWITCH_STICK_L_X, - R.integer.SWITCH_STICK_L_Y + R.integer.SWITCH_STICK_L_Y, + R.integer.SWITCH_BUTTON_STICK_L_X, + R.integer.SWITCH_BUTTON_STICK_L_Y, + R.integer.SWITCH_BUTTON_STICK_R_X, + R.integer.SWITCH_BUTTON_STICK_R_Y ) private val portraitResources = arrayOf( @@ -637,7 +701,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.integer.SWITCH_STICK_R_X_PORTRAIT, R.integer.SWITCH_STICK_R_Y_PORTRAIT, R.integer.SWITCH_STICK_L_X_PORTRAIT, - R.integer.SWITCH_STICK_L_Y_PORTRAIT + R.integer.SWITCH_STICK_L_Y_PORTRAIT, + R.integer.SWITCH_BUTTON_STICK_L_X_PORTRAIT, + R.integer.SWITCH_BUTTON_STICK_L_Y_PORTRAIT, + R.integer.SWITCH_BUTTON_STICK_R_X_PORTRAIT, + R.integer.SWITCH_BUTTON_STICK_R_Y_PORTRAIT ) private val foldableResources = arrayOf( @@ -670,139 +738,159 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : R.integer.SWITCH_STICK_R_X_FOLDABLE, R.integer.SWITCH_STICK_R_Y_FOLDABLE, R.integer.SWITCH_STICK_L_X_FOLDABLE, - R.integer.SWITCH_STICK_L_Y_FOLDABLE + R.integer.SWITCH_STICK_L_Y_FOLDABLE, + R.integer.SWITCH_BUTTON_STICK_L_X_FOLDABLE, + R.integer.SWITCH_BUTTON_STICK_L_Y_FOLDABLE, + R.integer.SWITCH_BUTTON_STICK_R_X_FOLDABLE, + R.integer.SWITCH_BUTTON_STICK_R_Y_FOLDABLE ) - private fun getResourceValue(orientation: String, position: Int): Float { - return when (orientation) { + private fun getResourceValue(layout: String, position: Int): Float { + return when (layout) { PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000 FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000 else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000 } } - private fun defaultOverlayByLayout(orientation: String) { + private fun defaultOverlayByLayout(layout: String) { // Each value represents the position of the button in relation to the screen size without insets. preferences.edit() .putFloat( - ButtonType.BUTTON_A.toString() + "-X$orientation", - getResourceValue(orientation, 0) + "${Settings.PREF_BUTTON_A}-X$layout", + getResourceValue(layout, 0) + ) + .putFloat( + "${Settings.PREF_BUTTON_A}-Y$layout", + getResourceValue(layout, 1) + ) + .putFloat( + "${Settings.PREF_BUTTON_B}-X$layout", + getResourceValue(layout, 2) + ) + .putFloat( + "${Settings.PREF_BUTTON_B}-Y$layout", + getResourceValue(layout, 3) + ) + .putFloat( + "${Settings.PREF_BUTTON_X}-X$layout", + getResourceValue(layout, 4) ) .putFloat( - ButtonType.BUTTON_A.toString() + "-Y$orientation", - getResourceValue(orientation, 1) + "${Settings.PREF_BUTTON_X}-Y$layout", + getResourceValue(layout, 5) ) .putFloat( - ButtonType.BUTTON_B.toString() + "-X$orientation", - getResourceValue(orientation, 2) + "${Settings.PREF_BUTTON_Y}-X$layout", + getResourceValue(layout, 6) ) .putFloat( - ButtonType.BUTTON_B.toString() + "-Y$orientation", - getResourceValue(orientation, 3) + "${Settings.PREF_BUTTON_Y}-Y$layout", + getResourceValue(layout, 7) ) .putFloat( - ButtonType.BUTTON_X.toString() + "-X$orientation", - getResourceValue(orientation, 4) + "${Settings.PREF_BUTTON_ZL}-X$layout", + getResourceValue(layout, 8) ) .putFloat( - ButtonType.BUTTON_X.toString() + "-Y$orientation", - getResourceValue(orientation, 5) + "${Settings.PREF_BUTTON_ZL}-Y$layout", + getResourceValue(layout, 9) ) .putFloat( - ButtonType.BUTTON_Y.toString() + "-X$orientation", - getResourceValue(orientation, 6) + "${Settings.PREF_BUTTON_ZR}-X$layout", + getResourceValue(layout, 10) ) .putFloat( - ButtonType.BUTTON_Y.toString() + "-Y$orientation", - getResourceValue(orientation, 7) + "${Settings.PREF_BUTTON_ZR}-Y$layout", + getResourceValue(layout, 11) ) .putFloat( - ButtonType.TRIGGER_ZL.toString() + "-X$orientation", - getResourceValue(orientation, 8) + "${Settings.PREF_BUTTON_DPAD}-X$layout", + getResourceValue(layout, 12) ) .putFloat( - ButtonType.TRIGGER_ZL.toString() + "-Y$orientation", - getResourceValue(orientation, 9) + "${Settings.PREF_BUTTON_DPAD}-Y$layout", + getResourceValue(layout, 13) ) .putFloat( - ButtonType.TRIGGER_ZR.toString() + "-X$orientation", - getResourceValue(orientation, 10) + "${Settings.PREF_BUTTON_L}-X$layout", + getResourceValue(layout, 14) ) .putFloat( - ButtonType.TRIGGER_ZR.toString() + "-Y$orientation", - getResourceValue(orientation, 11) + "${Settings.PREF_BUTTON_L}-Y$layout", + getResourceValue(layout, 15) ) .putFloat( - ButtonType.DPAD_UP.toString() + "-X$orientation", - getResourceValue(orientation, 12) + "${Settings.PREF_BUTTON_R}-X$layout", + getResourceValue(layout, 16) ) .putFloat( - ButtonType.DPAD_UP.toString() + "-Y$orientation", - getResourceValue(orientation, 13) + "${Settings.PREF_BUTTON_R}-Y$layout", + getResourceValue(layout, 17) ) .putFloat( - ButtonType.TRIGGER_L.toString() + "-X$orientation", - getResourceValue(orientation, 14) + "${Settings.PREF_BUTTON_PLUS}-X$layout", + getResourceValue(layout, 18) ) .putFloat( - ButtonType.TRIGGER_L.toString() + "-Y$orientation", - getResourceValue(orientation, 15) + "${Settings.PREF_BUTTON_PLUS}-Y$layout", + getResourceValue(layout, 19) ) .putFloat( - ButtonType.TRIGGER_R.toString() + "-X$orientation", - getResourceValue(orientation, 16) + "${Settings.PREF_BUTTON_MINUS}-X$layout", + getResourceValue(layout, 20) ) .putFloat( - ButtonType.TRIGGER_R.toString() + "-Y$orientation", - getResourceValue(orientation, 17) + "${Settings.PREF_BUTTON_MINUS}-Y$layout", + getResourceValue(layout, 21) ) .putFloat( - ButtonType.BUTTON_PLUS.toString() + "-X$orientation", - getResourceValue(orientation, 18) + "${Settings.PREF_BUTTON_HOME}-X$layout", + getResourceValue(layout, 22) ) .putFloat( - ButtonType.BUTTON_PLUS.toString() + "-Y$orientation", - getResourceValue(orientation, 19) + "${Settings.PREF_BUTTON_HOME}-Y$layout", + getResourceValue(layout, 23) ) .putFloat( - ButtonType.BUTTON_MINUS.toString() + "-X$orientation", - getResourceValue(orientation, 20) + "${Settings.PREF_BUTTON_SCREENSHOT}-X$layout", + getResourceValue(layout, 24) ) .putFloat( - ButtonType.BUTTON_MINUS.toString() + "-Y$orientation", - getResourceValue(orientation, 21) + "${Settings.PREF_BUTTON_SCREENSHOT}-Y$layout", + getResourceValue(layout, 25) ) .putFloat( - ButtonType.BUTTON_HOME.toString() + "-X$orientation", - getResourceValue(orientation, 22) + "${Settings.PREF_STICK_R}-X$layout", + getResourceValue(layout, 26) ) .putFloat( - ButtonType.BUTTON_HOME.toString() + "-Y$orientation", - getResourceValue(orientation, 23) + "${Settings.PREF_STICK_R}-Y$layout", + getResourceValue(layout, 27) ) .putFloat( - ButtonType.BUTTON_CAPTURE.toString() + "-X$orientation", - getResourceValue(orientation, 24) + "${Settings.PREF_STICK_L}-X$layout", + getResourceValue(layout, 28) ) .putFloat( - ButtonType.BUTTON_CAPTURE.toString() + "-Y$orientation", - getResourceValue(orientation, 25) + "${Settings.PREF_STICK_L}-Y$layout", + getResourceValue(layout, 29) ) .putFloat( - ButtonType.STICK_R.toString() + "-X$orientation", - getResourceValue(orientation, 26) + "${Settings.PREF_BUTTON_STICK_L}-X$layout", + getResourceValue(layout, 30) ) .putFloat( - ButtonType.STICK_R.toString() + "-Y$orientation", - getResourceValue(orientation, 27) + "${Settings.PREF_BUTTON_STICK_L}-Y$layout", + getResourceValue(layout, 31) ) .putFloat( - ButtonType.STICK_L.toString() + "-X$orientation", - getResourceValue(orientation, 28) + "${Settings.PREF_BUTTON_STICK_R}-X$layout", + getResourceValue(layout, 32) ) .putFloat( - ButtonType.STICK_L.toString() + "-Y$orientation", - getResourceValue(orientation, 29) + "${Settings.PREF_BUTTON_STICK_R}-Y$layout", + getResourceValue(layout, 33) ) .apply() } @@ -812,12 +900,30 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : } companion object { + // Increase this number every time there is a breaking change to every overlay layout + const val OVERLAY_VERSION = 1 + + // Increase the corresponding layout version number whenever that layout has a breaking change + private const val LANDSCAPE_OVERLAY_VERSION = 1 + private const val PORTRAIT_OVERLAY_VERSION = 1 + private const val FOLDABLE_OVERLAY_VERSION = 1 + val overlayLayoutVersions = listOf( + LANDSCAPE_OVERLAY_VERSION, + PORTRAIT_OVERLAY_VERSION, + FOLDABLE_OVERLAY_VERSION + ) + private val preferences: SharedPreferences = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) - const val LANDSCAPE = "" + const val LANDSCAPE = "_Landscape" const val PORTRAIT = "_Portrait" const val FOLDABLE = "_Foldable" + val overlayLayouts = listOf( + LANDSCAPE, + PORTRAIT, + FOLDABLE + ) /** * Resizes a [Bitmap] by a given scale factor @@ -948,6 +1054,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State). * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State). * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents. + * @param prefId Identifier for determining where a button appears on screen. + * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE]. * @return An [InputOverlayDrawableButton] with the correct drawing bounds set. */ private fun initializeOverlayButton( @@ -956,7 +1064,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : defaultResId: Int, pressedResId: Int, buttonId: Int, - orientation: String + prefId: String, + layout: String ): InputOverlayDrawableButton { // Resources handle for fetching the initial Drawable resource. val res = context.resources @@ -964,17 +1073,20 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton. val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) - // Decide scale based on button ID and user preference - var scale: Float = when (buttonId) { - ButtonType.BUTTON_HOME, - ButtonType.BUTTON_CAPTURE, - ButtonType.BUTTON_PLUS, - ButtonType.BUTTON_MINUS -> 0.07f + // Decide scale based on button preference ID and user preference + var scale: Float = when (prefId) { + Settings.PREF_BUTTON_HOME, + Settings.PREF_BUTTON_SCREENSHOT, + Settings.PREF_BUTTON_PLUS, + Settings.PREF_BUTTON_MINUS -> 0.07f - ButtonType.TRIGGER_L, - ButtonType.TRIGGER_R, - ButtonType.TRIGGER_ZL, - ButtonType.TRIGGER_ZR -> 0.26f + Settings.PREF_BUTTON_L, + Settings.PREF_BUTTON_R, + Settings.PREF_BUTTON_ZL, + Settings.PREF_BUTTON_ZR -> 0.26f + + Settings.PREF_BUTTON_STICK_L, + Settings.PREF_BUTTON_STICK_R -> 0.155f else -> 0.11f } @@ -984,8 +1096,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : // Initialize the InputOverlayDrawableButton. val defaultStateBitmap = getBitmap(context, defaultResId, scale) val pressedStateBitmap = getBitmap(context, pressedResId, scale) - val overlayDrawable = - InputOverlayDrawableButton(res, defaultStateBitmap, pressedStateBitmap, buttonId) + val overlayDrawable = InputOverlayDrawableButton( + res, + defaultStateBitmap, + pressedStateBitmap, + buttonId, + prefId + ) // Get the minimum and maximum coordinates of the screen where the button can be placed. val min = windowSize.first @@ -993,8 +1110,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. // These were set in the input overlay configuration menu. - val xKey = "$buttonId-X$orientation" - val yKey = "$buttonId-Y$orientation" + val xKey = "$prefId-X$layout" + val yKey = "$prefId-Y$layout" val drawableXPercent = sPrefs.getFloat(xKey, 0f) val drawableYPercent = sPrefs.getFloat(yKey, 0f) val drawableX = (drawableXPercent * max.x + min.x).toInt() @@ -1029,7 +1146,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : * @param defaultResId The [Bitmap] resource ID of the default state. * @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction. * @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions. - * @return the initialized [InputOverlayDrawableDpad] + * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE]. + * @return The initialized [InputOverlayDrawableDpad] */ private fun initializeOverlayDpad( context: Context, @@ -1037,7 +1155,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : defaultResId: Int, pressedOneDirectionResId: Int, pressedTwoDirectionsResId: Int, - orientation: String + layout: String ): InputOverlayDrawableDpad { // Resources handle for fetching the initial Drawable resource. val res = context.resources @@ -1074,8 +1192,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. // These were set in the input overlay configuration menu. - val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-X$orientation", 0f) - val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-Y$orientation", 0f) + val drawableXPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-X$layout", 0f) + val drawableYPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-Y$layout", 0f) val drawableX = (drawableXPercent * max.x + min.x).toInt() val drawableY = (drawableYPercent * max.y + min.y).toInt() val width = overlayDrawable.width @@ -1107,7 +1225,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : * @param pressedResInner Resource ID for the pressed inner image of the joystick. * @param joystick Identifier for which joystick this is. * @param button Identifier for which joystick button this is. - * @return the initialized [InputOverlayDrawableJoystick]. + * @param prefId Identifier for determining where a button appears on screen. + * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE]. + * @return The initialized [InputOverlayDrawableJoystick]. */ private fun initializeOverlayJoystick( context: Context, @@ -1117,7 +1237,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : pressedResInner: Int, joystick: Int, button: Int, - orientation: String + prefId: String, + layout: String ): InputOverlayDrawableJoystick { // Resources handle for fetching the initial Drawable resource. val res = context.resources @@ -1141,8 +1262,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. // These were set in the input overlay configuration menu. - val drawableXPercent = sPrefs.getFloat("$button-X$orientation", 0f) - val drawableYPercent = sPrefs.getFloat("$button-Y$orientation", 0f) + val drawableXPercent = sPrefs.getFloat("$prefId-X$layout", 0f) + val drawableYPercent = sPrefs.getFloat("$prefId-Y$layout", 0f) val drawableX = (drawableXPercent * max.x + min.x).toInt() val drawableY = (drawableYPercent * max.y + min.y).toInt() val outerScale = 1.66f @@ -1168,7 +1289,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : outerRect, innerRect, joystick, - button + button, + prefId ) // Need to set the image's position diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt index 4a93e0b14..2c28dda88 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt @@ -24,7 +24,8 @@ class InputOverlayDrawableButton( res: Resources, defaultStateBitmap: Bitmap, pressedStateBitmap: Bitmap, - val buttonId: Int + val buttonId: Int, + val prefId: String ) { // The ID value what motion event is tracking var trackId: Int diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt index fb48f584d..518b1e783 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt @@ -37,7 +37,8 @@ class InputOverlayDrawableJoystick( rectOuter: Rect, rectInner: Rect, val joystickId: Int, - val buttonId: Int + val buttonId: Int, + val prefId: String ) { // The ID value what motion event is tracking var trackId = -1 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/drawable/button_l3.xml b/src/android/app/src/main/res/drawable/button_l3.xml new file mode 100644 index 000000000..0cb28836e --- /dev/null +++ b/src/android/app/src/main/res/drawable/button_l3.xml @@ -0,0 +1,128 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="34.963dp" + android:height="37.265dp" + android:viewportWidth="34.963" + android:viewportHeight="37.265"> + <path + android:fillAlpha="0.5" + android:pathData="M19.451,19.024A3.498,3.498 0,0 0,21.165 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L20.327,16.481L20.327,15.7L20.901,15.7c0.757,0 1.714,-0.392 1.714,-1.302C22.621,13.785 22.224,13.229 21.271,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C23.967,19.27 23.017,20.346 21.165,20.346a3.929,3.929 135,0 1,-1.998 -0.529z" + android:strokeAlpha="0.6"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="21.568" + android:endY="33.938" + android:startX="21.568" + android:startY="16.14" + android:type="linear"> + <item + android:color="#FFC3C4C5" + android:offset="0" /> + <item + android:color="#FFC5C6C6" + android:offset="0.03" /> + <item + android:color="#FFC7C7C7" + android:offset="0.19" /> + <item + android:color="#DBB5B5B5" + android:offset="0.44" /> + <item + android:color="#7F878787" + android:offset="1" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillAlpha="0.5" + android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z" + android:strokeAlpha="0.6"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="17.395" + android:endY="18.74" + android:startX="17.395" + android:startY="-1.296" + android:type="linear"> + <item + android:color="#FFC3C4C5" + android:offset="0" /> + <item + android:color="#FFC5C6C6" + android:offset="0.03" /> + <item + android:color="#FFC7C7C7" + android:offset="0.19" /> + <item + android:color="#DBB5B5B5" + android:offset="0.44" /> + <item + android:color="#7F878787" + android:offset="1" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillAlpha="0.5" + android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z" + android:strokeAlpha="0.6"> + <aapt:attr name="android:fillColor"> + <gradient + android:centerX="17.477" + android:centerY="19.92" + android:gradientRadius="17.201" + android:type="radial"> + <item + android:color="#FFC3C4C5" + android:offset="0.58" /> + <item + android:color="#FFC6C6C6" + android:offset="0.84" /> + <item + android:color="#FFC7C7C7" + android:offset="0.88" /> + <item + android:color="#FFC2C2C2" + android:offset="0.91" /> + <item + android:color="#FFB5B5B5" + android:offset="0.94" /> + <item + android:color="#FF9E9E9E" + android:offset="0.98" /> + <item + android:color="#FF8F8F8F" + android:offset="1" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillAlpha="0.5" + android:pathData="m12.516,12.729l2,0l0,13.822l6.615,0l0,1.68L12.516,28.231Z" + android:strokeAlpha="0.6"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="16.829" + android:endY="46.882" + android:startX="16.829" + android:startY="20.479" + android:type="linear"> + <item + android:color="#FFC3C4C5" + android:offset="0" /> + <item + android:color="#FFC5C6C6" + android:offset="0.03" /> + <item + android:color="#FFC7C7C7" + android:offset="0.19" /> + <item + android:color="#DBB5B5B5" + android:offset="0.44" /> + <item + android:color="#7F878787" + android:offset="1" /> + </gradient> + </aapt:attr> + </path> +</vector> diff --git a/src/android/app/src/main/res/drawable/button_l3_depressed.xml b/src/android/app/src/main/res/drawable/button_l3_depressed.xml new file mode 100644 index 000000000..b078dedc9 --- /dev/null +++ b/src/android/app/src/main/res/drawable/button_l3_depressed.xml @@ -0,0 +1,75 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="34.963dp" + android:height="37.265dp" + android:viewportWidth="34.963" + android:viewportHeight="37.265"> + <path + android:fillAlpha="0.3" + android:fillColor="#151515" + android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z" + android:strokeAlpha="0.3" /> + <path + android:fillAlpha="0.6" + android:fillColor="#151515" + android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z" + android:strokeAlpha="0.6" /> + <path + android:fillAlpha="0.6" + android:pathData="M19.451,19.024A3.498,3.498 0,0 0,21.165 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L20.327,16.481L20.327,15.7L20.901,15.7c0.757,0 1.714,-0.392 1.714,-1.302C22.621,13.785 22.224,13.229 21.271,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C23.967,19.27 23.017,20.346 21.165,20.346a3.929,3.929 135,0 1,-1.998 -0.529z" + android:strokeAlpha="0.6"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="21.568" + android:endY="33.938" + android:startX="21.568" + android:startY="16.14" + android:type="linear"> + <item + android:color="#FFC3C4C5" + android:offset="0" /> + <item + android:color="#FFC5C6C6" + android:offset="0.03" /> + <item + android:color="#FFC7C7C7" + android:offset="0.19" /> + <item + android:color="#DBB5B5B5" + android:offset="0.44" /> + <item + android:color="#7F878787" + android:offset="1" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillAlpha="0.6" + android:pathData="m12.516,12.729l2,0l0,13.822l6.615,0l0,1.68L12.516,28.231Z" + android:strokeAlpha="0.6"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="16.829" + android:endY="46.882" + android:startX="16.829" + android:startY="20.479" + android:type="linear"> + <item + android:color="#FFC3C4C5" + android:offset="0" /> + <item + android:color="#FFC5C6C6" + android:offset="0.03" /> + <item + android:color="#FFC7C7C7" + android:offset="0.19" /> + <item + android:color="#DBB5B5B5" + android:offset="0.44" /> + <item + android:color="#7F878787" + android:offset="1" /> + </gradient> + </aapt:attr> + </path> +</vector> diff --git a/src/android/app/src/main/res/drawable/button_r3.xml b/src/android/app/src/main/res/drawable/button_r3.xml new file mode 100644 index 000000000..5c6864e26 --- /dev/null +++ b/src/android/app/src/main/res/drawable/button_r3.xml @@ -0,0 +1,128 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="34.963dp" + android:height="37.265dp" + android:viewportWidth="34.963" + android:viewportHeight="37.265"> + <path + android:fillAlpha="0.5" + android:pathData="m10.781,12.65a19.579,19.579 0,0 1,3.596 -0.302c2.003,0 3.294,0.368 4.199,1.185a3.622,3.622 0,0 1,1.14 2.757c0,1.916 -1.206,3.175 -2.733,3.704l0,0.063c1.119,0.386 1.786,1.421 2.117,2.929 0.474,2.024 0.818,3.424 1.119,3.982l-1.924,0c-0.238,-0.407 -0.561,-1.656 -0.968,-3.466 -0.431,-2.003 -1.206,-2.757 -2.91,-2.82l-1.762,0l0,6.286l-1.873,0zM12.654,19.264l1.916,0c2.003,0 3.273,-1.098 3.273,-2.757 0,-1.873 -1.357,-2.691 -3.336,-2.712a7.649,7.649 0,0 0,-1.852 0.172z" + android:strokeAlpha="0.6"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="15.506" + android:endY="48.977" + android:startX="15.506" + android:startY="19.659" + android:type="linear"> + <item + android:color="#FFC3C4C5" + android:offset="0" /> + <item + android:color="#FFC5C6C6" + android:offset="0.03" /> + <item + android:color="#FFC7C7C7" + android:offset="0.19" /> + <item + android:color="#DBB5B5B5" + android:offset="0.44" /> + <item + android:color="#7F878787" + android:offset="1" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillAlpha="0.5" + android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z" + android:strokeAlpha="0.6"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="17.395" + android:endY="18.74" + android:startX="17.395" + android:startY="-1.296" + android:type="linear"> + <item + android:color="#FFC3C4C5" + android:offset="0" /> + <item + android:color="#FFC5C6C6" + android:offset="0.03" /> + <item + android:color="#FFC7C7C7" + android:offset="0.19" /> + <item + android:color="#DBB5B5B5" + android:offset="0.44" /> + <item + android:color="#7F878787" + android:offset="1" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillAlpha="0.5" + android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z" + android:strokeAlpha="0.6"> + <aapt:attr name="android:fillColor"> + <gradient + android:centerX="17.477" + android:centerY="19.92" + android:gradientRadius="17.201" + android:type="radial"> + <item + android:color="#FFC3C4C5" + android:offset="0.58" /> + <item + android:color="#FFC6C6C6" + android:offset="0.84" /> + <item + android:color="#FFC7C7C7" + android:offset="0.88" /> + <item + android:color="#FFC2C2C2" + android:offset="0.91" /> + <item + android:color="#FFB5B5B5" + android:offset="0.94" /> + <item + android:color="#FF9E9E9E" + android:offset="0.98" /> + <item + android:color="#FF8F8F8F" + android:offset="1" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillAlpha="0.5" + android:pathData="M21.832,19.024A3.498,3.498 0,0 0,23.547 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L22.708,16.481L22.708,15.7L23.282,15.7c0.757,0 1.714,-0.392 1.714,-1.302C25.002,13.785 24.605,13.229 23.652,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C26.349,19.27 25.399,20.346 23.547,20.346a3.929,3.929 135,0 1,-1.998 -0.529z" + android:strokeAlpha="0.6"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="23.949" + android:endY="33.938" + android:startX="23.949" + android:startY="16.14" + android:type="linear"> + <item + android:color="#FFC3C4C5" + android:offset="0" /> + <item + android:color="#FFC5C6C6" + android:offset="0.03" /> + <item + android:color="#FFC7C7C7" + android:offset="0.19" /> + <item + android:color="#DBB5B5B5" + android:offset="0.44" /> + <item + android:color="#7F878787" + android:offset="1" /> + </gradient> + </aapt:attr> + </path> +</vector> diff --git a/src/android/app/src/main/res/drawable/button_r3_depressed.xml b/src/android/app/src/main/res/drawable/button_r3_depressed.xml new file mode 100644 index 000000000..20f480179 --- /dev/null +++ b/src/android/app/src/main/res/drawable/button_r3_depressed.xml @@ -0,0 +1,75 @@ +<vector xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:aapt="http://schemas.android.com/aapt" + android:width="34.963dp" + android:height="37.265dp" + android:viewportWidth="34.963" + android:viewportHeight="37.265"> + <path + android:fillAlpha="0.3" + android:fillColor="#151515" + android:pathData="M16.062,9.353 L9.624,3.405A1.963,1.963 0,0 1,10.955 0l12.88,0a1.963,1.963 135,0 1,1.323 3.405L18.726,9.353a1.961,1.961 135,0 1,-2.664 0z" + android:strokeAlpha="0.3" /> + <path + android:fillAlpha="0.6" + android:fillColor="#151515" + android:pathData="m25.79,5.657l0,0a2.09,2.09 45,0 0,0.23 3.262c3.522,2.402 4.762,5.927 4.741,10.52A13.279,13.279 135,0 1,4.206 19.365c0,-4.516 0.931,-7.71 4.374,-10.107a2.098,2.098 0,0 0,0.233 -3.265l0,0a2.101,2.101 135,0 0,-2.646 -0.169C1.433,9.133 -0.266,13.941 0.033,20.233a17.468,17.468 0,0 0,34.925 -0.868c0,-6.006 -1.971,-10.771 -6.585,-13.917a2.088,2.088 45,0 0,-2.582 0.209z" + android:strokeAlpha="0.6" /> + <path + android:fillAlpha="0.6" + android:pathData="m10.781,12.65a19.579,19.579 0,0 1,3.596 -0.302c2.003,0 3.294,0.368 4.199,1.185a3.622,3.622 0,0 1,1.14 2.757c0,1.916 -1.206,3.175 -2.733,3.704l0,0.063c1.119,0.386 1.786,1.421 2.117,2.929 0.474,2.024 0.818,3.424 1.119,3.982l-1.924,0c-0.238,-0.407 -0.561,-1.656 -0.968,-3.466 -0.431,-2.003 -1.206,-2.757 -2.91,-2.82l-1.762,0l0,6.286l-1.873,0zM12.654,19.264l1.916,0c2.003,0 3.273,-1.098 3.273,-2.757 0,-1.873 -1.357,-2.691 -3.336,-2.712a7.649,7.649 0,0 0,-1.852 0.172z" + android:strokeAlpha="0.6"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="15.506" + android:endY="48.977" + android:startX="15.506" + android:startY="19.659" + android:type="linear"> + <item + android:color="#FFC3C4C5" + android:offset="0" /> + <item + android:color="#FFC5C6C6" + android:offset="0.03" /> + <item + android:color="#FFC7C7C7" + android:offset="0.19" /> + <item + android:color="#DBB5B5B5" + android:offset="0.44" /> + <item + android:color="#7F878787" + android:offset="1" /> + </gradient> + </aapt:attr> + </path> + <path + android:fillAlpha="0.6" + android:pathData="M21.832,19.024A3.498,3.498 0,0 0,23.547 19.508c1.336,0 1.749,-0.852 1.738,-1.49 0,-1.077 -0.982,-1.537 -1.987,-1.537L22.708,16.481L22.708,15.7L23.282,15.7c0.757,0 1.714,-0.392 1.714,-1.302C25.002,13.785 24.605,13.229 23.652,13.229a2.834,2.834 0,0 0,-1.537 0.529l-0.265,-0.757a3.662,3.662 0,0 1,2.008 -0.59c1.513,0 2.201,0.897 2.201,1.834 0,0.794 -0.474,1.466 -1.421,1.807l0,0.024c0.947,0.19 1.714,0.9 1.714,1.976C26.349,19.27 25.399,20.346 23.547,20.346a3.929,3.929 135,0 1,-1.998 -0.529z" + android:strokeAlpha="0.6"> + <aapt:attr name="android:fillColor"> + <gradient + android:endX="23.949" + android:endY="33.938" + android:startX="23.949" + android:startY="16.14" + android:type="linear"> + <item + android:color="#FFC3C4C5" + android:offset="0" /> + <item + android:color="#FFC5C6C6" + android:offset="0.03" /> + <item + android:color="#FFC7C7C7" + android:offset="0.19" /> + <item + android:color="#DBB5B5B5" + android:offset="0.44" /> + <item + android:color="#7F878787" + android:offset="1" /> + </gradient> + </aapt:attr> + </path> +</vector> diff --git a/src/android/app/src/main/res/values/arrays.xml b/src/android/app/src/main/res/values/arrays.xml index 6d092f7a9..200b99185 100644 --- a/src/android/app/src/main/res/values/arrays.xml +++ b/src/android/app/src/main/res/values/arrays.xml @@ -205,6 +205,8 @@ <item>@string/gamepad_d_pad</item> <item>@string/gamepad_left_stick</item> <item>@string/gamepad_right_stick</item> + <item>L3</item> + <item>R3</item> <item>@string/gamepad_home</item> <item>@string/gamepad_screenshot</item> </string-array> diff --git a/src/android/app/src/main/res/values/integers.xml b/src/android/app/src/main/res/values/integers.xml index 2e93b408c..5e39bc7d9 100644 --- a/src/android/app/src/main/res/values/integers.xml +++ b/src/android/app/src/main/res/values/integers.xml @@ -33,6 +33,10 @@ <integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer> <integer name="SWITCH_BUTTON_DPAD_X">260</integer> <integer name="SWITCH_BUTTON_DPAD_Y">790</integer> + <integer name="SWITCH_BUTTON_STICK_L_X">870</integer> + <integer name="SWITCH_BUTTON_STICK_L_Y">400</integer> + <integer name="SWITCH_BUTTON_STICK_R_X">960</integer> + <integer name="SWITCH_BUTTON_STICK_R_Y">430</integer> <!-- Default SWITCH portrait layout --> <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer> @@ -65,6 +69,10 @@ <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer> <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer> <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer> + <integer name="SWITCH_BUTTON_STICK_L_X_PORTRAIT">730</integer> + <integer name="SWITCH_BUTTON_STICK_L_Y_PORTRAIT">510</integer> + <integer name="SWITCH_BUTTON_STICK_R_X_PORTRAIT">900</integer> + <integer name="SWITCH_BUTTON_STICK_R_Y_PORTRAIT">540</integer> <!-- Default SWITCH foldable layout --> <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer> @@ -97,5 +105,9 @@ <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer> <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer> <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer> + <integer name="SWITCH_BUTTON_STICK_L_X_FOLDABLE">550</integer> + <integer name="SWITCH_BUTTON_STICK_L_Y_FOLDABLE">210</integer> + <integer name="SWITCH_BUTTON_STICK_R_X_FOLDABLE">550</integer> + <integer name="SWITCH_BUTTON_STICK_R_Y_FOLDABLE">280</integer> </resources> 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 @@ <string name="fatal_error_message">A fatal error occurred. Check the log for details.\nContinuing emulation may result in crashes and bugs.</string> <string name="performance_warning">Turning off this setting will significantly reduce emulation performance! For the best experience, it is recommended that you leave this setting enabled.</string> <string name="device_memory_inadequate">Device RAM: %1$s\nRecommended: %2$s</string> + <string name="memory_formatted">%1$s %2$s</string> <!-- Region Names --> <string name="region_japan">Japan</string> diff --git a/src/android/gradle.properties b/src/android/gradle.properties index e2f278f33..4fca1b576 100644 --- a/src/android/gradle.properties +++ b/src/android/gradle.properties @@ -15,3 +15,6 @@ android.useAndroidX=true kotlin.code.style=official kotlin.parallel.tasks.in.project=true android.defaults.buildfeatures.buildconfig=true + +# Android Gradle plugin 8.0.2 +android.suppressUnsupportedCompileSdk=34 |