summaryrefslogtreecommitdiff
path: root/Source/Android/app/src
diff options
context:
space:
mode:
authorJMC47 <JMC4789@gmail.com>2025-03-28 18:25:04 -0400
committerGitHub <noreply@github.com>2025-03-28 18:25:04 -0400
commit7d794897c4043b9ee8b278340cfeff02923512f4 (patch)
tree05205ca6145a2450d3b3cc5e14526f83f8a379da /Source/Android/app/src
parente0032b3e2c821f11c1c2964d4901399e88429898 (diff)
parent9e9faf3be1582ac5cdcaa46dc60287b10c921aa2 (diff)
Merge pull request #13434 from JosJuice/android-non-blocking-input-detection
Android: Don't use separate thread for MotionAlertDialog
Diffstat (limited to 'Source/Android/app/src')
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputDetector.kt68
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.kt16
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.kt51
-rw-r--r--Source/Android/app/src/main/res/values/strings.xml3
4 files changed, 110 insertions, 28 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..12f3ffb138 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,16 @@
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 com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.dolphinemu.dolphinemu.R
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 +28,21 @@ 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()
+ if (running == false) {
+ MaterialAlertDialogBuilder(activity)
+ .setMessage(R.string.input_binding_disconnected_device)
+ .setPositiveButton(R.string.ok, null)
+ .show()
+ }
}
override fun onStop() {
@@ -48,9 +52,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 +69,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)
+ }
+ }
}
diff --git a/Source/Android/app/src/main/res/values/strings.xml b/Source/Android/app/src/main/res/values/strings.xml
index e10a781114..c0b33237af 100644
--- a/Source/Android/app/src/main/res/values/strings.xml
+++ b/Source/Android/app/src/main/res/values/strings.xml
@@ -54,7 +54,8 @@
<string name="input_binding">Input Binding</string>
<string name="input_binding_description">Press or move an input to bind it to %1$s.</string>
- <string name="input_binding_no_device">You need to select a device first!</string>
+ <string name="input_binding_no_device">You need to select a device first.</string>
+ <string name="input_binding_disconnected_device">The selected device is disconnected.\n\nPlease reconnect the device or select a different device.</string>
<string name="input_configure_input">Configure Input</string>
<string name="input_configure_output">Configure Output</string>
<string name="input_expression">Expression</string>