From cf7b141eb9d2651b20515f3d0ef5280ee1e97b6c Mon Sep 17 00:00:00 2001 From: JosJuice Date: Tue, 13 May 2025 19:43:40 +0200 Subject: [PATCH] Android: Make input mapping view holders observe input state changes --- .../features/input/model/CoreDevice.kt | 2 ++ .../model/view/InputMappingControlSetting.kt | 3 +++ .../input/ui/AdvancedMappingControlAdapter.kt | 3 ++- .../ui/AdvancedMappingControlViewHolder.kt | 23 ++++++++++++++++++- .../input/ui/AdvancedMappingDialog.kt | 14 ++++++++++- .../InputMappingControlSettingViewHolder.kt | 22 ++++++++++++++++++ .../features/settings/ui/SettingsActivity.kt | 14 ++++++++++- Source/Android/jni/Input/CoreDevice.cpp | 7 ++++++ 8 files changed, 84 insertions(+), 4 deletions(-) diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/CoreDevice.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/CoreDevice.kt index 1483422c50..53f8864123 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/CoreDevice.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/CoreDevice.kt @@ -18,6 +18,8 @@ class CoreDevice private constructor(private val pointer: Long) { @Keep inner class Control private constructor(private val pointer: Long) { external fun getName(): String + + external fun getState(): Double } protected external fun finalize() diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.kt index 26a86a9411..4220e9fcab 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.kt @@ -18,6 +18,9 @@ class InputMappingControlSetting(var control: Control, val controller: EmulatedC controller.updateSingleControlReference(controlReference) } + val state: Double + get() = controlReference.getState() + fun clearValue() { value = "" } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlAdapter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlAdapter.kt index 3982c62220..8919f22658 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlAdapter.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlAdapter.kt @@ -12,6 +12,7 @@ import java.util.function.Consumer class AdvancedMappingControlAdapter( private val parentLifecycle: Lifecycle, + private val isInput: Boolean, private val onClickCallback: Consumer ) : RecyclerView.Adapter() { @@ -23,7 +24,7 @@ class AdvancedMappingControlAdapter( ): AdvancedMappingControlViewHolder { val inflater = LayoutInflater.from(parent.context) val binding = ListItemAdvancedMappingControlBinding.inflate(inflater) - return AdvancedMappingControlViewHolder(binding, parentLifecycle, onClickCallback) + return AdvancedMappingControlViewHolder(binding, parentLifecycle, isInput, onClickCallback) } override fun onBindViewHolder(holder: AdvancedMappingControlViewHolder, position: Int) = diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlViewHolder.kt index 8c05115e85..80771a6f4b 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlViewHolder.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlViewHolder.kt @@ -4,13 +4,16 @@ package org.dolphinemu.dolphinemu.features.input.ui import androidx.lifecycle.Lifecycle import org.dolphinemu.dolphinemu.databinding.ListItemAdvancedMappingControlBinding +import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface import org.dolphinemu.dolphinemu.features.input.model.CoreDevice import org.dolphinemu.dolphinemu.utils.LifecycleViewHolder +import org.dolphinemu.dolphinemu.utils.Log import java.util.function.Consumer class AdvancedMappingControlViewHolder( private val binding: ListItemAdvancedMappingControlBinding, - parentLifecycle: Lifecycle, + private val parentLifecycle: Lifecycle, + private val isInput: Boolean, onClickCallback: Consumer ) : LifecycleViewHolder(binding.root, parentLifecycle) { @@ -18,10 +21,28 @@ class AdvancedMappingControlViewHolder( init { binding.root.setOnClickListener { onClickCallback.accept(control.getName()) } + if (isInput) { + ControllerInterface.inputStateChanged.observe(this) { + updateInputValue() + } + } } fun bind(control: CoreDevice.Control) { this.control = control binding.textName.text = control.getName() + if (isInput) { + updateInputValue() + } + } + + private fun updateInputValue() { + if (parentLifecycle.currentState == Lifecycle.State.DESTROYED) { + throw IllegalStateException("AdvancedMappingControlViewHolder leak") + } + + // TODO + Log.info("AdvancedMappingControlViewHolder: Value of " + control.getName() + " is " + + control.getState()) } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingDialog.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingDialog.kt index 570a5a4cae..2117ed3102 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingDialog.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingDialog.kt @@ -3,6 +3,8 @@ package org.dolphinemu.dolphinemu.features.input.ui import android.content.Context +import android.view.KeyEvent +import android.view.MotionEvent import android.view.View import android.widget.AdapterView import android.widget.AdapterView.OnItemClickListener @@ -38,7 +40,7 @@ class AdvancedMappingDialog( ArrayAdapter(context, android.R.layout.simple_spinner_dropdown_item, devices) binding.dropdownDevice.setAdapter(deviceAdapter) - controlAdapter = AdvancedMappingControlAdapter(lifecycle) { + controlAdapter = AdvancedMappingControlAdapter(lifecycle, controlReference.isInput()) { control: String -> onControlClicked(control) } binding.listControl.adapter = controlAdapter @@ -60,6 +62,16 @@ class AdvancedMappingDialog( override fun onItemClick(adapterView: AdapterView<*>?, view: View, position: Int, id: Long) = setSelectedDevice(devices[position]) + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + ControllerInterface.dispatchKeyEvent(event) + return super.dispatchKeyEvent(event) + } + + override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { + ControllerInterface.dispatchGenericMotionEvent(event) + return super.dispatchGenericMotionEvent(event) + } + private fun setSelectedDevice(deviceString: String) { selectedDevice = deviceString diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.kt index 5bbcbf07c4..b6b30da53d 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.kt @@ -3,11 +3,14 @@ package org.dolphinemu.dolphinemu.features.input.ui.viewholder import android.view.View +import androidx.lifecycle.Lifecycle import org.dolphinemu.dolphinemu.databinding.ListItemMappingBinding +import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder +import org.dolphinemu.dolphinemu.utils.Log class InputMappingControlSettingViewHolder( private val binding: ListItemMappingBinding, @@ -15,6 +18,12 @@ class InputMappingControlSettingViewHolder( ) : SettingViewHolder(binding.getRoot(), adapter) { lateinit var setting: InputMappingControlSetting + init { + ControllerInterface.inputStateChanged.observe(this) { + updateInputValue() + } + } + override val item: SettingsItem get() = setting @@ -26,6 +35,7 @@ class InputMappingControlSettingViewHolder( binding.buttonAdvancedSettings.setOnClickListener { clicked: View -> onLongClick(clicked) } setStyle(binding.textSettingName, setting) + updateInputValue() } override fun onClick(clicked: View) { @@ -52,4 +62,16 @@ class InputMappingControlSettingViewHolder( return true } + + private fun updateInputValue() { + if (adapter.getFragmentLifecycle().currentState == Lifecycle.State.DESTROYED) { + throw IllegalStateException("InputMappingControlSettingViewHolder leak") + } + + if (setting.isInput) { + // TODO + Log.info("InputMappingControlSettingViewHolder: Value of " + setting.name + " is " + + setting.state) + } + } } diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt index 63a3992487..e5db2f63d2 100644 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsActivity.kt @@ -11,7 +11,9 @@ import android.content.DialogInterface import android.content.Intent import android.net.Uri import android.os.Bundle +import android.view.KeyEvent import android.view.Menu +import android.view.MotionEvent import android.view.View import android.widget.Toast import androidx.activity.enableEdgeToEdge @@ -22,11 +24,11 @@ import androidx.core.view.WindowInsetsCompat import androidx.fragment.app.DialogFragment import androidx.lifecycle.ViewModelProvider import com.google.android.material.appbar.CollapsingToolbarLayout -import com.google.android.material.color.MaterialColors import com.google.android.material.dialog.MaterialAlertDialogBuilder import org.dolphinemu.dolphinemu.NativeLibrary import org.dolphinemu.dolphinemu.R import org.dolphinemu.dolphinemu.databinding.ActivitySettingsBinding +import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface import org.dolphinemu.dolphinemu.features.settings.model.Settings import org.dolphinemu.dolphinemu.features.settings.ui.SettingsFragment.Companion.newInstance import org.dolphinemu.dolphinemu.ui.main.MainPresenter @@ -197,6 +199,16 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { } } + override fun dispatchKeyEvent(event: KeyEvent): Boolean { + ControllerInterface.dispatchKeyEvent(event) + return super.dispatchKeyEvent(event) + } + + override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean { + ControllerInterface.dispatchGenericMotionEvent(event) + return super.dispatchGenericMotionEvent(event) + } + private fun canonicalizeIfPossible(uri: Uri): Uri { val canonicalizedUri = contentResolver.canonicalize(uri) return canonicalizedUri ?: uri diff --git a/Source/Android/jni/Input/CoreDevice.cpp b/Source/Android/jni/Input/CoreDevice.cpp index 4fd4758ebb..8d7984b5e3 100644 --- a/Source/Android/jni/Input/CoreDevice.cpp +++ b/Source/Android/jni/Input/CoreDevice.cpp @@ -64,6 +64,13 @@ Java_org_dolphinemu_dolphinemu_features_input_model_CoreDevice_00024Control_getN return ToJString(env, GetControlPointer(env, obj)->GetName()); } +JNIEXPORT jdouble JNICALL +Java_org_dolphinemu_dolphinemu_features_input_model_CoreDevice_00024Control_getState(JNIEnv* env, + jobject obj) +{ + return env, GetControlPointer(env, obj)->ToInput()->GetState(); +} + JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_features_input_model_CoreDevice_finalize(JNIEnv* env, jobject obj) {