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.
This commit is contained in:
Charles Lombardo 2023-06-27 02:53:14 -04:00
parent 95ceae40e6
commit 68f6f2671b
11 changed files with 751 additions and 170 deletions

View File

@ -112,25 +112,36 @@ class Settings {
const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown" const val PREF_MEMORY_WARNING_SHOWN = "MemoryWarningShown"
const val PREF_OVERLAY_INIT = "OverlayInit" 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_SCALE = "controlScale"
const val PREF_CONTROL_OPACITY = "controlOpacity" const val PREF_CONTROL_OPACITY = "controlOpacity"
const val PREF_TOUCH_ENABLED = "isTouchEnabled" const val PREF_TOUCH_ENABLED = "isTouchEnabled"
const val PREF_BUTTON_TOGGLE_0 = "buttonToggle0" const val PREF_BUTTON_A = "buttonToggle0"
const val PREF_BUTTON_TOGGLE_1 = "buttonToggle1" const val PREF_BUTTON_B = "buttonToggle1"
const val PREF_BUTTON_TOGGLE_2 = "buttonToggle2" const val PREF_BUTTON_X = "buttonToggle2"
const val PREF_BUTTON_TOGGLE_3 = "buttonToggle3" const val PREF_BUTTON_Y = "buttonToggle3"
const val PREF_BUTTON_TOGGLE_4 = "buttonToggle4" const val PREF_BUTTON_L = "buttonToggle4"
const val PREF_BUTTON_TOGGLE_5 = "buttonToggle5" const val PREF_BUTTON_R = "buttonToggle5"
const val PREF_BUTTON_TOGGLE_6 = "buttonToggle6" const val PREF_BUTTON_ZL = "buttonToggle6"
const val PREF_BUTTON_TOGGLE_7 = "buttonToggle7" const val PREF_BUTTON_ZR = "buttonToggle7"
const val PREF_BUTTON_TOGGLE_8 = "buttonToggle8" const val PREF_BUTTON_PLUS = "buttonToggle8"
const val PREF_BUTTON_TOGGLE_9 = "buttonToggle9" const val PREF_BUTTON_MINUS = "buttonToggle9"
const val PREF_BUTTON_TOGGLE_10 = "buttonToggle10" const val PREF_BUTTON_DPAD = "buttonToggle10"
const val PREF_BUTTON_TOGGLE_11 = "buttonToggle11" const val PREF_STICK_L = "buttonToggle11"
const val PREF_BUTTON_TOGGLE_12 = "buttonToggle12" const val PREF_STICK_R = "buttonToggle12"
const val PREF_BUTTON_TOGGLE_13 = "buttonToggle13" const val PREF_BUTTON_STICK_L = "buttonToggle13"
const val PREF_BUTTON_TOGGLE_14 = "buttonToggle14" 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_JOYSTICK_REL_CENTER = "EmulationMenuSettings_JoystickRelCenter"
const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable" const val PREF_MENU_SETTINGS_DPAD_SLIDE = "EmulationMenuSettings_DpadSlideEnable"
@ -145,6 +156,30 @@ class Settings {
private val configFileSectionsMap: MutableMap<String, List<String>> = HashMap() 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_Unspecified = 0
const val LayoutOption_MobilePortrait = 4 const val LayoutOption_MobilePortrait = 4
const val LayoutOption_MobileLandscape = 5 const val LayoutOption_MobileLandscape = 5

View File

@ -212,9 +212,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
} }
if (!isInFoldableLayout) { if (!isInFoldableLayout) {
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
binding.surfaceInputOverlay.orientation = InputOverlay.PORTRAIT binding.surfaceInputOverlay.layout = InputOverlay.PORTRAIT
} else { } else {
binding.surfaceInputOverlay.orientation = InputOverlay.LANDSCAPE binding.surfaceInputOverlay.layout = InputOverlay.LANDSCAPE
} }
} }
if (!binding.surfaceInputOverlay.isInEditMode) { if (!binding.surfaceInputOverlay.isInEditMode) {
@ -260,7 +260,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
.remove(Settings.PREF_CONTROL_SCALE) .remove(Settings.PREF_CONTROL_SCALE)
.remove(Settings.PREF_CONTROL_OPACITY) .remove(Settings.PREF_CONTROL_OPACITY)
.apply() .apply()
binding.surfaceInputOverlay.post { binding.surfaceInputOverlay.resetButtonPlacement() } binding.surfaceInputOverlay.post {
binding.surfaceInputOverlay.resetLayoutVisibilityAndPlacement()
}
} }
private fun updateShowFpsOverlay() { private fun updateShowFpsOverlay() {
@ -337,7 +339,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
binding.inGameMenu.layoutParams.height = it.bounds.bottom binding.inGameMenu.layoutParams.height = it.bounds.bottom
isInFoldableLayout = true isInFoldableLayout = true
binding.surfaceInputOverlay.orientation = InputOverlay.FOLDABLE binding.surfaceInputOverlay.layout = InputOverlay.FOLDABLE
refreshInputOverlay() refreshInputOverlay()
} }
} }
@ -410,9 +412,9 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
R.id.menu_toggle_controls -> { R.id.menu_toggle_controls -> {
val preferences = val preferences =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
val optionsArray = BooleanArray(15) val optionsArray = BooleanArray(Settings.overlayPreferences.size)
for (i in 0..14) { Settings.overlayPreferences.forEachIndexed { i, _ ->
optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 13) optionsArray[i] = preferences.getBoolean("buttonToggle$i", i < 15)
} }
val dialog = MaterialAlertDialogBuilder(requireContext()) val dialog = MaterialAlertDialogBuilder(requireContext())
@ -436,7 +438,7 @@ class EmulationFragment : Fragment(), SurfaceHolder.Callback {
dialog.getButton(AlertDialog.BUTTON_NEUTRAL) dialog.getButton(AlertDialog.BUTTON_NEUTRAL)
.setOnClickListener { .setOnClickListener {
val isChecked = !optionsArray[0] val isChecked = !optionsArray[0]
for (i in 0..14) { Settings.overlayPreferences.forEachIndexed { i, _ ->
optionsArray[i] = isChecked optionsArray[i] = isChecked
dialog.listView.setItemChecked(i, isChecked) dialog.listView.setItemChecked(i, isChecked)
preferences.edit() preferences.edit()

View File

@ -51,15 +51,23 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
private lateinit var windowInsets: WindowInsets private lateinit var windowInsets: WindowInsets
var orientation = LANDSCAPE var layout = LANDSCAPE
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) { override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom) super.onLayout(changed, left, top, right, bottom)
windowInsets = rootWindowInsets windowInsets = rootWindowInsets
if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) { val overlayVersion = preferences.getInt(Settings.PREF_OVERLAY_VERSION, 0)
defaultOverlay() 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. // Load the controls.
@ -266,10 +274,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) { MotionEvent.ACTION_POINTER_UP -> if (buttonBeingConfigured === button) {
// Persist button position by saving new place. // Persist button position by saving new place.
saveControlPosition( saveControlPosition(
buttonBeingConfigured!!.buttonId, buttonBeingConfigured!!.prefId,
buttonBeingConfigured!!.bounds.centerX(), buttonBeingConfigured!!.bounds.centerX(),
buttonBeingConfigured!!.bounds.centerY(), buttonBeingConfigured!!.bounds.centerY(),
orientation layout
) )
buttonBeingConfigured = null buttonBeingConfigured = null
} }
@ -299,10 +307,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) { MotionEvent.ACTION_POINTER_UP -> if (dpadBeingConfigured === dpad) {
// Persist button position by saving new place. // Persist button position by saving new place.
saveControlPosition( saveControlPosition(
dpadBeingConfigured!!.upId, Settings.PREF_BUTTON_DPAD,
dpadBeingConfigured!!.bounds.centerX(), dpadBeingConfigured!!.bounds.centerX(),
dpadBeingConfigured!!.bounds.centerY(), dpadBeingConfigured!!.bounds.centerY(),
orientation layout
) )
dpadBeingConfigured = null dpadBeingConfigured = null
} }
@ -330,10 +338,10 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
MotionEvent.ACTION_UP, MotionEvent.ACTION_UP,
MotionEvent.ACTION_POINTER_UP -> if (joystickBeingConfigured != null) { MotionEvent.ACTION_POINTER_UP -> if (joystickBeingConfigured != null) {
saveControlPosition( saveControlPosition(
joystickBeingConfigured!!.buttonId, joystickBeingConfigured!!.prefId,
joystickBeingConfigured!!.bounds.centerX(), joystickBeingConfigured!!.bounds.centerX(),
joystickBeingConfigured!!.bounds.centerY(), joystickBeingConfigured!!.bounds.centerY(),
orientation layout
) )
joystickBeingConfigured = null joystickBeingConfigured = null
} }
@ -343,9 +351,9 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
return true return true
} }
private fun addOverlayControls(orientation: String) { private fun addOverlayControls(layout: String) {
val windowSize = getSafeScreenSize(context) val windowSize = getSafeScreenSize(context)
if (preferences.getBoolean(Settings.PREF_BUTTON_TOGGLE_0, true)) { if (preferences.getBoolean(Settings.PREF_BUTTON_A, true)) {
overlayButtons.add( overlayButtons.add(
initializeOverlayButton( initializeOverlayButton(
context, context,
@ -353,11 +361,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.facebutton_a, R.drawable.facebutton_a,
R.drawable.facebutton_a_depressed, R.drawable.facebutton_a_depressed,
ButtonType.BUTTON_A, 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( overlayButtons.add(
initializeOverlayButton( initializeOverlayButton(
context, context,
@ -365,11 +374,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.facebutton_b, R.drawable.facebutton_b,
R.drawable.facebutton_b_depressed, R.drawable.facebutton_b_depressed,
ButtonType.BUTTON_B, 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( overlayButtons.add(
initializeOverlayButton( initializeOverlayButton(
context, context,
@ -377,11 +387,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.facebutton_x, R.drawable.facebutton_x,
R.drawable.facebutton_x_depressed, R.drawable.facebutton_x_depressed,
ButtonType.BUTTON_X, 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( overlayButtons.add(
initializeOverlayButton( initializeOverlayButton(
context, context,
@ -389,11 +400,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.facebutton_y, R.drawable.facebutton_y,
R.drawable.facebutton_y_depressed, R.drawable.facebutton_y_depressed,
ButtonType.BUTTON_Y, 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( overlayButtons.add(
initializeOverlayButton( initializeOverlayButton(
context, context,
@ -401,11 +413,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.l_shoulder, R.drawable.l_shoulder,
R.drawable.l_shoulder_depressed, R.drawable.l_shoulder_depressed,
ButtonType.TRIGGER_L, 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( overlayButtons.add(
initializeOverlayButton( initializeOverlayButton(
context, context,
@ -413,11 +426,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.r_shoulder, R.drawable.r_shoulder,
R.drawable.r_shoulder_depressed, R.drawable.r_shoulder_depressed,
ButtonType.TRIGGER_R, 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( overlayButtons.add(
initializeOverlayButton( initializeOverlayButton(
context, context,
@ -425,11 +439,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.zl_trigger, R.drawable.zl_trigger,
R.drawable.zl_trigger_depressed, R.drawable.zl_trigger_depressed,
ButtonType.TRIGGER_ZL, 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( overlayButtons.add(
initializeOverlayButton( initializeOverlayButton(
context, context,
@ -437,11 +452,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.zr_trigger, R.drawable.zr_trigger,
R.drawable.zr_trigger_depressed, R.drawable.zr_trigger_depressed,
ButtonType.TRIGGER_ZR, 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( overlayButtons.add(
initializeOverlayButton( initializeOverlayButton(
context, context,
@ -449,11 +465,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.facebutton_plus, R.drawable.facebutton_plus,
R.drawable.facebutton_plus_depressed, R.drawable.facebutton_plus_depressed,
ButtonType.BUTTON_PLUS, 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( overlayButtons.add(
initializeOverlayButton( initializeOverlayButton(
context, context,
@ -461,11 +478,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.facebutton_minus, R.drawable.facebutton_minus,
R.drawable.facebutton_minus_depressed, R.drawable.facebutton_minus_depressed,
ButtonType.BUTTON_MINUS, 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( overlayDpads.add(
initializeOverlayDpad( initializeOverlayDpad(
context, context,
@ -473,11 +491,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.dpad_standard, R.drawable.dpad_standard,
R.drawable.dpad_standard_cardinal_depressed, R.drawable.dpad_standard_cardinal_depressed,
R.drawable.dpad_standard_diagonal_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( overlayJoysticks.add(
initializeOverlayJoystick( initializeOverlayJoystick(
context, context,
@ -487,11 +505,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.joystick_depressed, R.drawable.joystick_depressed,
StickType.STICK_L, StickType.STICK_L,
ButtonType.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( overlayJoysticks.add(
initializeOverlayJoystick( initializeOverlayJoystick(
context, context,
@ -501,11 +520,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.joystick_depressed, R.drawable.joystick_depressed,
StickType.STICK_R, StickType.STICK_R,
ButtonType.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( overlayButtons.add(
initializeOverlayButton( initializeOverlayButton(
context, context,
@ -513,11 +533,12 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.facebutton_home, R.drawable.facebutton_home,
R.drawable.facebutton_home_depressed, R.drawable.facebutton_home_depressed,
ButtonType.BUTTON_HOME, 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( overlayButtons.add(
initializeOverlayButton( initializeOverlayButton(
context, context,
@ -525,7 +546,34 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.drawable.facebutton_screenshot, R.drawable.facebutton_screenshot,
R.drawable.facebutton_screenshot_depressed, R.drawable.facebutton_screenshot_depressed,
ButtonType.BUTTON_CAPTURE, 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. // Add all the enabled overlay items back to the HashSet.
if (EmulationMenuSettings.showOverlay) { if (EmulationMenuSettings.showOverlay) {
addOverlayControls(orientation) addOverlayControls(layout)
} }
invalidate() 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 windowSize = getSafeScreenSize(context)
val min = windowSize.first val min = windowSize.first
val max = windowSize.second val max = windowSize.second
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit() PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext).edit()
.putFloat("$sharedPrefsId-X$orientation", (x - min.x).toFloat() / max.x) .putFloat("$prefId-X$layout", (x - min.x).toFloat() / max.x)
.putFloat("$sharedPrefsId-Y$orientation", (y - min.y).toFloat() / max.y) .putFloat("$prefId-Y$layout", (y - min.y).toFloat() / max.y)
.apply() .apply()
} }
@ -558,19 +606,31 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
inEditMode = editMode inEditMode = editMode
} }
private fun defaultOverlay() { private fun resetCurrentLayout() {
if (!preferences.getBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", false)) { defaultOverlayByLayout(layout)
defaultOverlayByLayout(orientation) val layoutIndex = overlayLayouts.indexOf(layout)
}
resetButtonPlacement()
preferences.edit() preferences.edit()
.putBoolean("${Settings.PREF_OVERLAY_INIT}$orientation", true) .putInt(Settings.overlayLayoutPrefs[layoutIndex], overlayLayoutVersions[layoutIndex])
.apply() .apply()
} }
fun resetButtonPlacement() { private fun resetAllLayouts() {
defaultOverlayByLayout(orientation) 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() refreshControls()
} }
@ -604,7 +664,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
R.integer.SWITCH_STICK_R_X, R.integer.SWITCH_STICK_R_X,
R.integer.SWITCH_STICK_R_Y, R.integer.SWITCH_STICK_R_Y,
R.integer.SWITCH_STICK_L_X, 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( 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_X_PORTRAIT,
R.integer.SWITCH_STICK_R_Y_PORTRAIT, R.integer.SWITCH_STICK_R_Y_PORTRAIT,
R.integer.SWITCH_STICK_L_X_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( 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_X_FOLDABLE,
R.integer.SWITCH_STICK_R_Y_FOLDABLE, R.integer.SWITCH_STICK_R_Y_FOLDABLE,
R.integer.SWITCH_STICK_L_X_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 { private fun getResourceValue(layout: String, position: Int): Float {
return when (orientation) { return when (layout) {
PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000 PORTRAIT -> resources.getInteger(portraitResources[position]).toFloat() / 1000
FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000 FOLDABLE -> resources.getInteger(foldableResources[position]).toFloat() / 1000
else -> resources.getInteger(landscapeResources[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. // Each value represents the position of the button in relation to the screen size without insets.
preferences.edit() preferences.edit()
.putFloat( .putFloat(
ButtonType.BUTTON_A.toString() + "-X$orientation", "${Settings.PREF_BUTTON_A}-X$layout",
getResourceValue(orientation, 0) getResourceValue(layout, 0)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_A.toString() + "-Y$orientation", "${Settings.PREF_BUTTON_A}-Y$layout",
getResourceValue(orientation, 1) getResourceValue(layout, 1)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_B.toString() + "-X$orientation", "${Settings.PREF_BUTTON_B}-X$layout",
getResourceValue(orientation, 2) getResourceValue(layout, 2)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_B.toString() + "-Y$orientation", "${Settings.PREF_BUTTON_B}-Y$layout",
getResourceValue(orientation, 3) getResourceValue(layout, 3)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_X.toString() + "-X$orientation", "${Settings.PREF_BUTTON_X}-X$layout",
getResourceValue(orientation, 4) getResourceValue(layout, 4)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_X.toString() + "-Y$orientation", "${Settings.PREF_BUTTON_X}-Y$layout",
getResourceValue(orientation, 5) getResourceValue(layout, 5)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_Y.toString() + "-X$orientation", "${Settings.PREF_BUTTON_Y}-X$layout",
getResourceValue(orientation, 6) getResourceValue(layout, 6)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_Y.toString() + "-Y$orientation", "${Settings.PREF_BUTTON_Y}-Y$layout",
getResourceValue(orientation, 7) getResourceValue(layout, 7)
) )
.putFloat( .putFloat(
ButtonType.TRIGGER_ZL.toString() + "-X$orientation", "${Settings.PREF_BUTTON_ZL}-X$layout",
getResourceValue(orientation, 8) getResourceValue(layout, 8)
) )
.putFloat( .putFloat(
ButtonType.TRIGGER_ZL.toString() + "-Y$orientation", "${Settings.PREF_BUTTON_ZL}-Y$layout",
getResourceValue(orientation, 9) getResourceValue(layout, 9)
) )
.putFloat( .putFloat(
ButtonType.TRIGGER_ZR.toString() + "-X$orientation", "${Settings.PREF_BUTTON_ZR}-X$layout",
getResourceValue(orientation, 10) getResourceValue(layout, 10)
) )
.putFloat( .putFloat(
ButtonType.TRIGGER_ZR.toString() + "-Y$orientation", "${Settings.PREF_BUTTON_ZR}-Y$layout",
getResourceValue(orientation, 11) getResourceValue(layout, 11)
) )
.putFloat( .putFloat(
ButtonType.DPAD_UP.toString() + "-X$orientation", "${Settings.PREF_BUTTON_DPAD}-X$layout",
getResourceValue(orientation, 12) getResourceValue(layout, 12)
) )
.putFloat( .putFloat(
ButtonType.DPAD_UP.toString() + "-Y$orientation", "${Settings.PREF_BUTTON_DPAD}-Y$layout",
getResourceValue(orientation, 13) getResourceValue(layout, 13)
) )
.putFloat( .putFloat(
ButtonType.TRIGGER_L.toString() + "-X$orientation", "${Settings.PREF_BUTTON_L}-X$layout",
getResourceValue(orientation, 14) getResourceValue(layout, 14)
) )
.putFloat( .putFloat(
ButtonType.TRIGGER_L.toString() + "-Y$orientation", "${Settings.PREF_BUTTON_L}-Y$layout",
getResourceValue(orientation, 15) getResourceValue(layout, 15)
) )
.putFloat( .putFloat(
ButtonType.TRIGGER_R.toString() + "-X$orientation", "${Settings.PREF_BUTTON_R}-X$layout",
getResourceValue(orientation, 16) getResourceValue(layout, 16)
) )
.putFloat( .putFloat(
ButtonType.TRIGGER_R.toString() + "-Y$orientation", "${Settings.PREF_BUTTON_R}-Y$layout",
getResourceValue(orientation, 17) getResourceValue(layout, 17)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_PLUS.toString() + "-X$orientation", "${Settings.PREF_BUTTON_PLUS}-X$layout",
getResourceValue(orientation, 18) getResourceValue(layout, 18)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_PLUS.toString() + "-Y$orientation", "${Settings.PREF_BUTTON_PLUS}-Y$layout",
getResourceValue(orientation, 19) getResourceValue(layout, 19)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_MINUS.toString() + "-X$orientation", "${Settings.PREF_BUTTON_MINUS}-X$layout",
getResourceValue(orientation, 20) getResourceValue(layout, 20)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_MINUS.toString() + "-Y$orientation", "${Settings.PREF_BUTTON_MINUS}-Y$layout",
getResourceValue(orientation, 21) getResourceValue(layout, 21)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_HOME.toString() + "-X$orientation", "${Settings.PREF_BUTTON_HOME}-X$layout",
getResourceValue(orientation, 22) getResourceValue(layout, 22)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_HOME.toString() + "-Y$orientation", "${Settings.PREF_BUTTON_HOME}-Y$layout",
getResourceValue(orientation, 23) getResourceValue(layout, 23)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_CAPTURE.toString() + "-X$orientation", "${Settings.PREF_BUTTON_SCREENSHOT}-X$layout",
getResourceValue(orientation, 24) getResourceValue(layout, 24)
) )
.putFloat( .putFloat(
ButtonType.BUTTON_CAPTURE.toString() + "-Y$orientation", "${Settings.PREF_BUTTON_SCREENSHOT}-Y$layout",
getResourceValue(orientation, 25) getResourceValue(layout, 25)
) )
.putFloat( .putFloat(
ButtonType.STICK_R.toString() + "-X$orientation", "${Settings.PREF_STICK_R}-X$layout",
getResourceValue(orientation, 26) getResourceValue(layout, 26)
) )
.putFloat( .putFloat(
ButtonType.STICK_R.toString() + "-Y$orientation", "${Settings.PREF_STICK_R}-Y$layout",
getResourceValue(orientation, 27) getResourceValue(layout, 27)
) )
.putFloat( .putFloat(
ButtonType.STICK_L.toString() + "-X$orientation", "${Settings.PREF_STICK_L}-X$layout",
getResourceValue(orientation, 28) getResourceValue(layout, 28)
) )
.putFloat( .putFloat(
ButtonType.STICK_L.toString() + "-Y$orientation", "${Settings.PREF_STICK_L}-Y$layout",
getResourceValue(orientation, 29) getResourceValue(layout, 29)
)
.putFloat(
"${Settings.PREF_BUTTON_STICK_L}-X$layout",
getResourceValue(layout, 30)
)
.putFloat(
"${Settings.PREF_BUTTON_STICK_L}-Y$layout",
getResourceValue(layout, 31)
)
.putFloat(
"${Settings.PREF_BUTTON_STICK_R}-X$layout",
getResourceValue(layout, 32)
)
.putFloat(
"${Settings.PREF_BUTTON_STICK_R}-Y$layout",
getResourceValue(layout, 33)
) )
.apply() .apply()
} }
@ -812,12 +900,30 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
} }
companion object { 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 = private val preferences: SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
const val LANDSCAPE = "" const val LANDSCAPE = "_Landscape"
const val PORTRAIT = "_Portrait" const val PORTRAIT = "_Portrait"
const val FOLDABLE = "_Foldable" const val FOLDABLE = "_Foldable"
val overlayLayouts = listOf(
LANDSCAPE,
PORTRAIT,
FOLDABLE
)
/** /**
* Resizes a [Bitmap] by a given scale factor * 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 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 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 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. * @return An [InputOverlayDrawableButton] with the correct drawing bounds set.
*/ */
private fun initializeOverlayButton( private fun initializeOverlayButton(
@ -956,7 +1064,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
defaultResId: Int, defaultResId: Int,
pressedResId: Int, pressedResId: Int,
buttonId: Int, buttonId: Int,
orientation: String prefId: String,
layout: String
): InputOverlayDrawableButton { ): InputOverlayDrawableButton {
// Resources handle for fetching the initial Drawable resource. // Resources handle for fetching the initial Drawable resource.
val res = context.resources 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. // SharedPreference to retrieve the X and Y coordinates for the InputOverlayDrawableButton.
val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext) val sPrefs = PreferenceManager.getDefaultSharedPreferences(YuzuApplication.appContext)
// Decide scale based on button ID and user preference // Decide scale based on button preference ID and user preference
var scale: Float = when (buttonId) { var scale: Float = when (prefId) {
ButtonType.BUTTON_HOME, Settings.PREF_BUTTON_HOME,
ButtonType.BUTTON_CAPTURE, Settings.PREF_BUTTON_SCREENSHOT,
ButtonType.BUTTON_PLUS, Settings.PREF_BUTTON_PLUS,
ButtonType.BUTTON_MINUS -> 0.07f Settings.PREF_BUTTON_MINUS -> 0.07f
ButtonType.TRIGGER_L, Settings.PREF_BUTTON_L,
ButtonType.TRIGGER_R, Settings.PREF_BUTTON_R,
ButtonType.TRIGGER_ZL, Settings.PREF_BUTTON_ZL,
ButtonType.TRIGGER_ZR -> 0.26f Settings.PREF_BUTTON_ZR -> 0.26f
Settings.PREF_BUTTON_STICK_L,
Settings.PREF_BUTTON_STICK_R -> 0.155f
else -> 0.11f else -> 0.11f
} }
@ -984,8 +1096,13 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
// Initialize the InputOverlayDrawableButton. // Initialize the InputOverlayDrawableButton.
val defaultStateBitmap = getBitmap(context, defaultResId, scale) val defaultStateBitmap = getBitmap(context, defaultResId, scale)
val pressedStateBitmap = getBitmap(context, pressedResId, scale) val pressedStateBitmap = getBitmap(context, pressedResId, scale)
val overlayDrawable = val overlayDrawable = InputOverlayDrawableButton(
InputOverlayDrawableButton(res, defaultStateBitmap, pressedStateBitmap, buttonId) res,
defaultStateBitmap,
pressedStateBitmap,
buttonId,
prefId
)
// Get the minimum and maximum coordinates of the screen where the button can be placed. // Get the minimum and maximum coordinates of the screen where the button can be placed.
val min = windowSize.first 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. // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
// These were set in the input overlay configuration menu. // These were set in the input overlay configuration menu.
val xKey = "$buttonId-X$orientation" val xKey = "$prefId-X$layout"
val yKey = "$buttonId-Y$orientation" val yKey = "$prefId-Y$layout"
val drawableXPercent = sPrefs.getFloat(xKey, 0f) val drawableXPercent = sPrefs.getFloat(xKey, 0f)
val drawableYPercent = sPrefs.getFloat(yKey, 0f) val drawableYPercent = sPrefs.getFloat(yKey, 0f)
val drawableX = (drawableXPercent * max.x + min.x).toInt() 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 defaultResId The [Bitmap] resource ID of the default state.
* @param pressedOneDirectionResId The [Bitmap] resource ID of the pressed state in one direction. * @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. * @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( private fun initializeOverlayDpad(
context: Context, context: Context,
@ -1037,7 +1155,7 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
defaultResId: Int, defaultResId: Int,
pressedOneDirectionResId: Int, pressedOneDirectionResId: Int,
pressedTwoDirectionsResId: Int, pressedTwoDirectionsResId: Int,
orientation: String layout: String
): InputOverlayDrawableDpad { ): InputOverlayDrawableDpad {
// Resources handle for fetching the initial Drawable resource. // Resources handle for fetching the initial Drawable resource.
val res = context.resources 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. // The X and Y coordinates of the InputOverlayDrawableDpad on the InputOverlay.
// These were set in the input overlay configuration menu. // These were set in the input overlay configuration menu.
val drawableXPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-X$orientation", 0f) val drawableXPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-X$layout", 0f)
val drawableYPercent = sPrefs.getFloat("${ButtonType.DPAD_UP}-Y$orientation", 0f) val drawableYPercent = sPrefs.getFloat("${Settings.PREF_BUTTON_DPAD}-Y$layout", 0f)
val drawableX = (drawableXPercent * max.x + min.x).toInt() val drawableX = (drawableXPercent * max.x + min.x).toInt()
val drawableY = (drawableYPercent * max.y + min.y).toInt() val drawableY = (drawableYPercent * max.y + min.y).toInt()
val width = overlayDrawable.width 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 pressedResInner Resource ID for the pressed inner image of the joystick.
* @param joystick Identifier for which joystick this is. * @param joystick Identifier for which joystick this is.
* @param button Identifier for which joystick button 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( private fun initializeOverlayJoystick(
context: Context, context: Context,
@ -1117,7 +1237,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
pressedResInner: Int, pressedResInner: Int,
joystick: Int, joystick: Int,
button: Int, button: Int,
orientation: String prefId: String,
layout: String
): InputOverlayDrawableJoystick { ): InputOverlayDrawableJoystick {
// Resources handle for fetching the initial Drawable resource. // Resources handle for fetching the initial Drawable resource.
val res = context.resources 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. // The X and Y coordinates of the InputOverlayDrawableButton on the InputOverlay.
// These were set in the input overlay configuration menu. // These were set in the input overlay configuration menu.
val drawableXPercent = sPrefs.getFloat("$button-X$orientation", 0f) val drawableXPercent = sPrefs.getFloat("$prefId-X$layout", 0f)
val drawableYPercent = sPrefs.getFloat("$button-Y$orientation", 0f) val drawableYPercent = sPrefs.getFloat("$prefId-Y$layout", 0f)
val drawableX = (drawableXPercent * max.x + min.x).toInt() val drawableX = (drawableXPercent * max.x + min.x).toInt()
val drawableY = (drawableYPercent * max.y + min.y).toInt() val drawableY = (drawableYPercent * max.y + min.y).toInt()
val outerScale = 1.66f val outerScale = 1.66f
@ -1168,7 +1289,8 @@ class InputOverlay(context: Context, attrs: AttributeSet?) :
outerRect, outerRect,
innerRect, innerRect,
joystick, joystick,
button button,
prefId
) )
// Need to set the image's position // Need to set the image's position

View File

@ -24,7 +24,8 @@ class InputOverlayDrawableButton(
res: Resources, res: Resources,
defaultStateBitmap: Bitmap, defaultStateBitmap: Bitmap,
pressedStateBitmap: Bitmap, pressedStateBitmap: Bitmap,
val buttonId: Int val buttonId: Int,
val prefId: String
) { ) {
// The ID value what motion event is tracking // The ID value what motion event is tracking
var trackId: Int var trackId: Int

View File

@ -37,7 +37,8 @@ class InputOverlayDrawableJoystick(
rectOuter: Rect, rectOuter: Rect,
rectInner: Rect, rectInner: Rect,
val joystickId: Int, val joystickId: Int,
val buttonId: Int val buttonId: Int,
val prefId: String
) { ) {
// The ID value what motion event is tracking // The ID value what motion event is tracking
var trackId = -1 var trackId = -1

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -205,6 +205,8 @@
<item>@string/gamepad_d_pad</item> <item>@string/gamepad_d_pad</item>
<item>@string/gamepad_left_stick</item> <item>@string/gamepad_left_stick</item>
<item>@string/gamepad_right_stick</item> <item>@string/gamepad_right_stick</item>
<item>L3</item>
<item>R3</item>
<item>@string/gamepad_home</item> <item>@string/gamepad_home</item>
<item>@string/gamepad_screenshot</item> <item>@string/gamepad_screenshot</item>
</string-array> </string-array>

View File

@ -33,6 +33,10 @@
<integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer> <integer name="SWITCH_BUTTON_CAPTURE_Y">950</integer>
<integer name="SWITCH_BUTTON_DPAD_X">260</integer> <integer name="SWITCH_BUTTON_DPAD_X">260</integer>
<integer name="SWITCH_BUTTON_DPAD_Y">790</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 --> <!-- Default SWITCH portrait layout -->
<integer name="SWITCH_BUTTON_A_X_PORTRAIT">840</integer> <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_CAPTURE_Y_PORTRAIT">950</integer>
<integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer> <integer name="SWITCH_BUTTON_DPAD_X_PORTRAIT">240</integer>
<integer name="SWITCH_BUTTON_DPAD_Y_PORTRAIT">840</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 --> <!-- Default SWITCH foldable layout -->
<integer name="SWITCH_BUTTON_A_X_FOLDABLE">840</integer> <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_CAPTURE_Y_FOLDABLE">470</integer>
<integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer> <integer name="SWITCH_BUTTON_DPAD_X_FOLDABLE">240</integer>
<integer name="SWITCH_BUTTON_DPAD_Y_FOLDABLE">390</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> </resources>