From c9cf901d1cc8250472bbdba9f5eb86ee2c09d203 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20N=C3=BCsse?= Date: Wed, 10 Jul 2024 11:07:29 +0200 Subject: [PATCH 1/6] [WIP] implement button-quickassignment --- .../model/view/InputBindingSetting.kt | 18 ++ .../ui/ControllerAutomappingDialog.kt | 241 ++++++++++++++++++ .../features/settings/ui/SettingsAdapter.kt | 28 ++ .../settings/ui/SettingsFragmentPresenter.kt | 43 ++-- .../layout/dialog_controllerautomapping.xml | 73 ++++++ .../app/src/main/res/values/strings.xml | 3 + 6 files changed, 382 insertions(+), 24 deletions(-) create mode 100644 src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerAutomappingDialog.kt create mode 100644 src/android/app/src/main/res/layout/dialog_controllerautomapping.xml diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt index 8ecd60684..50f693b42 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt @@ -16,6 +16,7 @@ import org.citra.citra_emu.NativeLibrary import org.citra.citra_emu.R import org.citra.citra_emu.features.hotkeys.Hotkey import org.citra.citra_emu.features.settings.model.AbstractSetting +import org.citra.citra_emu.features.settings.model.AbstractStringSetting import org.citra.citra_emu.features.settings.model.Settings class InputBindingSetting( @@ -329,5 +330,22 @@ class InputBindingSetting( event.keyCode } } + + fun getInputObject(key: String, preferences: SharedPreferences): AbstractStringSetting { + return object : AbstractStringSetting { + override var string: String + get() = preferences.getString(key, "")!! + set(value) { + preferences.edit() + .putString(key, value) + .apply() + } + override val key = key + override val section = Settings.SECTION_CONTROLS + override val isRuntimeEditable = true + override val valueAsString = preferences.getString(key, "")!! + override val defaultValue = "" + } + } } } diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerAutomappingDialog.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerAutomappingDialog.kt new file mode 100644 index 000000000..be3fc4db0 --- /dev/null +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerAutomappingDialog.kt @@ -0,0 +1,241 @@ +package org.citra.citra_emu.features.settings.ui + +import android.app.AlertDialog +import android.content.Context +import android.content.SharedPreferences +import android.graphics.drawable.Drawable +import android.view.InputDevice +import android.view.KeyEvent +import android.view.LayoutInflater +import android.view.MotionEvent +import android.view.View +import androidx.core.content.ContextCompat +import org.citra.citra_emu.R +import org.citra.citra_emu.databinding.DialogControllerautomappingBinding +import org.citra.citra_emu.features.settings.model.view.InputBindingSetting +import kotlin.math.abs + + +class ControllerAutomappingDialog( + private var context: Context, + buttons: ArrayList>, + titles: ArrayList>, + private var preferences: SharedPreferences +) { + + private var index = 0 + val inflater = LayoutInflater.from(context) + val automappingBinding = DialogControllerautomappingBinding.inflate(inflater) + var dialog: AlertDialog? = null + + var allButtons = arrayListOf() + var allTitles = arrayListOf() + + init { + buttons.forEach {group -> + group.forEach {button -> + allButtons.add(button) + } + } + titles.forEach {group -> + group.forEach {title -> + allTitles.add(title) + } + } + + } + + fun show() { + val builder: AlertDialog.Builder = AlertDialog.Builder(context) + builder + .setView(automappingBinding.root) + .setTitle("Automapper") + .setPositiveButton("Next") {_,_ -> } + .setNegativeButton("Close") { dialog, which -> + dialog.dismiss() + } + + dialog = builder.create() + dialog?.show() + + dialog?.setOnKeyListener { _, _, event -> onKeyEvent(event) } + automappingBinding.root.setOnGenericMotionListener { _, event -> onMotionEvent(event) } + + // Prepare the first element + prepareUIforIndex(index) + + val nextButton = dialog?.getButton(AlertDialog.BUTTON_POSITIVE) + nextButton?.setOnClickListener { + // Skip to next: + prepareUIforIndex(index++) + } + + } + + private fun prepareUIforIndex(i: Int) { + if (allButtons.size-1 < i) { + dialog?.dismiss() + return + } + + if(index>0) { + automappingBinding.lastMappingIcon.visibility = View.VISIBLE + automappingBinding.lastMappingDescription.visibility = View.VISIBLE + } + + val currentButton = allButtons[i] + val currentTitleInt = allTitles[i] + + val button = InputBindingSetting.getInputObject(currentButton, preferences) + + var lastTitle = setting?.value ?: "" + if(lastTitle.isBlank()) { + lastTitle = context.getString(R.string.automapping_unassigned) + } + automappingBinding.lastMappingDescription.text = lastTitle + automappingBinding.lastMappingIcon.setImageDrawable(automappingBinding.currentMappingIcon.drawable) + setting = InputBindingSetting(button, currentTitleInt) + + automappingBinding.currentMappingTitle.text = calculateTitle() + automappingBinding.currentMappingDescription.text = setting?.value + automappingBinding.currentMappingIcon.setImageDrawable(getIcon()) + + + if (allButtons.size-1 < index) { + dialog?.getButton(AlertDialog.BUTTON_POSITIVE)?.text = + context.getString(R.string.automapping_dialog_finish) + dialog?.getButton(AlertDialog.BUTTON_NEGATIVE)?.visibility = View.GONE + } + + } + + private fun calculateTitle(): String { + + val inputTypeId = when { + setting!!.isCirclePad() -> R.string.controller_circlepad + setting!!.isCStick() -> R.string.controller_c + setting!!.isDPad() -> R.string.controller_dpad + setting!!.isTrigger() -> R.string.controller_trigger + else -> R.string.button + } + + val nameId = setting?.nameId?.let { context.getString(it) } + + return String.format( + context.getString(R.string.input_dialog_title), + context.getString(inputTypeId), + nameId + ) + } + + private fun getIcon(): Drawable? { + val id = when { + setting!!.isCirclePad() -> R.drawable.stick_main + setting!!.isCStick() -> R.drawable.stick_c + setting!!.isDPad() -> R.drawable.dpad + else -> { + val resourceTitle = context.resources.getResourceEntryName(setting!!.nameId) + if(resourceTitle.startsWith("direction")) { + R.drawable.dpad + } else { + context.resources.getIdentifier(resourceTitle, "drawable", context.packageName) + } + } + } + return ContextCompat.getDrawable(context, id) + } + + private val previousValues = ArrayList() + private var prevDeviceId = 0 + private var waitingForEvent = true + private var setting: InputBindingSetting? = null + + + private var debounceTimestamp = System.currentTimeMillis() + + + private fun onKeyEvent(event: KeyEvent): Boolean { + return when (event.action) { + KeyEvent.ACTION_UP -> { + if(System.currentTimeMillis()-debounceTimestamp < 500) { + return true + } + + debounceTimestamp = System.currentTimeMillis() + + index++ + setting?.onKeyInput(event) + prepareUIforIndex(index) + // Even if we ignore the key, we still consume it. Thus return true regardless. + true + } + + else -> false + } + } + + private fun onMotionEvent(event: MotionEvent): Boolean { + if (event.source and InputDevice.SOURCE_CLASS_JOYSTICK == 0) return false + if (event.action != MotionEvent.ACTION_MOVE) return false + + val input = event.device + + val motionRanges = input.motionRanges + + if (input.id != prevDeviceId) { + previousValues.clear() + } + prevDeviceId = input.id + val firstEvent = previousValues.isEmpty() + + var numMovedAxis = 0 + var axisMoveValue = 0.0f + var lastMovedRange: InputDevice.MotionRange? = null + var lastMovedDir = '?' + if (waitingForEvent) { + for (i in motionRanges.indices) { + val range = motionRanges[i] + val axis = range.axis + val origValue = event.getAxisValue(axis) + if (firstEvent) { + previousValues.add(origValue) + } else { + val previousValue = previousValues[i] + + // Only handle the axes that are not neutral (more than 0.5) + // but ignore any axis that has a constant value (e.g. always 1) + if (abs(origValue) > 0.5f && origValue != previousValue) { + // It is common to have multiple axes with the same physical input. For example, + // shoulder butters are provided as both AXIS_LTRIGGER and AXIS_BRAKE. + // To handle this, we ignore an axis motion that's the exact same as a motion + // we already saw. This way, we ignore axes with two names, but catch the case + // where a joystick is moved in two directions. + // ref: bottom of https://developer.android.com/training/game-controllers/controller-input.html + if (origValue != axisMoveValue) { + axisMoveValue = origValue + numMovedAxis++ + lastMovedRange = range + lastMovedDir = if (origValue < 0.0f) '-' else '+' + } + } else if (abs(origValue) < 0.25f && abs(previousValue) > 0.75f) { + // Special case for d-pads (axis value jumps between 0 and 1 without any values + // in between). Without this, the user would need to press the d-pad twice + // due to the first press being caught by the "if (firstEvent)" case further up. + numMovedAxis++ + lastMovedRange = range + lastMovedDir = if (previousValue < 0.0f) '-' else '+' + } + } + previousValues[i] = origValue + } + + // If only one axis moved, that's the winner. + if (numMovedAxis == 1) { + waitingForEvent = false + setting?.onMotionInput(input, lastMovedRange!!, lastMovedDir) + } + } + return true + } + +} \ No newline at end of file diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.kt index 71075b40c..f9b83fb4c 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.kt @@ -44,6 +44,7 @@ import org.citra.citra_emu.features.settings.model.AbstractStringSetting import org.citra.citra_emu.features.settings.model.FloatSetting import org.citra.citra_emu.features.settings.model.ScaledFloatSetting import org.citra.citra_emu.features.settings.model.AbstractShortSetting +import org.citra.citra_emu.features.settings.model.Settings import org.citra.citra_emu.features.settings.model.view.DateTimeSetting import org.citra.citra_emu.features.settings.model.view.InputBindingSetting import org.citra.citra_emu.features.settings.model.view.SettingsItem @@ -65,6 +66,7 @@ import org.citra.citra_emu.features.settings.ui.viewholder.SubmenuViewHolder import org.citra.citra_emu.features.settings.ui.viewholder.SwitchSettingViewHolder import org.citra.citra_emu.fragments.MessageDialogFragment import org.citra.citra_emu.fragments.MotionBottomSheetDialogFragment +import org.citra.citra_emu.utils.PermissionsHandler.preferences import org.citra.citra_emu.utils.SystemSaveGame import java.lang.IllegalStateException import java.lang.NumberFormatException @@ -514,6 +516,32 @@ class SettingsAdapter( .show() } + fun onClickAutoconfigureControls() { + + val buttons = arrayListOf( + Settings.buttonKeys, + Settings.circlePadKeys, + Settings.cStickKeys, + Settings.dPadAxisKeys, + Settings.dPadButtonKeys, + Settings.triggerKeys + ) + + val titles = arrayListOf( + Settings.buttonTitles, + Settings.axisTitles, + Settings.axisTitles, + Settings.axisTitles, + Settings.dPadTitles, + Settings.triggerTitles + ) + + Settings.buttonTitles + ControllerAutomappingDialog(context, buttons, titles, preferences).show() + + + } + fun closeDialog() { if (dialog != null) { if (clickedPosition != -1) { diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt index 31401567f..091643bf0 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -646,44 +646,56 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) private fun addControlsSettings(sl: ArrayList) { settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_controls)) sl.apply { + add(HeaderSetting(R.string.auto_configure)) + + add( + RunnableSetting( + R.string.auto_configure, + 0, + false, + 0, + { settingsAdapter.onClickAutoconfigureControls() } + ) + ) + add(HeaderSetting(R.string.generic_buttons)) Settings.buttonKeys.forEachIndexed { i: Int, key: String -> - val button = getInputObject(key) + val button = InputBindingSetting.getInputObject(key, preferences) add(InputBindingSetting(button, Settings.buttonTitles[i])) } add(HeaderSetting(R.string.controller_circlepad)) Settings.circlePadKeys.forEachIndexed { i: Int, key: String -> - val button = getInputObject(key) + val button = InputBindingSetting.getInputObject(key, preferences) add(InputBindingSetting(button, Settings.axisTitles[i])) } add(HeaderSetting(R.string.controller_c)) Settings.cStickKeys.forEachIndexed { i: Int, key: String -> - val button = getInputObject(key) + val button = InputBindingSetting.getInputObject(key, preferences) add(InputBindingSetting(button, Settings.axisTitles[i])) } add(HeaderSetting(R.string.controller_dpad_axis,R.string.controller_dpad_axis_description)) Settings.dPadAxisKeys.forEachIndexed { i: Int, key: String -> - val button = getInputObject(key) + val button = InputBindingSetting.getInputObject(key, preferences) add(InputBindingSetting(button, Settings.axisTitles[i])) } add(HeaderSetting(R.string.controller_dpad_button,R.string.controller_dpad_button_description)) Settings.dPadButtonKeys.forEachIndexed { i: Int, key: String -> - val button = getInputObject(key) + val button = InputBindingSetting.getInputObject(key, preferences) add(InputBindingSetting(button, Settings.dPadTitles[i])) } add(HeaderSetting(R.string.controller_triggers)) Settings.triggerKeys.forEachIndexed { i: Int, key: String -> - val button = getInputObject(key) + val button = InputBindingSetting.getInputObject(key, preferences) add(InputBindingSetting(button, Settings.triggerTitles[i])) } add(HeaderSetting(R.string.controller_hotkeys)) Settings.hotKeys.forEachIndexed { i: Int, key: String -> - val button = getInputObject(key) + val button = InputBindingSetting.getInputObject(key, preferences) add(InputBindingSetting(button, Settings.hotkeyTitles[i])) } add(HeaderSetting(R.string.miscellaneous)) @@ -699,23 +711,6 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) } } - private fun getInputObject(key: String): AbstractStringSetting { - return object : AbstractStringSetting { - override var string: String - get() = preferences.getString(key, "")!! - set(value) { - preferences.edit() - .putString(key, value) - .apply() - } - override val key = key - override val section = Settings.SECTION_CONTROLS - override val isRuntimeEditable = true - override val valueAsString = preferences.getString(key, "")!! - override val defaultValue = "" - } - } - private fun addGraphicsSettings(sl: ArrayList) { settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_graphics)) sl.apply { diff --git a/src/android/app/src/main/res/layout/dialog_controllerautomapping.xml b/src/android/app/src/main/res/layout/dialog_controllerautomapping.xml new file mode 100644 index 000000000..9dd0160ed --- /dev/null +++ b/src/android/app/src/main/res/layout/dialog_controllerautomapping.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 5495a2a23..9cc19ab80 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -169,6 +169,7 @@ Due to how slow Android\'s storage access framework is for accessing Azahar\'s files, downloading multiple versions of system files can dramatically slow down loading for applications, save states, and the Applications list. Only download the files that you require to avoid any issues with loading speeds. + Auto Configuration Buttons Button @@ -765,6 +766,8 @@ Enter Artic Base server address Delay game render thread Delay the game render thread when it submits data to the GPU. Helps with performance issues in the (very few) applications with dynamic framerates. + Finish + Unassigned Quicksave From 2656290107a5c60971d246d7b2e3d05d8d639960 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Wed, 10 Jul 2024 23:39:55 +0100 Subject: [PATCH 2/6] Code cleanup + Rebrand feature to "Quick Configure" --- ...ingDialog.kt => ControllerQuickConfigDialog.kt} | 14 +++++--------- .../features/settings/ui/SettingsAdapter.kt | 10 +++------- .../settings/ui/SettingsFragmentPresenter.kt | 6 ++---- ...ping.xml => dialog_controller_quick_config.xml} | 0 src/android/app/src/main/res/values/strings.xml | 6 +++--- 5 files changed, 13 insertions(+), 23 deletions(-) rename src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/{ControllerAutomappingDialog.kt => ControllerQuickConfigDialog.kt} (96%) rename src/android/app/src/main/res/layout/{dialog_controllerautomapping.xml => dialog_controller_quick_config.xml} (100%) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerAutomappingDialog.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt similarity index 96% rename from src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerAutomappingDialog.kt rename to src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt index be3fc4db0..bb7db4a36 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerAutomappingDialog.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt @@ -11,12 +11,12 @@ import android.view.MotionEvent import android.view.View import androidx.core.content.ContextCompat import org.citra.citra_emu.R -import org.citra.citra_emu.databinding.DialogControllerautomappingBinding +import org.citra.citra_emu.databinding.DialogControllerQuickConfigBinding import org.citra.citra_emu.features.settings.model.view.InputBindingSetting import kotlin.math.abs -class ControllerAutomappingDialog( +class ControllerQuickConfigDialog( private var context: Context, buttons: ArrayList>, titles: ArrayList>, @@ -25,7 +25,7 @@ class ControllerAutomappingDialog( private var index = 0 val inflater = LayoutInflater.from(context) - val automappingBinding = DialogControllerautomappingBinding.inflate(inflater) + val automappingBinding = DialogControllerQuickConfigBinding.inflate(inflater) var dialog: AlertDialog? = null var allButtons = arrayListOf() @@ -90,7 +90,7 @@ class ControllerAutomappingDialog( var lastTitle = setting?.value ?: "" if(lastTitle.isBlank()) { - lastTitle = context.getString(R.string.automapping_unassigned) + lastTitle = context.getString(R.string.controller_quick_config_unassigned) } automappingBinding.lastMappingDescription.text = lastTitle automappingBinding.lastMappingIcon.setImageDrawable(automappingBinding.currentMappingIcon.drawable) @@ -103,7 +103,7 @@ class ControllerAutomappingDialog( if (allButtons.size-1 < index) { dialog?.getButton(AlertDialog.BUTTON_POSITIVE)?.text = - context.getString(R.string.automapping_dialog_finish) + context.getString(R.string.controller_quick_config_finish) dialog?.getButton(AlertDialog.BUTTON_NEGATIVE)?.visibility = View.GONE } @@ -149,11 +149,8 @@ class ControllerAutomappingDialog( private var prevDeviceId = 0 private var waitingForEvent = true private var setting: InputBindingSetting? = null - - private var debounceTimestamp = System.currentTimeMillis() - private fun onKeyEvent(event: KeyEvent): Boolean { return when (event.action) { KeyEvent.ACTION_UP -> { @@ -179,7 +176,6 @@ class ControllerAutomappingDialog( if (event.action != MotionEvent.ACTION_MOVE) return false val input = event.device - val motionRanges = input.motionRanges if (input.id != prevDeviceId) { diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.kt index f9b83fb4c..dcb2ea210 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsAdapter.kt @@ -66,7 +66,7 @@ import org.citra.citra_emu.features.settings.ui.viewholder.SubmenuViewHolder import org.citra.citra_emu.features.settings.ui.viewholder.SwitchSettingViewHolder import org.citra.citra_emu.fragments.MessageDialogFragment import org.citra.citra_emu.fragments.MotionBottomSheetDialogFragment -import org.citra.citra_emu.utils.PermissionsHandler.preferences +import org.citra.citra_emu.utils.PermissionsHandler import org.citra.citra_emu.utils.SystemSaveGame import java.lang.IllegalStateException import java.lang.NumberFormatException @@ -516,8 +516,7 @@ class SettingsAdapter( .show() } - fun onClickAutoconfigureControls() { - + fun onClickControllerQuickConfig() { val buttons = arrayListOf( Settings.buttonKeys, Settings.circlePadKeys, @@ -536,10 +535,7 @@ class SettingsAdapter( Settings.triggerTitles ) - Settings.buttonTitles - ControllerAutomappingDialog(context, buttons, titles, preferences).show() - - + ControllerQuickConfigDialog(context, buttons, titles, PermissionsHandler.preferences).show() } fun closeDialog() { diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt index 091643bf0..1bbba31a3 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/SettingsFragmentPresenter.kt @@ -646,15 +646,13 @@ class SettingsFragmentPresenter(private val fragmentView: SettingsFragmentView) private fun addControlsSettings(sl: ArrayList) { settingsActivity.setToolbarTitle(settingsActivity.getString(R.string.preferences_controls)) sl.apply { - add(HeaderSetting(R.string.auto_configure)) - add( RunnableSetting( - R.string.auto_configure, + R.string.controller_quick_config, 0, false, 0, - { settingsAdapter.onClickAutoconfigureControls() } + { settingsAdapter.onClickControllerQuickConfig() } ) ) diff --git a/src/android/app/src/main/res/layout/dialog_controllerautomapping.xml b/src/android/app/src/main/res/layout/dialog_controller_quick_config.xml similarity index 100% rename from src/android/app/src/main/res/layout/dialog_controllerautomapping.xml rename to src/android/app/src/main/res/layout/dialog_controller_quick_config.xml diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index 9cc19ab80..d66d845a8 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -140,6 +140,9 @@ ZR This control must be bound to a gamepad analog stick or D-pad axis! This control must be bound to a gamepad button! + Quick Configure + Finish + Unassigned System Files @@ -169,7 +172,6 @@ Due to how slow Android\'s storage access framework is for accessing Azahar\'s files, downloading multiple versions of system files can dramatically slow down loading for applications, save states, and the Applications list. Only download the files that you require to avoid any issues with loading speeds. - Auto Configuration Buttons Button @@ -766,8 +768,6 @@ Enter Artic Base server address Delay game render thread Delay the game render thread when it submits data to the GPU. Helps with performance issues in the (very few) applications with dynamic framerates. - Finish - Unassigned Quicksave From d72284f49ac2c199fd5d79e87c832883e848ac95 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Thu, 11 Jul 2024 19:25:41 +0100 Subject: [PATCH 3/6] More code cleanup --- .../ui/ControllerQuickConfigDialog.kt | 46 ++++++++----------- 1 file changed, 18 insertions(+), 28 deletions(-) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt index bb7db4a36..c87973bdb 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt @@ -22,10 +22,9 @@ class ControllerQuickConfigDialog( titles: ArrayList>, private var preferences: SharedPreferences ) { - private var index = 0 val inflater = LayoutInflater.from(context) - val automappingBinding = DialogControllerQuickConfigBinding.inflate(inflater) + val quickConfigBinding = DialogControllerQuickConfigBinding.inflate(inflater) var dialog: AlertDialog? = null var allButtons = arrayListOf() @@ -42,14 +41,13 @@ class ControllerQuickConfigDialog( allTitles.add(title) } } - } fun show() { val builder: AlertDialog.Builder = AlertDialog.Builder(context) builder - .setView(automappingBinding.root) - .setTitle("Automapper") + .setView(quickConfigBinding.root) + .setTitle("Quick Configure") .setPositiveButton("Next") {_,_ -> } .setNegativeButton("Close") { dialog, which -> dialog.dismiss() @@ -59,7 +57,7 @@ class ControllerQuickConfigDialog( dialog?.show() dialog?.setOnKeyListener { _, _, event -> onKeyEvent(event) } - automappingBinding.root.setOnGenericMotionListener { _, event -> onMotionEvent(event) } + quickConfigBinding.root.setOnGenericMotionListener { _, event -> onMotionEvent(event) } // Prepare the first element prepareUIforIndex(index) @@ -69,7 +67,6 @@ class ControllerQuickConfigDialog( // Skip to next: prepareUIforIndex(index++) } - } private fun prepareUIforIndex(i: Int) { @@ -78,9 +75,9 @@ class ControllerQuickConfigDialog( return } - if(index>0) { - automappingBinding.lastMappingIcon.visibility = View.VISIBLE - automappingBinding.lastMappingDescription.visibility = View.VISIBLE + if (index>0) { + quickConfigBinding.lastMappingIcon.visibility = View.VISIBLE + quickConfigBinding.lastMappingDescription.visibility = View.VISIBLE } val currentButton = allButtons[i] @@ -89,28 +86,25 @@ class ControllerQuickConfigDialog( val button = InputBindingSetting.getInputObject(currentButton, preferences) var lastTitle = setting?.value ?: "" - if(lastTitle.isBlank()) { + if (lastTitle.isBlank()) { lastTitle = context.getString(R.string.controller_quick_config_unassigned) } - automappingBinding.lastMappingDescription.text = lastTitle - automappingBinding.lastMappingIcon.setImageDrawable(automappingBinding.currentMappingIcon.drawable) + quickConfigBinding.lastMappingDescription.text = lastTitle + quickConfigBinding.lastMappingIcon.setImageDrawable(quickConfigBinding.currentMappingIcon.drawable) + setting = InputBindingSetting(button, currentTitleInt) - - automappingBinding.currentMappingTitle.text = calculateTitle() - automappingBinding.currentMappingDescription.text = setting?.value - automappingBinding.currentMappingIcon.setImageDrawable(getIcon()) - + quickConfigBinding.currentMappingTitle.text = calculateTitle() + quickConfigBinding.currentMappingDescription.text = setting?.value + quickConfigBinding.currentMappingIcon.setImageDrawable(getIcon()) if (allButtons.size-1 < index) { dialog?.getButton(AlertDialog.BUTTON_POSITIVE)?.text = context.getString(R.string.controller_quick_config_finish) dialog?.getButton(AlertDialog.BUTTON_NEGATIVE)?.visibility = View.GONE } - } private fun calculateTitle(): String { - val inputTypeId = when { setting!!.isCirclePad() -> R.string.controller_circlepad setting!!.isCStick() -> R.string.controller_c @@ -135,7 +129,7 @@ class ControllerQuickConfigDialog( setting!!.isDPad() -> R.drawable.dpad else -> { val resourceTitle = context.resources.getResourceEntryName(setting!!.nameId) - if(resourceTitle.startsWith("direction")) { + if (resourceTitle.startsWith("direction")) { R.drawable.dpad } else { context.resources.getIdentifier(resourceTitle, "drawable", context.packageName) @@ -154,26 +148,24 @@ class ControllerQuickConfigDialog( private fun onKeyEvent(event: KeyEvent): Boolean { return when (event.action) { KeyEvent.ACTION_UP -> { - if(System.currentTimeMillis()-debounceTimestamp < 500) { + if (System.currentTimeMillis()-debounceTimestamp < 500) { return true } debounceTimestamp = System.currentTimeMillis() - index++ setting?.onKeyInput(event) prepareUIforIndex(index) // Even if we ignore the key, we still consume it. Thus return true regardless. true } - else -> false } } private fun onMotionEvent(event: MotionEvent): Boolean { - if (event.source and InputDevice.SOURCE_CLASS_JOYSTICK == 0) return false - if (event.action != MotionEvent.ACTION_MOVE) return false + if ((event.source and InputDevice.SOURCE_CLASS_JOYSTICK == 0) || + event.action != MotionEvent.ACTION_MOVE) return false val input = event.device val motionRanges = input.motionRanges @@ -224,7 +216,6 @@ class ControllerQuickConfigDialog( } previousValues[i] = origValue } - // If only one axis moved, that's the winner. if (numMovedAxis == 1) { waitingForEvent = false @@ -233,5 +224,4 @@ class ControllerQuickConfigDialog( } return true } - } \ No newline at end of file From 159b148dc3894edc95eb519b5a02eb70da16bb47 Mon Sep 17 00:00:00 2001 From: OpenSauce04 Date: Thu, 11 Jul 2024 19:42:20 +0100 Subject: [PATCH 4/6] Reduce debounce timer + Remove magic number --- .../features/settings/ui/ControllerQuickConfigDialog.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt index c87973bdb..a26b8999a 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt @@ -148,7 +148,7 @@ class ControllerQuickConfigDialog( private fun onKeyEvent(event: KeyEvent): Boolean { return when (event.action) { KeyEvent.ACTION_UP -> { - if (System.currentTimeMillis()-debounceTimestamp < 500) { + if (System.currentTimeMillis()-debounceTimestamp < DEBOUNCE_TIMER) { return true } @@ -224,4 +224,8 @@ class ControllerQuickConfigDialog( } return true } + + companion object { + private const val DEBOUNCE_TIMER = 100 + } } \ No newline at end of file From dbfefa615dedbdf848bd0df0225879f125ff8c95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20N=C3=BCsse?= Date: Thu, 11 Jul 2024 22:22:21 +0200 Subject: [PATCH 5/6] extract strings --- .../features/settings/ui/ControllerQuickConfigDialog.kt | 6 +++--- src/android/app/src/main/res/values/strings.xml | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt index a26b8999a..c8fcea00a 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt @@ -47,9 +47,9 @@ class ControllerQuickConfigDialog( val builder: AlertDialog.Builder = AlertDialog.Builder(context) builder .setView(quickConfigBinding.root) - .setTitle("Quick Configure") - .setPositiveButton("Next") {_,_ -> } - .setNegativeButton("Close") { dialog, which -> + .setTitle(context.getString(R.string.controller_quick_config)) + .setPositiveButton(context.getString(R.string.controller_quick_config_next)) {_,_ -> } + .setNegativeButton(context.getString(R.string.controller_quick_config_close)) { dialog, which -> dialog.dismiss() } diff --git a/src/android/app/src/main/res/values/strings.xml b/src/android/app/src/main/res/values/strings.xml index d66d845a8..51650bcfe 100644 --- a/src/android/app/src/main/res/values/strings.xml +++ b/src/android/app/src/main/res/values/strings.xml @@ -143,6 +143,8 @@ Quick Configure Finish Unassigned + Next + Close System Files From c7f6d2d679ff600942b388ddebacd8fdd2b51bcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20N=C3=BCsse?= Date: Fri, 12 Jul 2024 21:13:19 +0200 Subject: [PATCH 6/6] apply settings only on finish --- .../model/view/InputBindingSetting.kt | 25 +++++++++++++++++++ .../ui/ControllerQuickConfigDialog.kt | 8 +++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt index 50f693b42..297afe861 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/model/view/InputBindingSetting.kt @@ -35,6 +35,8 @@ class InputBindingSetting( .apply() } + private var key: String = "" + /** * Returns true if this key is for the 3DS Circle Pad */ @@ -230,6 +232,29 @@ class InputBindingSetting( value = uiString } + /** + * Stores the provided key input setting as an Android preference. + * Only gets applied when apply(); is called. + * + * @param keyEvent KeyEvent of this key press. + */ + fun onKeyInputDeferred(keyEvent: KeyEvent) { + if (!isButtonMappingSupported()) { + Toast.makeText(context, R.string.input_message_analog_only, Toast.LENGTH_LONG).show() + return + } + key = getInputButtonKey(keyEvent.keyCode) + val uiString = "${keyEvent.device.name}: Button ${keyEvent.keyCode}" + value = uiString + } + + /** + * Stores the provided key input setting as an Android preference. + */ + fun applyMapping() { + writeButtonMapping(key) + } + /** * Saves the provided motion input setting as an Android preference. * diff --git a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt index c8fcea00a..be8c586ce 100644 --- a/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt +++ b/src/android/app/src/main/java/org/citra/citra_emu/features/settings/ui/ControllerQuickConfigDialog.kt @@ -71,6 +71,7 @@ class ControllerQuickConfigDialog( private fun prepareUIforIndex(i: Int) { if (allButtons.size-1 < i) { + settingsList.forEach { it.applyMapping() } dialog?.dismiss() return } @@ -145,6 +146,8 @@ class ControllerQuickConfigDialog( private var setting: InputBindingSetting? = null private var debounceTimestamp = System.currentTimeMillis() + private var settingsList = arrayListOf() + private fun onKeyEvent(event: KeyEvent): Boolean { return when (event.action) { KeyEvent.ACTION_UP -> { @@ -154,7 +157,10 @@ class ControllerQuickConfigDialog( debounceTimestamp = System.currentTimeMillis() index++ - setting?.onKeyInput(event) + setting?.let { + it.onKeyInputDeferred(event) + settingsList.add(it) + } prepareUIforIndex(index) // Even if we ignore the key, we still consume it. Thus return true regardless. true