summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Charles Lombardo2023-06-27 02:53:14 -0400
committerGravatar Charles Lombardo2023-07-02 20:19:01 -0400
commit68f6f2671bec3d69019c119694396fc740e6ddac (patch)
tree7fd8d44806cb42b000dd52a64fa1db8947f251ce
parentMerge pull request #10998 from Morph1984/qt-stop-messing-with-me (diff)
downloadyuzu-68f6f2671bec3d69019c119694396fc740e6ddac.tar.gz
yuzu-68f6f2671bec3d69019c119694396fc740e6ddac.tar.xz
yuzu-68f6f2671bec3d69019c119694396fc740e6ddac.zip
android: Version the input overlay
Now within the Input Overlay file, there is a version that will determine when the overlay will be reset. This is intended for breaking changes like the ones we had with the additions of percentage based layouts or the addition of foldable/portrait layouts. This also includes versions for each individual layout so we don't have to reset every layout if only one is broken. Additionally, this includes new L3/R3 buttons.
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/model/Settings.kt67
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/fragments/EmulationFragment.kt18
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt410
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableButton.kt3
-rw-r--r--src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlayDrawableJoystick.kt3
-rw-r--r--src/android/app/src/main/res/drawable/button_l3.xml128
-rw-r--r--src/android/app/src/main/res/drawable/button_l3_depressed.xml75
-rw-r--r--src/android/app/src/main/res/drawable/button_r3.xml128
-rw-r--r--src/android/app/src/main/res/drawable/button_r3_depressed.xml75
-rw-r--r--src/android/app/src/main/res/values/arrays.xml2
-rw-r--r--src/android/app/src/main/res/values/integers.xml12
11 files changed, 751 insertions, 170 deletions
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 be6e17e65..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
@@ -112,25 +112,36 @@ class Settings {
112 112
113 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" 113 const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
114 114
115 const val PREF_OVERLAY_INIT = "OverlayInit" 115 const val PREF_OVERLAY_VERSION = "OverlayVersion"
116 const val PREF_LANDSCAPE_OVERLAY_VERSION = "LandscapeOverlayVersion"
117 const val PREF_PORTRAIT_OVERLAY_VERSION = "PortraitOverlayVersion"
118 const val PREF_FOLDABLE_OVERLAY_VERSION = "FoldableOverlayVersion"
119 val overlayLayoutPrefs = listOf(
120 PREF_LANDSCAPE_OVERLAY_VERSION,
121 PREF_PORTRAIT_OVERLAY_VERSION,
122 PREF_FOLDABLE_OVERLAY_VERSION
123 )
124
116 const val PREF_CONTROL_SCALE = "controlScale" 125 const val PREF_CONTROL_SCALE = "controlScale"
117 const val PREF_CONTROL_OPACITY = "controlOpacity" 126 const val PREF_CONTROL_OPACITY = "controlOpacity"
118 const val PREF_TOUCH_ENABLED = "isTouchEnabled" 127 const val PREF_TOUCH_ENABLED = "isTouchEnabled"
119 const val PREF_BUTTON_TOGGLE_0 = "buttonToggle0" 128 const val PREF_BUTTON_A = "buttonToggle0"
120 const val PREF_BUTTON_TOGGLE_1 = "buttonToggle1" 129 const val PREF_BUTTON_B = "buttonToggle1"
121 const val PREF_BUTTON_TOGGLE_2 = "buttonToggle2" 130 const val PREF_BUTTON_X = "buttonToggle2"
122 const val PREF_BUTTON_TOGGLE_3 = "buttonToggle3" 131 const val PREF_BUTTON_Y = "buttonToggle3"
123 const val PREF_BUTTON_TOGGLE_4 = "buttonToggle4" 132 const val PREF_BUTTON_L = "buttonToggle4"
124 const val PREF_BUTTON_TOGGLE_5 = "buttonToggle5" 133 const val PREF_BUTTON_R = "buttonToggle5"
125 const val PREF_BUTTON_TOGGLE_6 = "buttonToggle6" 134 const val PREF_BUTTON_ZL = "buttonToggle6"
126 const val PREF_BUTTON_TOGGLE_7 = "buttonToggle7" 135 const val PREF_BUTTON_ZR = "buttonToggle7"
127 const val PREF_BUTTON_TOGGLE_8 = "buttonToggle8" 136 const val PREF_BUTTON_PLUS = "buttonToggle8"
128 const val PREF_BUTTON_TOGGLE_9 = "buttonToggle9" 137 const val PREF_BUTTON_MINUS = "buttonToggle9"
129 const val PREF_BUTTON_TOGGLE_10 = "buttonToggle10" 138 const val PREF_BUTTON_DPAD = "buttonToggle10"
130 const val PREF_BUTTON_TOGGLE_11 = "buttonToggle11" 139 const val PREF_STICK_L = "buttonToggle11"
131 const val PREF_BUTTON_TOGGLE_12 = "buttonToggle12" 140 const val PREF_STICK_R = "buttonToggle12"
132 const val PREF_BUTTON_TOGGLE_13 = "buttonToggle13" 141 const val PREF_BUTTON_STICK_L = "buttonToggle13"
133 const val PREF_BUTTON_TOGGLE_14 = "buttonToggle14" 142 const val PREF_BUTTON_STICK_R = "buttonToggle14"
143 const val PREF_BUTTON_HOME = "buttonToggle15"
144 const val PREF_BUTTON_SCREENSHOT = "buttonToggle16"
134 145
135 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter" 146 const val PREF_MENU_SETTINGS_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
136 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" 147 const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
@@ -145,6 +156,30 @@ class Settings {
145 156
146 private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap() 157 private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap()
147 158
159 val overlayPreferences = listOf(
160 PREF_OVERLAY_VERSION,
161 PREF_CONTROL_SCALE,
162 PREF_CONTROL_OPACITY,
163 PREF_TOUCH_ENABLED,
164 PREF_BUTTON_A,
165 PREF_BUTTON_B,
166 PREF_BUTTON_X,
167 PREF_BUTTON_Y,
168 PREF_BUTTON_L,
169 PREF_BUTTON_R,
170 PREF_BUTTON_ZL,
171 PREF_BUTTON_ZR,
172 PREF_BUTTON_PLUS,
173 PREF_BUTTON_MINUS,
174 PREF_BUTTON_DPAD,
175 PREF_STICK_L,
176 PREF_STICK_R,
177 PREF_BUTTON_HOME,
178 PREF_BUTTON_SCREENSHOT,
179 PREF_BUTTON_STICK_L,
180 PREF_BUTTON_STICK_R
181 )
182
148 const val LayoutOption_Unspecified = 0 183 const val LayoutOption_Unspecified = 0
149 const val LayoutOption_MobilePortrait = 4 184 const val LayoutOption_MobilePortrait = 4
150 const val LayoutOption_MobileLandscape = 5 185 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 {
212 } 212 }
213 if (!isInFoldableLayout) { 213 if (!isInFoldableLayout) {
214 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { 214 if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
215 binding.surfaceInputOverlay.orientation = InputOverlay.PORTRAIT 215 binding.surfaceInputOverlay.layout = InputOverlay.PORTRAIT
216 } else { 216 } else {
217 binding.surfaceInputOverlay.orientation = InputOverlay.LANDSCAPE 217 binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE
218 } 218 }
219 } 219 }
220 if (!binding.surfaceInputOverlay.isInEditMode) { 220 if (!binding.surfaceInputOverlay.isInEditMode) {
@@ -260,7 +260,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
260 .remove(Settings.PREF_CONTROL_SCALE) 260 .remove(Settings.PREF_CONTROL_SCALE)
261 .remove(Settings.PREF_CONTROL_OPACITY) 261 .remove(Settings.PREF_CONTROL_OPACITY)
262 .apply() 262 .apply()
263 binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.resetButtonPlacement() } 263 binding.surfaceInputOverlay.post {
264 binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement()
265 }
264 } 266 }
265 267
266 private fun updateShowFpsOverlay() { 268 private fun updateShowFpsOverlay() {
@@ -337,7 +339,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
337 binding.inGameMenu.layoutParams.height = it.bounds.bottom 339 binding.inGameMenu.layoutParams.height = it.bounds.bottom
338 340
339 isInFoldableLayout = true 341 isInFoldableLayout = true
340 binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE 342 binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE
341 refreshInputOverlay() 343 refreshInputOverlay()
342 } 344 }
343 } 345 }
@@ -410,9 +412,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
410 R.id.menu_toggle_controls -> { 412 R.id.menu_toggle_controls -> {
411 val preferences = 413 val preferences =
412 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 414 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
413 val optionsArray = BooleanArray(15) 415 val optionsArray = BooleanArray(Settings.overlayPreferences.size)
414 for (i in 0..14) { 416 Settings.overlayPreferences.forEachIndexed { i, _ ->
415 optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 13) 417 optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 15)
416 } 418 }
417 419
418 val dialog = MaterialAlertDialogBuilder(requireContext()) 420 val dialog = MaterialAlertDialogBuilder(requireContext())
@@ -436,7 +438,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
436 dialog.getButton(AlertDialog.BUTTON_NEUTRAL) 438 dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
437 .setOnClickListener { 439 .setOnClickListener {
438 val isChecked = !optionsArray[0] 440 val isChecked = !optionsArray[0]
439 for (i in 0..14) { 441 Settings.overlayPreferences.forEachIndexed { i, _ ->
440 optionsArray[i] = isChecked 442 optionsArray[i] = isChecked
441 dialog.listView.setItemChecked(i, isChecked) 443 dialog.listView.setItemChecked(i, isChecked)
442 preferences.edit() 444 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?) :
51 51
52 private lateinit var windowInsets: WindowInsets 52 private lateinit var windowInsets: WindowInsets
53 53
54 var orientation = LANDSCAPE 54 var layout = LANDSCAPE
55 55
56 override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { 56 override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
57 super.onLayout(changed, left, top, right, bottom) 57 super.onLayout(changed, left, top, right, bottom)
58 58
59 windowInsets = rootWindowInsets 59 windowInsets = rootWindowInsets
60 60
61 if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) { 61 val overlayVersion = preferences.getInt(Settings.PREF_OVERLAY_VERSION, 0)
62 defaultOverlay() 62 if (overlayVersion != OVERLAY_VERSION) {
63 resetAllLayouts()
64 } else {
65 val layoutIndex = overlayLayouts.indexOf(layout)
66 val currentLayoutVersion =
67 preferences.getInt(Settings.overlayLayoutPrefs[layoutIndex], 0)
68 if (currentLayoutVersion != overlayLayoutVersions[layoutIndex]) {
69 resetCurrentLayout()
70 }
63 } 71 }
64 72
65 // Load the controls. 73 // Load the controls.
@@ -266,10 +274,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
266 MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) { 274 MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) {
267 // Persist button position by saving new place. 275 // Persist button position by saving new place.
268 saveControlPosition( 276 saveControlPosition(
269 buttonBeingConfigured!!.buttonId, 277 buttonBeingConfigured!!.prefId,
270 buttonBeingConfigured!!.bounds.centerX(), 278 buttonBeingConfigured!!.bounds.centerX(),
271 buttonBeingConfigured!!.bounds.centerY(), 279 buttonBeingConfigured!!.bounds.centerY(),
272 orientation 280 layout
273 ) 281 )
274 buttonBeingConfigured = null 282 buttonBeingConfigured = null
275 } 283 }
@@ -299,10 +307,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
299 MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) { 307 MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) {
300 // Persist button position by saving new place. 308 // Persist button position by saving new place.
301 saveControlPosition( 309 saveControlPosition(
302 dpadBeingConfigured!!.upId, 310 Settings.PREF_BUTTON_DPAD,
303 dpadBeingConfigured!!.bounds.centerX(), 311 dpadBeingConfigured!!.bounds.centerX(),
304 dpadBeingConfigured!!.bounds.centerY(), 312 dpadBeingConfigured!!.bounds.centerY(),
305 orientation 313 layout
306 ) 314 )
307 dpadBeingConfigured = null 315 dpadBeingConfigured = null
308 } 316 }
@@ -330,10 +338,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
330 MotionEvent.ACTION_UP, 338 MotionEvent.ACTION_UP,
331 MotionEvent.ACTION_POINTER_UP -> if (joystickBeingConfigured != null) { 339 MotionEvent.ACTION_POINTER_UP -> if (joystickBeingConfigured != null) {
332 saveControlPosition( 340 saveControlPosition(
333 joystickBeingConfigured!!.buttonId, 341 joystickBeingConfigured!!.prefId,
334 joystickBeingConfigured!!.bounds.centerX(), 342 joystickBeingConfigured!!.bounds.centerX(),
335 joystickBeingConfigured!!.bounds.centerY(), 343 joystickBeingConfigured!!.bounds.centerY(),
336 orientation 344 layout
337 ) 345 )
338 joystickBeingConfigured = null 346 joystickBeingConfigured = null
339 } 347 }
@@ -343,9 +351,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
343 return true 351 return true
344 } 352 }
345 353
346 private fun addOverlayControls(orientation: String) { 354 private fun addOverlayControls(layout: String) {
347 val windowSize = getSafeScreenSize(context) 355 val windowSize = getSafeScreenSize(context)
348 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_0, true)) { 356 if (preferences.getBoolean(Settings.PREF_BUTTON_A, true)) {
349 overlayButtons.add( 357 overlayButtons.add(
350 initializeOverlayButton( 358 initializeOverlayButton(
351 context, 359 context,
@@ -353,11 +361,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
353 R.drawable.facebutton_a, 361 R.drawable.facebutton_a,
354 R.drawable.facebutton_a_depressed, 362 R.drawable.facebutton_a_depressed,
355 ButtonType.BUTTON_A, 363 ButtonType.BUTTON_A,
356 orientation 364 Settings.PREF_BUTTON_A,
365 layout
357 ) 366 )
358 ) 367 )
359 } 368 }
360 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_1, true)) { 369 if (preferences.getBoolean(Settings.PREF_BUTTON_B, true)) {
361 overlayButtons.add( 370 overlayButtons.add(
362 initializeOverlayButton( 371 initializeOverlayButton(
363 context, 372 context,
@@ -365,11 +374,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
365 R.drawable.facebutton_b, 374 R.drawable.facebutton_b,
366 R.drawable.facebutton_b_depressed, 375 R.drawable.facebutton_b_depressed,
367 ButtonType.BUTTON_B, 376 ButtonType.BUTTON_B,
368 orientation 377 Settings.PREF_BUTTON_B,
378 layout
369 ) 379 )
370 ) 380 )
371 } 381 }
372 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_2, true)) { 382 if (preferences.getBoolean(Settings.PREF_BUTTON_X, true)) {
373 overlayButtons.add( 383 overlayButtons.add(
374 initializeOverlayButton( 384 initializeOverlayButton(
375 context, 385 context,
@@ -377,11 +387,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
377 R.drawable.facebutton_x, 387 R.drawable.facebutton_x,
378 R.drawable.facebutton_x_depressed, 388 R.drawable.facebutton_x_depressed,
379 ButtonType.BUTTON_X, 389 ButtonType.BUTTON_X,
380 orientation 390 Settings.PREF_BUTTON_X,
391 layout
381 ) 392 )
382 ) 393 )
383 } 394 }
384 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_3, true)) { 395 if (preferences.getBoolean(Settings.PREF_BUTTON_Y, true)) {
385 overlayButtons.add( 396 overlayButtons.add(
386 initializeOverlayButton( 397 initializeOverlayButton(
387 context, 398 context,
@@ -389,11 +400,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
389 R.drawable.facebutton_y, 400 R.drawable.facebutton_y,
390 R.drawable.facebutton_y_depressed, 401 R.drawable.facebutton_y_depressed,
391 ButtonType.BUTTON_Y, 402 ButtonType.BUTTON_Y,
392 orientation 403 Settings.PREF_BUTTON_Y,
404 layout
393 ) 405 )
394 ) 406 )
395 } 407 }
396 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_4, true)) { 408 if (preferences.getBoolean(Settings.PREF_BUTTON_L, true)) {
397 overlayButtons.add( 409 overlayButtons.add(
398 initializeOverlayButton( 410 initializeOverlayButton(
399 context, 411 context,
@@ -401,11 +413,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
401 R.drawable.l_shoulder, 413 R.drawable.l_shoulder,
402 R.drawable.l_shoulder_depressed, 414 R.drawable.l_shoulder_depressed,
403 ButtonType.TRIGGER_L, 415 ButtonType.TRIGGER_L,
404 orientation 416 Settings.PREF_BUTTON_L,
417 layout
405 ) 418 )
406 ) 419 )
407 } 420 }
408 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_5, true)) { 421 if (preferences.getBoolean(Settings.PREF_BUTTON_R, true)) {
409 overlayButtons.add( 422 overlayButtons.add(
410 initializeOverlayButton( 423 initializeOverlayButton(
411 context, 424 context,
@@ -413,11 +426,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
413 R.drawable.r_shoulder, 426 R.drawable.r_shoulder,
414 R.drawable.r_shoulder_depressed, 427 R.drawable.r_shoulder_depressed,
415 ButtonType.TRIGGER_R, 428 ButtonType.TRIGGER_R,
416 orientation 429 Settings.PREF_BUTTON_R,
430 layout
417 ) 431 )
418 ) 432 )
419 } 433 }
420 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_6, true)) { 434 if (preferences.getBoolean(Settings.PREF_BUTTON_ZL, true)) {
421 overlayButtons.add( 435 overlayButtons.add(
422 initializeOverlayButton( 436 initializeOverlayButton(
423 context, 437 context,
@@ -425,11 +439,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
425 R.drawable.zl_trigger, 439 R.drawable.zl_trigger,
426 R.drawable.zl_trigger_depressed, 440 R.drawable.zl_trigger_depressed,
427 ButtonType.TRIGGER_ZL, 441 ButtonType.TRIGGER_ZL,
428 orientation 442 Settings.PREF_BUTTON_ZL,
443 layout
429 ) 444 )
430 ) 445 )
431 } 446 }
432 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_7, true)) { 447 if (preferences.getBoolean(Settings.PREF_BUTTON_ZR, true)) {
433 overlayButtons.add( 448 overlayButtons.add(
434 initializeOverlayButton( 449 initializeOverlayButton(
435 context, 450 context,
@@ -437,11 +452,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
437 R.drawable.zr_trigger, 452 R.drawable.zr_trigger,
438 R.drawable.zr_trigger_depressed, 453 R.drawable.zr_trigger_depressed,
439 ButtonType.TRIGGER_ZR, 454 ButtonType.TRIGGER_ZR,
440 orientation 455 Settings.PREF_BUTTON_ZR,
456 layout
441 ) 457 )
442 ) 458 )
443 } 459 }
444 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_8, true)) { 460 if (preferences.getBoolean(Settings.PREF_BUTTON_PLUS, true)) {
445 overlayButtons.add( 461 overlayButtons.add(
446 initializeOverlayButton( 462 initializeOverlayButton(
447 context, 463 context,
@@ -449,11 +465,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
449 R.drawable.facebutton_plus, 465 R.drawable.facebutton_plus,
450 R.drawable.facebutton_plus_depressed, 466 R.drawable.facebutton_plus_depressed,
451 ButtonType.BUTTON_PLUS, 467 ButtonType.BUTTON_PLUS,
452 orientation 468 Settings.PREF_BUTTON_PLUS,
469 layout
453 ) 470 )
454 ) 471 )
455 } 472 }
456 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_9, true)) { 473 if (preferences.getBoolean(Settings.PREF_BUTTON_MINUS, true)) {
457 overlayButtons.add( 474 overlayButtons.add(
458 initializeOverlayButton( 475 initializeOverlayButton(
459 context, 476 context,
@@ -461,11 +478,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
461 R.drawable.facebutton_minus, 478 R.drawable.facebutton_minus,
462 R.drawable.facebutton_minus_depressed, 479 R.drawable.facebutton_minus_depressed,
463 ButtonType.BUTTON_MINUS, 480 ButtonType.BUTTON_MINUS,
464 orientation 481 Settings.PREF_BUTTON_MINUS,
482 layout
465 ) 483 )
466 ) 484 )
467 } 485 }
468 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_10, true)) { 486 if (preferences.getBoolean(Settings.PREF_BUTTON_DPAD, true)) {
469 overlayDpads.add( 487 overlayDpads.add(
470 initializeOverlayDpad( 488 initializeOverlayDpad(
471 context, 489 context,
@@ -473,11 +491,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
473 R.drawable.dpad_standard, 491 R.drawable.dpad_standard,
474 R.drawable.dpad_standard_cardinal_depressed, 492 R.drawable.dpad_standard_cardinal_depressed,
475 R.drawable.dpad_standard_diagonal_depressed, 493 R.drawable.dpad_standard_diagonal_depressed,
476 orientation 494 layout
477 ) 495 )
478 ) 496 )
479 } 497 }
480 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_11, true)) { 498 if (preferences.getBoolean(Settings.PREF_STICK_L, true)) {
481 overlayJoysticks.add( 499 overlayJoysticks.add(
482 initializeOverlayJoystick( 500 initializeOverlayJoystick(
483 context, 501 context,
@@ -487,11 +505,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
487 R.drawable.joystick_depressed, 505 R.drawable.joystick_depressed,
488 StickType.STICK_L, 506 StickType.STICK_L,
489 ButtonType.STICK_L, 507 ButtonType.STICK_L,
490 orientation 508 Settings.PREF_STICK_L,
509 layout
491 ) 510 )
492 ) 511 )
493 } 512 }
494 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_12, true)) { 513 if (preferences.getBoolean(Settings.PREF_STICK_R, true)) {
495 overlayJoysticks.add( 514 overlayJoysticks.add(
496 initializeOverlayJoystick( 515 initializeOverlayJoystick(
497 context, 516 context,
@@ -501,11 +520,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
501 R.drawable.joystick_depressed, 520 R.drawable.joystick_depressed,
502 StickType.STICK_R, 521 StickType.STICK_R,
503 ButtonType.STICK_R, 522 ButtonType.STICK_R,
504 orientation 523 Settings.PREF_STICK_R,
524 layout
505 ) 525 )
506 ) 526 )
507 } 527 }
508 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_13, false)) { 528 if (preferences.getBoolean(Settings.PREF_BUTTON_HOME, false)) {
509 overlayButtons.add( 529 overlayButtons.add(
510 initializeOverlayButton( 530 initializeOverlayButton(
511 context, 531 context,
@@ -513,11 +533,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
513 R.drawable.facebutton_home, 533 R.drawable.facebutton_home,
514 R.drawable.facebutton_home_depressed, 534 R.drawable.facebutton_home_depressed,
515 ButtonType.BUTTON_HOME, 535 ButtonType.BUTTON_HOME,
516 orientation 536 Settings.PREF_BUTTON_HOME,
537 layout
517 ) 538 )
518 ) 539 )
519 } 540 }
520 if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_14, false)) { 541 if (preferences.getBoolean(Settings.PREF_BUTTON_SCREENSHOT, false)) {
521 overlayButtons.add( 542 overlayButtons.add(
522 initializeOverlayButton( 543 initializeOverlayButton(
523 context, 544 context,
@@ -525,7 +546,34 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
525 R.drawable.facebutton_screenshot, 546 R.drawable.facebutton_screenshot,
526 R.drawable.facebutton_screenshot_depressed, 547 R.drawable.facebutton_screenshot_depressed,
527 ButtonType.BUTTON_CAPTURE, 548 ButtonType.BUTTON_CAPTURE,
528 orientation 549 Settings.PREF_BUTTON_SCREENSHOT,
550 layout
551 )
552 )
553 }
554 if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_L, true)) {
555 overlayButtons.add(
556 initializeOverlayButton(
557 context,
558 windowSize,
559 R.drawable.button_l3,
560 R.drawable.button_l3_depressed,
561 ButtonType.STICK_L,
562 Settings.PREF_BUTTON_STICK_L,
563 layout
564 )
565 )
566 }
567 if (preferences.getBoolean(Settings.PREF_BUTTON_STICK_R, true)) {
568 overlayButtons.add(
569 initializeOverlayButton(
570 context,
571 windowSize,
572 R.drawable.button_r3,
573 R.drawable.button_r3_depressed,
574 ButtonType.STICK_R,
575 Settings.PREF_BUTTON_STICK_R,
576 layout
529 ) 577 )
530 ) 578 )
531 } 579 }
@@ -539,18 +587,18 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
539 587
540 // Add all the enabled overlay items back to the HashSet. 588 // Add all the enabled overlay items back to the HashSet.
541 if (EmulationMenuSettings.showOverlay) { 589 if (EmulationMenuSettings.showOverlay) {
542 addOverlayControls(orientation) 590 addOverlayControls(layout)
543 } 591 }
544 invalidate() 592 invalidate()
545 } 593 }
546 594
547 private fun saveControlPosition(sharedPrefsId: Int, x: Int, y: Int, orientation: String) { 595 private fun saveControlPosition(prefId: String, x: Int, y: Int, layout: String) {
548 val windowSize = getSafeScreenSize(context) 596 val windowSize = getSafeScreenSize(context)
549 val min = windowSize.first 597 val min = windowSize.first
550 val max = windowSize.second 598 val max = windowSize.second
551 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() 599 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
552 .putFloat("$sharedPrefsId-X$orientation", (x - min.x).toFloat() / max.x) 600 .putFloat("$prefId-X$layout", (x - min.x).toFloat() / max.x)
553 .putFloat("$sharedPrefsId-Y$orientation", (y - min.y).toFloat() / max.y) 601 .putFloat("$prefId-Y$layout", (y - min.y).toFloat() / max.y)
554 .apply() 602 .apply()
555 } 603 }
556 604
@@ -558,19 +606,31 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
558 inEditMode = editMode 606 inEditMode = editMode
559 } 607 }
560 608
561 private fun defaultOverlay() { 609 private fun resetCurrentLayout() {
562 if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) { 610 defaultOverlayByLayout(layout)
563 defaultOverlayByLayout(orientation) 611 val layoutIndex = overlayLayouts.indexOf(layout)
564 }
565
566 resetButtonPlacement()
567 preferences.edit() 612 preferences.edit()
568 .putBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", true) 613 .putInt(Settings.overlayLayoutPrefs[layoutIndex], overlayLayoutVersions[layoutIndex])
569 .apply() 614 .apply()
570 } 615 }
571 616
572 fun resetButtonPlacement() { 617 private fun resetAllLayouts() {
573 defaultOverlayByLayout(orientation) 618 val editor = preferences.edit()
619 overlayLayouts.forEachIndexed { i, layout ->
620 defaultOverlayByLayout(layout)
621 editor.putInt(Settings.overlayLayoutPrefs[i], overlayLayoutVersions[i])
622 }
623 editor.putInt(Settings.PREF_OVERLAY_VERSION, OVERLAY_VERSION)
624 editor.apply()
625 }
626
627 fun resetLayoutVisibilityAndPlacement() {
628 defaultOverlayByLayout(layout)
629 val editor = preferences.edit()
630 Settings.overlayPreferences.forEachIndexed { _, pref ->
631 editor.remove(pref)
632 }
633 editor.apply()
574 refreshControls() 634 refreshControls()
575 } 635 }
576 636
@@ -604,7 +664,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
604 R.integer.SWITCH_STICK_R_X, 664 R.integer.SWITCH_STICK_R_X,
605 R.integer.SWITCH_STICK_R_Y, 665 R.integer.SWITCH_STICK_R_Y,
606 R.integer.SWITCH_STICK_L_X, 666 R.integer.SWITCH_STICK_L_X,
607 R.integer.SWITCH_STICK_L_Y 667 R.integer.SWITCH_STICK_L_Y,
668 R.integer.SWITCH_BUTTON_STICK_L_X,
669 R.integer.SWITCH_BUTTON_STICK_L_Y,
670 R.integer.SWITCH_BUTTON_STICK_R_X,
671 R.integer.SWITCH_BUTTON_STICK_R_Y
608 ) 672 )
609 673
610 private val portraitResources = arrayOf( 674 private val portraitResources = arrayOf(
@@ -637,7 +701,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
637 R.integer.SWITCH_STICK_R_X_PORTRAIT, 701 R.integer.SWITCH_STICK_R_X_PORTRAIT,
638 R.integer.SWITCH_STICK_R_Y_PORTRAIT, 702 R.integer.SWITCH_STICK_R_Y_PORTRAIT,
639 R.integer.SWITCH_STICK_L_X_PORTRAIT, 703 R.integer.SWITCH_STICK_L_X_PORTRAIT,
640 R.integer.SWITCH_STICK_L_Y_PORTRAIT 704 R.integer.SWITCH_STICK_L_Y_PORTRAIT,
705 R.integer.SWITCH_BUTTON_STICK_L_X_PORTRAIT,
706 R.integer.SWITCH_BUTTON_STICK_L_Y_PORTRAIT,
707 R.integer.SWITCH_BUTTON_STICK_R_X_PORTRAIT,
708 R.integer.SWITCH_BUTTON_STICK_R_Y_PORTRAIT
641 ) 709 )
642 710
643 private val foldableResources = arrayOf( 711 private val foldableResources = arrayOf(
@@ -670,139 +738,159 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
670 R.integer.SWITCH_STICK_R_X_FOLDABLE, 738 R.integer.SWITCH_STICK_R_X_FOLDABLE,
671 R.integer.SWITCH_STICK_R_Y_FOLDABLE, 739 R.integer.SWITCH_STICK_R_Y_FOLDABLE,
672 R.integer.SWITCH_STICK_L_X_FOLDABLE, 740 R.integer.SWITCH_STICK_L_X_FOLDABLE,
673 R.integer.SWITCH_STICK_L_Y_FOLDABLE 741 R.integer.SWITCH_STICK_L_Y_FOLDABLE,
742 R.integer.SWITCH_BUTTON_STICK_L_X_FOLDABLE,
743 R.integer.SWITCH_BUTTON_STICK_L_Y_FOLDABLE,
744 R.integer.SWITCH_BUTTON_STICK_R_X_FOLDABLE,
745 R.integer.SWITCH_BUTTON_STICK_R_Y_FOLDABLE
674 ) 746 )
675 747
676 private fun getResourceValue(orientation: String, position: Int): Float { 748 private fun getResourceValue(layout: String, position: Int): Float {
677 return when (orientation) { 749 return when (layout) {
678 PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000 750 PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000
679 FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000 751 FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000
680 else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000 752 else -> resources.getInteger(landscapeResources[position]).toFloat() / 1000
681 } 753 }
682 } 754 }
683 755
684 private fun defaultOverlayByLayout(orientation: String) { 756 private fun defaultOverlayByLayout(layout: String) {
685 // Each value represents the position of the button in relation to the screen size without insets. 757 // Each value represents the position of the button in relation to the screen size without insets.
686 preferences.edit() 758 preferences.edit()
687 .putFloat( 759 .putFloat(
688 ButtonType.BUTTON_A.toString() + "-X$orientation", 760 "${Settings.PREF_BUTTON_A}-X$layout",
689 getResourceValue(orientation, 0) 761 getResourceValue(layout, 0)
762 )
763 .putFloat(
764 "${Settings.PREF_BUTTON_A}-Y$layout",
765 getResourceValue(layout, 1)
766 )
767 .putFloat(
768 "${Settings.PREF_BUTTON_B}-X$layout",
769 getResourceValue(layout, 2)
770 )
771 .putFloat(
772 "${Settings.PREF_BUTTON_B}-Y$layout",
773 getResourceValue(layout, 3)
774 )
775 .putFloat(
776 "${Settings.PREF_BUTTON_X}-X$layout",
777 getResourceValue(layout, 4)
690 ) 778 )
691 .putFloat( 779 .putFloat(
692 ButtonType.BUTTON_A.toString() + "-Y$orientation", 780 "${Settings.PREF_BUTTON_X}-Y$layout",
693 getResourceValue(orientation, 1) 781 getResourceValue(layout, 5)
694 ) 782 )
695 .putFloat( 783 .putFloat(
696 ButtonType.BUTTON_B.toString() + "-X$orientation", 784 "${Settings.PREF_BUTTON_Y}-X$layout",
697 getResourceValue(orientation, 2) 785 getResourceValue(layout, 6)
698 ) 786 )
699 .putFloat( 787 .putFloat(
700 ButtonType.BUTTON_B.toString() + "-Y$orientation", 788 "${Settings.PREF_BUTTON_Y}-Y$layout",
701 getResourceValue(orientation, 3) 789 getResourceValue(layout, 7)
702 ) 790 )
703 .putFloat( 791 .putFloat(
704 ButtonType.BUTTON_X.toString() + "-X$orientation", 792 "${Settings.PREF_BUTTON_ZL}-X$layout",
705 getResourceValue(orientation, 4) 793 getResourceValue(layout, 8)
706 ) 794 )
707 .putFloat( 795 .putFloat(
708 ButtonType.BUTTON_X.toString() + "-Y$orientation", 796 "${Settings.PREF_BUTTON_ZL}-Y$layout",
709 getResourceValue(orientation, 5) 797 getResourceValue(layout, 9)
710 ) 798 )
711 .putFloat( 799 .putFloat(
712 ButtonType.BUTTON_Y.toString() + "-X$orientation", 800 "${Settings.PREF_BUTTON_ZR}-X$layout",
713 getResourceValue(orientation, 6) 801 getResourceValue(layout, 10)
714 ) 802 )
715 .putFloat( 803 .putFloat(
716 ButtonType.BUTTON_Y.toString() + "-Y$orientation", 804 "${Settings.PREF_BUTTON_ZR}-Y$layout",
717 getResourceValue(orientation, 7) 805 getResourceValue(layout, 11)
718 ) 806 )
719 .putFloat( 807 .putFloat(
720 ButtonType.TRIGGER_ZL.toString() + "-X$orientation", 808 "${Settings.PREF_BUTTON_DPAD}-X$layout",
721 getResourceValue(orientation, 8) 809 getResourceValue(layout, 12)
722 ) 810 )
723 .putFloat( 811 .putFloat(
724 ButtonType.TRIGGER_ZL.toString() + "-Y$orientation", 812 "${Settings.PREF_BUTTON_DPAD}-Y$layout",
725 getResourceValue(orientation, 9) 813 getResourceValue(layout, 13)
726 ) 814 )
727 .putFloat( 815 .putFloat(
728 ButtonType.TRIGGER_ZR.toString() + "-X$orientation", 816 "${Settings.PREF_BUTTON_L}-X$layout",
729 getResourceValue(orientation, 10) 817 getResourceValue(layout, 14)
730 ) 818 )
731 .putFloat( 819 .putFloat(
732 ButtonType.TRIGGER_ZR.toString() + "-Y$orientation", 820 "${Settings.PREF_BUTTON_L}-Y$layout",
733 getResourceValue(orientation, 11) 821 getResourceValue(layout, 15)
734 ) 822 )
735 .putFloat( 823 .putFloat(
736 ButtonType.DPAD_UP.toString() + "-X$orientation", 824 "${Settings.PREF_BUTTON_R}-X$layout",
737 getResourceValue(orientation, 12) 825 getResourceValue(layout, 16)
738 ) 826 )
739 .putFloat( 827 .putFloat(
740 ButtonType.DPAD_UP.toString() + "-Y$orientation", 828 "${Settings.PREF_BUTTON_R}-Y$layout",
741 getResourceValue(orientation, 13) 829 getResourceValue(layout, 17)
742 ) 830 )
743 .putFloat( 831 .putFloat(
744 ButtonType.TRIGGER_L.toString() + "-X$orientation", 832 "${Settings.PREF_BUTTON_PLUS}-X$layout",
745 getResourceValue(orientation, 14) 833 getResourceValue(layout, 18)
746 ) 834 )
747 .putFloat( 835 .putFloat(
748 ButtonType.TRIGGER_L.toString() + "-Y$orientation", 836 "${Settings.PREF_BUTTON_PLUS}-Y$layout",
749 getResourceValue(orientation, 15) 837 getResourceValue(layout, 19)
750 ) 838 )
751 .putFloat( 839 .putFloat(
752 ButtonType.TRIGGER_R.toString() + "-X$orientation", 840 "${Settings.PREF_BUTTON_MINUS}-X$layout",
753 getResourceValue(orientation, 16) 841 getResourceValue(layout, 20)
754 ) 842 )
755 .putFloat( 843 .putFloat(
756 ButtonType.TRIGGER_R.toString() + "-Y$orientation", 844 "${Settings.PREF_BUTTON_MINUS}-Y$layout",
757 getResourceValue(orientation, 17) 845 getResourceValue(layout, 21)
758 ) 846 )
759 .putFloat( 847 .putFloat(
760 ButtonType.BUTTON_PLUS.toString() + "-X$orientation", 848 "${Settings.PREF_BUTTON_HOME}-X$layout",
761 getResourceValue(orientation, 18) 849 getResourceValue(layout, 22)
762 ) 850 )
763 .putFloat( 851 .putFloat(
764 ButtonType.BUTTON_PLUS.toString() + "-Y$orientation", 852 "${Settings.PREF_BUTTON_HOME}-Y$layout",
765 getResourceValue(orientation, 19) 853 getResourceValue(layout, 23)
766 ) 854 )
767 .putFloat( 855 .putFloat(
768 ButtonType.BUTTON_MINUS.toString() + "-X$orientation", 856 "${Settings.PREF_BUTTON_SCREENSHOT}-X$layout",
769 getResourceValue(orientation, 20) 857 getResourceValue(layout, 24)
770 ) 858 )
771 .putFloat( 859 .putFloat(
772 ButtonType.BUTTON_MINUS.toString() + "-Y$orientation", 860 "${Settings.PREF_BUTTON_SCREENSHOT}-Y$layout",
773 getResourceValue(orientation, 21) 861 getResourceValue(layout, 25)
774 ) 862 )
775 .putFloat( 863 .putFloat(
776 ButtonType.BUTTON_HOME.toString() + "-X$orientation", 864 "${Settings.PREF_STICK_R}-X$layout",
777 getResourceValue(orientation, 22) 865 getResourceValue(layout, 26)
778 ) 866 )
779 .putFloat( 867 .putFloat(
780 ButtonType.BUTTON_HOME.toString() + "-Y$orientation", 868 "${Settings.PREF_STICK_R}-Y$layout",
781 getResourceValue(orientation, 23) 869 getResourceValue(layout, 27)
782 ) 870 )
783 .putFloat( 871 .putFloat(
784 ButtonType.BUTTON_CAPTURE.toString() + "-X$orientation", 872 "${Settings.PREF_STICK_L}-X$layout",
785 getResourceValue(orientation, 24) 873 getResourceValue(layout, 28)
786 ) 874 )
787 .putFloat( 875 .putFloat(
788 ButtonType.BUTTON_CAPTURE.toString() + "-Y$orientation", 876 "${Settings.PREF_STICK_L}-Y$layout",
789 getResourceValue(orientation, 25) 877 getResourceValue(layout, 29)
790 ) 878 )
791 .putFloat( 879 .putFloat(
792 ButtonType.STICK_R.toString() + "-X$orientation", 880 "${Settings.PREF_BUTTON_STICK_L}-X$layout",
793 getResourceValue(orientation, 26) 881 getResourceValue(layout, 30)
794 ) 882 )
795 .putFloat( 883 .putFloat(
796 ButtonType.STICK_R.toString() + "-Y$orientation", 884 "${Settings.PREF_BUTTON_STICK_L}-Y$layout",
797 getResourceValue(orientation, 27) 885 getResourceValue(layout, 31)
798 ) 886 )
799 .putFloat( 887 .putFloat(
800 ButtonType.STICK_L.toString() + "-X$orientation", 888 "${Settings.PREF_BUTTON_STICK_R}-X$layout",
801 getResourceValue(orientation, 28) 889 getResourceValue(layout, 32)
802 ) 890 )
803 .putFloat( 891 .putFloat(
804 ButtonType.STICK_L.toString() + "-Y$orientation", 892 "${Settings.PREF_BUTTON_STICK_R}-Y$layout",
805 getResourceValue(orientation, 29) 893 getResourceValue(layout, 33)
806 ) 894 )
807 .apply() 895 .apply()
808 } 896 }
@@ -812,12 +900,30 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
812 } 900 }
813 901
814 companion object { 902 companion object {
903 // Increase this number every time there is a breaking change to every overlay layout
904 const val OVERLAY_VERSION = 1
905
906 // Increase the corresponding layout version number whenever that layout has a breaking change
907 private const val LANDSCAPE_OVERLAY_VERSION = 1
908 private const val PORTRAIT_OVERLAY_VERSION = 1
909 private const val FOLDABLE_OVERLAY_VERSION = 1
910 val overlayLayoutVersions = listOf(
911 LANDSCAPE_OVERLAY_VERSION,
912 PORTRAIT_OVERLAY_VERSION,
913 FOLDABLE_OVERLAY_VERSION
914 )
915
815 private val preferences: SharedPreferences = 916 private val preferences: SharedPreferences =
816 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 917 PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
817 918
818 const val LANDSCAPE = "" 919 const val LANDSCAPE = "_Landscape"
819 const val PORTRAIT = "_Portrait" 920 const val PORTRAIT = "_Portrait"
820 const val FOLDABLE = "_Foldable" 921 const val FOLDABLE = "_Foldable"
922 val overlayLayouts = listOf(
923 LANDSCAPE,
924 PORTRAIT,
925 FOLDABLE
926 )
821 927
822 /** 928 /**
823 * Resizes a [Bitmap] by a given scale factor 929 * Resizes a [Bitmap] by a given scale factor
@@ -948,6 +1054,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
948 * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State). 1054 * @param defaultResId The resource ID of the [Drawable] to get the [Bitmap] of (Default State).
949 * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State). 1055 * @param pressedResId The resource ID of the [Drawable] to get the [Bitmap] of (Pressed State).
950 * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents. 1056 * @param buttonId Identifier for determining what type of button the initialized InputOverlayDrawableButton represents.
1057 * @param prefId Identifier for determining where a button appears on screen.
1058 * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
951 * @return An [InputOverlayDrawableButton] with the correct drawing bounds set. 1059 * @return An [InputOverlayDrawableButton] with the correct drawing bounds set.
952 */ 1060 */
953 private fun initializeOverlayButton( 1061 private fun initializeOverlayButton(
@@ -956,7 +1064,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
956 defaultResId: Int, 1064 defaultResId: Int,
957 pressedResId: Int, 1065 pressedResId: Int,
958 buttonId: Int, 1066 buttonId: Int,
959 orientation: String 1067 prefId: String,
1068 layout: String
960 ): InputOverlayDrawableButton { 1069 ): InputOverlayDrawableButton {
961 // Resources handle for fetching the initial Drawable resource. 1070 // Resources handle for fetching the initial Drawable resource.
962 val res = context.resources 1071 val res = context.resources
@@ -964,17 +1073,20 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
964 // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton. 1073 // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton.
965 val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) 1074 val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
966 1075
967 // Decide scale based on button ID and user preference 1076 // Decide scale based on button preference ID and user preference
968 var scale: Float = when (buttonId) { 1077 var scale: Float = when (prefId) {
969 ButtonType.BUTTON_HOME, 1078 Settings.PREF_BUTTON_HOME,
970 ButtonType.BUTTON_CAPTURE, 1079 Settings.PREF_BUTTON_SCREENSHOT,
971 ButtonType.BUTTON_PLUS, 1080 Settings.PREF_BUTTON_PLUS,
972 ButtonType.BUTTON_MINUS -> 0.07f 1081 Settings.PREF_BUTTON_MINUS -> 0.07f
973 1082
974 ButtonType.TRIGGER_L, 1083 Settings.PREF_BUTTON_L,
975 ButtonType.TRIGGER_R, 1084 Settings.PREF_BUTTON_R,
976 ButtonType.TRIGGER_ZL, 1085 Settings.PREF_BUTTON_ZL,
977 ButtonType.TRIGGER_ZR -> 0.26f 1086 Settings.PREF_BUTTON_ZR -> 0.26f
1087
1088 Settings.PREF_BUTTON_STICK_L,
1089 Settings.PREF_BUTTON_STICK_R -> 0.155f
978 1090
979 else -> 0.11f 1091 else -> 0.11f
980 } 1092 }
@@ -984,8 +1096,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
984 // Initialize the InputOverlayDrawableButton. 1096 // Initialize the InputOverlayDrawableButton.
985 val defaultStateBitmap = getBitmap(context, defaultResId, scale) 1097 val defaultStateBitmap = getBitmap(context, defaultResId, scale)
986 val pressedStateBitmap = getBitmap(context, pressedResId, scale) 1098 val pressedStateBitmap = getBitmap(context, pressedResId, scale)
987 val overlayDrawable = 1099 val overlayDrawable = InputOverlayDrawableButton(
988 InputOverlayDrawableButton(res, defaultStateBitmap, pressedStateBitmap, buttonId) 1100 res,
1101 defaultStateBitmap,
1102 pressedStateBitmap,
1103 buttonId,
1104 prefId
1105 )
989 1106
990 // Get the minimum and maximum coordinates of the screen where the button can be placed. 1107 // Get the minimum and maximum coordinates of the screen where the button can be placed.
991 val min = windowSize.first 1108 val min = windowSize.first
@@ -993,8 +1110,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
993 1110
994 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. 1111 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
995 // These were set in the input overlay configuration menu. 1112 // These were set in the input overlay configuration menu.
996 val xKey = "$buttonId-X$orientation" 1113 val xKey = "$prefId-X$layout"
997 val yKey = "$buttonId-Y$orientation" 1114 val yKey = "$prefId-Y$layout"
998 val drawableXPercent = sPrefs.getFloat(xKey, 0f) 1115 val drawableXPercent = sPrefs.getFloat(xKey, 0f)
999 val drawableYPercent = sPrefs.getFloat(yKey, 0f) 1116 val drawableYPercent = sPrefs.getFloat(yKey, 0f)
1000 val drawableX = (drawableXPercent * max.x + min.x).toInt() 1117 val drawableX = (drawableXPercent * max.x + min.x).toInt()
@@ -1029,7 +1146,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1029 * @param defaultResId The [Bitmap] resource ID of the default state. 1146 * @param defaultResId The [Bitmap] resource ID of the default state.
1030 * @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction. 1147 * @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction.
1031 * @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions. 1148 * @param pressedTwoDirectionsResId The [Bitmap] resource ID of the pressed state in two directions.
1032 * @return the initialized [InputOverlayDrawableDpad] 1149 * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
1150 * @return The initialized [InputOverlayDrawableDpad]
1033 */ 1151 */
1034 private fun initializeOverlayDpad( 1152 private fun initializeOverlayDpad(
1035 context: Context, 1153 context: Context,
@@ -1037,7 +1155,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1037 defaultResId: Int, 1155 defaultResId: Int,
1038 pressedOneDirectionResId: Int, 1156 pressedOneDirectionResId: Int,
1039 pressedTwoDirectionsResId: Int, 1157 pressedTwoDirectionsResId: Int,
1040 orientation: String 1158 layout: String
1041 ): InputOverlayDrawableDpad { 1159 ): InputOverlayDrawableDpad {
1042 // Resources handle for fetching the initial Drawable resource. 1160 // Resources handle for fetching the initial Drawable resource.
1043 val res = context.resources 1161 val res = context.resources
@@ -1074,8 +1192,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1074 1192
1075 // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay. 1193 // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay.
1076 // These were set in the input overlay configuration menu. 1194 // These were set in the input overlay configuration menu.
1077 val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-X$orientation", 0f) 1195 val drawableXPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-X$layout", 0f)
1078 val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-Y$orientation", 0f) 1196 val drawableYPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-Y$layout", 0f)
1079 val drawableX = (drawableXPercent * max.x + min.x).toInt() 1197 val drawableX = (drawableXPercent * max.x + min.x).toInt()
1080 val drawableY = (drawableYPercent * max.y + min.y).toInt() 1198 val drawableY = (drawableYPercent * max.y + min.y).toInt()
1081 val width = overlayDrawable.width 1199 val width = overlayDrawable.width
@@ -1107,7 +1225,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1107 * @param pressedResInner Resource ID for the pressed inner image of the joystick. 1225 * @param pressedResInner Resource ID for the pressed inner image of the joystick.
1108 * @param joystick Identifier for which joystick this is. 1226 * @param joystick Identifier for which joystick this is.
1109 * @param button Identifier for which joystick button this is. 1227 * @param button Identifier for which joystick button this is.
1110 * @return the initialized [InputOverlayDrawableJoystick]. 1228 * @param prefId Identifier for determining where a button appears on screen.
1229 * @param layout The current screen layout as determined by [LANDSCAPE], [PORTRAIT], or [FOLDABLE].
1230 * @return The initialized [InputOverlayDrawableJoystick].
1111 */ 1231 */
1112 private fun initializeOverlayJoystick( 1232 private fun initializeOverlayJoystick(
1113 context: Context, 1233 context: Context,
@@ -1117,7 +1237,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1117 pressedResInner: Int, 1237 pressedResInner: Int,
1118 joystick: Int, 1238 joystick: Int,
1119 button: Int, 1239 button: Int,
1120 orientation: String 1240 prefId: String,
1241 layout: String
1121 ): InputOverlayDrawableJoystick { 1242 ): InputOverlayDrawableJoystick {
1122 // Resources handle for fetching the initial Drawable resource. 1243 // Resources handle for fetching the initial Drawable resource.
1123 val res = context.resources 1244 val res = context.resources
@@ -1141,8 +1262,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1141 1262
1142 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay. 1263 // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
1143 // These were set in the input overlay configuration menu. 1264 // These were set in the input overlay configuration menu.
1144 val drawableXPercent = sPrefs.getFloat("$button-X$orientation", 0f) 1265 val drawableXPercent = sPrefs.getFloat("$prefId-X$layout", 0f)
1145 val drawableYPercent = sPrefs.getFloat("$button-Y$orientation", 0f) 1266 val drawableYPercent = sPrefs.getFloat("$prefId-Y$layout", 0f)
1146 val drawableX = (drawableXPercent * max.x + min.x).toInt() 1267 val drawableX = (drawableXPercent * max.x + min.x).toInt()
1147 val drawableY = (drawableYPercent * max.y + min.y).toInt() 1268 val drawableY = (drawableYPercent * max.y + min.y).toInt()
1148 val outerScale = 1.66f 1269 val outerScale = 1.66f
@@ -1168,7 +1289,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
1168 outerRect, 1289 outerRect,
1169 innerRect, 1290 innerRect,
1170 joystick, 1291 joystick,
1171 button 1292 button,
1293 prefId
1172 ) 1294 )
1173 1295
1174 // Need to set the image's position 1296 // 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(
24 res: Resources, 24 res: Resources,
25 defaultStateBitmap: Bitmap, 25 defaultStateBitmap: Bitmap,
26 pressedStateBitmap: Bitmap, 26 pressedStateBitmap: Bitmap,
27 val buttonId: Int 27 val buttonId: Int,
28 val prefId: String
28) { 29) {
29 // The ID value what motion event is tracking 30 // The ID value what motion event is tracking
30 var trackId: Int 31 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(
37 rectOuter: Rect, 37 rectOuter: Rect,
38 rectInner: Rect, 38 rectInner: Rect,
39 val joystickId: Int, 39 val joystickId: Int,
40 val buttonId: Int 40 val buttonId: Int,
41 val prefId: String
41) { 42) {
42 // The ID value what motion event is tracking 43 // The ID value what motion event is tracking
43 var trackId = -1 44 var trackId = -1
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 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:aapt="http://schemas.android.com/aapt"
3 android:width="34.963dp"
4 android:height="37.265dp"
5 android:viewportWidth="34.963"
6 android:viewportHeight="37.265">
7 <path
8 android:fillAlpha="0.5"
9 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"
10 android:strokeAlpha="0.6">
11 <aapt:attr name="android:fillColor">
12 <gradient
13 android:endX="21.568"
14 android:endY="33.938"
15 android:startX="21.568"
16 android:startY="16.14"
17 android:type="linear">
18 <item
19 android:color="#FFC3C4C5"
20 android:offset="0" />
21 <item
22 android:color="#FFC5C6C6"
23 android:offset="0.03" />
24 <item
25 android:color="#FFC7C7C7"
26 android:offset="0.19" />
27 <item
28 android:color="#DBB5B5B5"
29 android:offset="0.44" />
30 <item
31 android:color="#7F878787"
32 android:offset="1" />
33 </gradient>
34 </aapt:attr>
35 </path>
36 <path
37 android:fillAlpha="0.5"
38 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"
39 android:strokeAlpha="0.6">
40 <aapt:attr name="android:fillColor">
41 <gradient
42 android:endX="17.395"
43 android:endY="18.74"
44 android:startX="17.395"
45 android:startY="-1.296"
46 android:type="linear">
47 <item
48 android:color="#FFC3C4C5"
49 android:offset="0" />
50 <item
51 android:color="#FFC5C6C6"
52 android:offset="0.03" />
53 <item
54 android:color="#FFC7C7C7"
55 android:offset="0.19" />
56 <item
57 android:color="#DBB5B5B5"
58 android:offset="0.44" />
59 <item
60 android:color="#7F878787"
61 android:offset="1" />
62 </gradient>
63 </aapt:attr>
64 </path>
65 <path
66 android:fillAlpha="0.5"
67 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"
68 android:strokeAlpha="0.6">
69 <aapt:attr name="android:fillColor">
70 <gradient
71 android:centerX="17.477"
72 android:centerY="19.92"
73 android:gradientRadius="17.201"
74 android:type="radial">
75 <item
76 android:color="#FFC3C4C5"
77 android:offset="0.58" />
78 <item
79 android:color="#FFC6C6C6"
80 android:offset="0.84" />
81 <item
82 android:color="#FFC7C7C7"
83 android:offset="0.88" />
84 <item
85 android:color="#FFC2C2C2"
86 android:offset="0.91" />
87 <item
88 android:color="#FFB5B5B5"
89 android:offset="0.94" />
90 <item
91 android:color="#FF9E9E9E"
92 android:offset="0.98" />
93 <item
94 android:color="#FF8F8F8F"
95 android:offset="1" />
96 </gradient>
97 </aapt:attr>
98 </path>
99 <path
100 android:fillAlpha="0.5"
101 android:pathData="m12.516,12.729l2,0l0,13.822l6.615,0l0,1.68L12.516,28.231Z"
102 android:strokeAlpha="0.6">
103 <aapt:attr name="android:fillColor">
104 <gradient
105 android:endX="16.829"
106 android:endY="46.882"
107 android:startX="16.829"
108 android:startY="20.479"
109 android:type="linear">
110 <item
111 android:color="#FFC3C4C5"
112 android:offset="0" />
113 <item
114 android:color="#FFC5C6C6"
115 android:offset="0.03" />
116 <item
117 android:color="#FFC7C7C7"
118 android:offset="0.19" />
119 <item
120 android:color="#DBB5B5B5"
121 android:offset="0.44" />
122 <item
123 android:color="#7F878787"
124 android:offset="1" />
125 </gradient>
126 </aapt:attr>
127 </path>
128</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 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:aapt="http://schemas.android.com/aapt"
3 android:width="34.963dp"
4 android:height="37.265dp"
5 android:viewportWidth="34.963"
6 android:viewportHeight="37.265">
7 <path
8 android:fillAlpha="0.3"
9 android:fillColor="#151515"
10 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"
11 android:strokeAlpha="0.3" />
12 <path
13 android:fillAlpha="0.6"
14 android:fillColor="#151515"
15 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"
16 android:strokeAlpha="0.6" />
17 <path
18 android:fillAlpha="0.6"
19 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"
20 android:strokeAlpha="0.6">
21 <aapt:attr name="android:fillColor">
22 <gradient
23 android:endX="21.568"
24 android:endY="33.938"
25 android:startX="21.568"
26 android:startY="16.14"
27 android:type="linear">
28 <item
29 android:color="#FFC3C4C5"
30 android:offset="0" />
31 <item
32 android:color="#FFC5C6C6"
33 android:offset="0.03" />
34 <item
35 android:color="#FFC7C7C7"
36 android:offset="0.19" />
37 <item
38 android:color="#DBB5B5B5"
39 android:offset="0.44" />
40 <item
41 android:color="#7F878787"
42 android:offset="1" />
43 </gradient>
44 </aapt:attr>
45 </path>
46 <path
47 android:fillAlpha="0.6"
48 android:pathData="m12.516,12.729l2,0l0,13.822l6.615,0l0,1.68L12.516,28.231Z"
49 android:strokeAlpha="0.6">
50 <aapt:attr name="android:fillColor">
51 <gradient
52 android:endX="16.829"
53 android:endY="46.882"
54 android:startX="16.829"
55 android:startY="20.479"
56 android:type="linear">
57 <item
58 android:color="#FFC3C4C5"
59 android:offset="0" />
60 <item
61 android:color="#FFC5C6C6"
62 android:offset="0.03" />
63 <item
64 android:color="#FFC7C7C7"
65 android:offset="0.19" />
66 <item
67 android:color="#DBB5B5B5"
68 android:offset="0.44" />
69 <item
70 android:color="#7F878787"
71 android:offset="1" />
72 </gradient>
73 </aapt:attr>
74 </path>
75</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 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:aapt="http://schemas.android.com/aapt"
3 android:width="34.963dp"
4 android:height="37.265dp"
5 android:viewportWidth="34.963"
6 android:viewportHeight="37.265">
7 <path
8 android:fillAlpha="0.5"
9 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"
10 android:strokeAlpha="0.6">
11 <aapt:attr name="android:fillColor">
12 <gradient
13 android:endX="15.506"
14 android:endY="48.977"
15 android:startX="15.506"
16 android:startY="19.659"
17 android:type="linear">
18 <item
19 android:color="#FFC3C4C5"
20 android:offset="0" />
21 <item
22 android:color="#FFC5C6C6"
23 android:offset="0.03" />
24 <item
25 android:color="#FFC7C7C7"
26 android:offset="0.19" />
27 <item
28 android:color="#DBB5B5B5"
29 android:offset="0.44" />
30 <item
31 android:color="#7F878787"
32 android:offset="1" />
33 </gradient>
34 </aapt:attr>
35 </path>
36 <path
37 android:fillAlpha="0.5"
38 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"
39 android:strokeAlpha="0.6">
40 <aapt:attr name="android:fillColor">
41 <gradient
42 android:endX="17.395"
43 android:endY="18.74"
44 android:startX="17.395"
45 android:startY="-1.296"
46 android:type="linear">
47 <item
48 android:color="#FFC3C4C5"
49 android:offset="0" />
50 <item
51 android:color="#FFC5C6C6"
52 android:offset="0.03" />
53 <item
54 android:color="#FFC7C7C7"
55 android:offset="0.19" />
56 <item
57 android:color="#DBB5B5B5"
58 android:offset="0.44" />
59 <item
60 android:color="#7F878787"
61 android:offset="1" />
62 </gradient>
63 </aapt:attr>
64 </path>
65 <path
66 android:fillAlpha="0.5"
67 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"
68 android:strokeAlpha="0.6">
69 <aapt:attr name="android:fillColor">
70 <gradient
71 android:centerX="17.477"
72 android:centerY="19.92"
73 android:gradientRadius="17.201"
74 android:type="radial">
75 <item
76 android:color="#FFC3C4C5"
77 android:offset="0.58" />
78 <item
79 android:color="#FFC6C6C6"
80 android:offset="0.84" />
81 <item
82 android:color="#FFC7C7C7"
83 android:offset="0.88" />
84 <item
85 android:color="#FFC2C2C2"
86 android:offset="0.91" />
87 <item
88 android:color="#FFB5B5B5"
89 android:offset="0.94" />
90 <item
91 android:color="#FF9E9E9E"
92 android:offset="0.98" />
93 <item
94 android:color="#FF8F8F8F"
95 android:offset="1" />
96 </gradient>
97 </aapt:attr>
98 </path>
99 <path
100 android:fillAlpha="0.5"
101 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"
102 android:strokeAlpha="0.6">
103 <aapt:attr name="android:fillColor">
104 <gradient
105 android:endX="23.949"
106 android:endY="33.938"
107 android:startX="23.949"
108 android:startY="16.14"
109 android:type="linear">
110 <item
111 android:color="#FFC3C4C5"
112 android:offset="0" />
113 <item
114 android:color="#FFC5C6C6"
115 android:offset="0.03" />
116 <item
117 android:color="#FFC7C7C7"
118 android:offset="0.19" />
119 <item
120 android:color="#DBB5B5B5"
121 android:offset="0.44" />
122 <item
123 android:color="#7F878787"
124 android:offset="1" />
125 </gradient>
126 </aapt:attr>
127 </path>
128</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 @@
1<vector xmlns:android="http://schemas.android.com/apk/res/android"
2 xmlns:aapt="http://schemas.android.com/aapt"
3 android:width="34.963dp"
4 android:height="37.265dp"
5 android:viewportWidth="34.963"
6 android:viewportHeight="37.265">
7 <path
8 android:fillAlpha="0.3"
9 android:fillColor="#151515"
10 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"
11 android:strokeAlpha="0.3" />
12 <path
13 android:fillAlpha="0.6"
14 android:fillColor="#151515"
15 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"
16 android:strokeAlpha="0.6" />
17 <path
18 android:fillAlpha="0.6"
19 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"
20 android:strokeAlpha="0.6">
21 <aapt:attr name="android:fillColor">
22 <gradient
23 android:endX="15.506"
24 android:endY="48.977"
25 android:startX="15.506"
26 android:startY="19.659"
27 android:type="linear">
28 <item
29 android:color="#FFC3C4C5"
30 android:offset="0" />
31 <item
32 android:color="#FFC5C6C6"
33 android:offset="0.03" />
34 <item
35 android:color="#FFC7C7C7"
36 android:offset="0.19" />
37 <item
38 android:color="#DBB5B5B5"
39 android:offset="0.44" />
40 <item
41 android:color="#7F878787"
42 android:offset="1" />
43 </gradient>
44 </aapt:attr>
45 </path>
46 <path
47 android:fillAlpha="0.6"
48 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"
49 android:strokeAlpha="0.6">
50 <aapt:attr name="android:fillColor">
51 <gradient
52 android:endX="23.949"
53 android:endY="33.938"
54 android:startX="23.949"
55 android:startY="16.14"
56 android:type="linear">
57 <item
58 android:color="#FFC3C4C5"
59 android:offset="0" />
60 <item
61 android:color="#FFC5C6C6"
62 android:offset="0.03" />
63 <item
64 android:color="#FFC7C7C7"
65 android:offset="0.19" />
66 <item
67 android:color="#DBB5B5B5"
68 android:offset="0.44" />
69 <item
70 android:color="#7F878787"
71 android:offset="1" />
72 </gradient>
73 </aapt:attr>
74 </path>
75</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 @@
205 <item>@string/gamepad_d_pad</item> 205 <item>@string/gamepad_d_pad</item>
206 <item>@string/gamepad_left_stick</item> 206 <item>@string/gamepad_left_stick</item>
207 <item>@string/gamepad_right_stick</item> 207 <item>@string/gamepad_right_stick</item>
208 <item>L3</item>
209 <item>R3</item>
208 <item>@string/gamepad_home</item> 210 <item>@string/gamepad_home</item>
209 <item>@string/gamepad_screenshot</item> 211 <item>@string/gamepad_screenshot</item>
210 </string-array> 212 </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 @@
33 <integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer> 33 <integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer>
34 <integer name="SWITCH_BUTTON_DPAD_X">260</integer> 34 <integer name="SWITCH_BUTTON_DPAD_X">260</integer>
35 <integer name="SWITCH_BUTTON_DPAD_Y">790</integer> 35 <integer name="SWITCH_BUTTON_DPAD_Y">790</integer>
36 <integer name="SWITCH_BUTTON_STICK_L_X">870</integer>
37 <integer name="SWITCH_BUTTON_STICK_L_Y">400</integer>
38 <integer name="SWITCH_BUTTON_STICK_R_X">960</integer>
39 <integer name="SWITCH_BUTTON_STICK_R_Y">430</integer>
36 40
37 <!-- Default SWITCH portrait layout --> 41 <!-- Default SWITCH portrait layout -->
38 <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer> 42 <integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer>
@@ -65,6 +69,10 @@
65 <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer> 69 <integer name="SWITCH_BUTTON_CAPTURE_Y_PORTRAIT">950</integer>
66 <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer> 70 <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer>
67 <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer> 71 <integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</integer>
72 <integer name="SWITCH_BUTTON_STICK_L_X_PORTRAIT">730</integer>
73 <integer name="SWITCH_BUTTON_STICK_L_Y_PORTRAIT">510</integer>
74 <integer name="SWITCH_BUTTON_STICK_R_X_PORTRAIT">900</integer>
75 <integer name="SWITCH_BUTTON_STICK_R_Y_PORTRAIT">540</integer>
68 76
69 <!-- Default SWITCH foldable layout --> 77 <!-- Default SWITCH foldable layout -->
70 <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer> 78 <integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer>
@@ -97,5 +105,9 @@
97 <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer> 105 <integer name="SWITCH_BUTTON_CAPTURE_Y_FOLDABLE">470</integer>
98 <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer> 106 <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer>
99 <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer> 107 <integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</integer>
108 <integer name="SWITCH_BUTTON_STICK_L_X_FOLDABLE">550</integer>
109 <integer name="SWITCH_BUTTON_STICK_L_Y_FOLDABLE">210</integer>
110 <integer name="SWITCH_BUTTON_STICK_R_X_FOLDABLE">550</integer>
111 <integer name="SWITCH_BUTTON_STICK_R_Y_FOLDABLE">280</integer>
100 112
101</resources> 113</resources>