diff options
Diffstat (limited to 'Source/Android/app/src/main/java/org')
3 files changed, 100 insertions, 27 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputDetector.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputDetector.kt new file mode 100644 index 0000000000..e180814a58 --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputDetector.kt @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.model + +import androidx.annotation.Keep + +/** + * Waits for the user to press inputs, and reports which inputs were pressed. + * + * The caller is responsible for forwarding input events from Android to ControllerInterface + * and then calling [update]. + */ +class InputDetector { + @Keep + private val pointer: Long + + constructor() { + pointer = createNew() + } + + @Keep + private constructor(pointer: Long) { + this.pointer = pointer + } + + external fun finalize() + + private external fun createNew(): Long + + /** + * Starts a detection session. + * + * @param defaultDevice The device to detect inputs from. + * @param allDevices Whether to also detect inputs from devices other than the specified one. + */ + external fun start(defaultDevice: String, allDevices: Boolean) + + /** + * Checks what inputs are currently pressed and updates internal state. + * + * During a detection session, this should be called after each call to + * [ControllerInterface.dispatchKeyEvent] and [ControllerInterface#dispatchGenericMotionEvent]. + */ + external fun update() + + /** + * Returns whether a detection session has finished. + * + * A detection session can end once the user has pressed and released an input or once a timeout + * has been reached. + */ + external fun isComplete(): Boolean + + /** + * Returns the result of a detection session. + * + * The result of each detection session is only returned once. If this method is called more + * than once without starting a new detection session, the second call onwards will return an + * empty string. + * + * @param defaultDevice The device to detect inputs from. Should normally be the same as the one + * passed to [start]. + * + * @return The input(s) pressed by the user in the form of an InputCommon expression, + * or an empty string if there were no inputs. + */ + external fun takeResults(defaultDevice: String): String +} diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.kt index 32775a7ebc..622f4f5515 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.kt @@ -2,23 +2,7 @@ package org.dolphinemu.dolphinemu.features.input.model -import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController - object MappingCommon { - /** - * Waits until the user presses one or more inputs or until a timeout, - * then returns the pressed inputs. - * - * When this is being called, a separate thread must be calling ControllerInterface's - * dispatchKeyEvent and dispatchGenericMotionEvent, otherwise no inputs will be registered. - * - * @param controller The device to detect inputs from. - * @param allDevices Whether to also detect inputs from devices other than the specified one. - * @return The input(s) pressed by the user in the form of an InputCommon expression, - * or an empty string if there were no inputs. - */ - external fun detectInput(controller: EmulatedController, allDevices: Boolean): String - external fun getExpressionForControl( control: String, device: String, diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.kt index ff4a278f4c..c3b471899e 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.kt @@ -3,12 +3,14 @@ package org.dolphinemu.dolphinemu.features.input.ui import android.app.Activity +import android.os.Looper +import android.os.Handler import android.view.InputDevice import android.view.KeyEvent import android.view.MotionEvent import androidx.appcompat.app.AlertDialog import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface -import org.dolphinemu.dolphinemu.features.input.model.MappingCommon +import org.dolphinemu.dolphinemu.features.input.model.InputDetector import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting /** @@ -24,21 +26,15 @@ class MotionAlertDialog( private val setting: InputMappingControlSetting, private val allDevices: Boolean ) : AlertDialog(activity) { + private val handler = Handler(Looper.getMainLooper()) + private val inputDetector: InputDetector = InputDetector() private var running = false override fun onStart() { super.onStart() - running = true - Thread { - val result = MappingCommon.detectInput(setting.controller, allDevices) - activity.runOnUiThread { - if (running) { - setting.value = result - dismiss() - } - } - }.start() + inputDetector.start(setting.controller.getDefaultDevice(), allDevices) + periodicUpdate() } override fun onStop() { @@ -48,9 +44,11 @@ class MotionAlertDialog( override fun dispatchKeyEvent(event: KeyEvent): Boolean { ControllerInterface.dispatchKeyEvent(event) + updateInputDetector() if (event.keyCode == KeyEvent.KEYCODE_BACK && event.isLongPress) { // Special case: Let the user cancel by long-pressing Back (intended for non-touch devices) setting.clearValue() + running = false dismiss() } return true @@ -63,6 +61,29 @@ class MotionAlertDialog( } ControllerInterface.dispatchGenericMotionEvent(event) + updateInputDetector() return true } + + private fun updateInputDetector() { + if (running) { + if (inputDetector.isComplete()) { + setting.value = inputDetector.takeResults(setting.controller.getDefaultDevice()) + running = false + + // Quirk: If this method has been called from onStart, calling dismiss directly + // doesn't seem to do anything. As a workaround, post a call to dismiss instead. + handler.post(this::dismiss) + } else { + inputDetector.update() + } + } + } + + private fun periodicUpdate() { + updateInputDetector() + if (running) { + handler.postDelayed(this::periodicUpdate, 10) + } + } } |
