Android: Make input mapping view holders observe input state changes

This commit is contained in:
JosJuice
2025-05-13 19:43:40 +02:00
parent 22fd0472ed
commit cf7b141eb9
8 changed files with 84 additions and 4 deletions

View File

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

View File

@ -18,6 +18,9 @@ class InputMappingControlSetting(var control: Control, val controller: EmulatedC
controller.updateSingleControlReference(controlReference)
}
val state: Double
get() = controlReference.getState()
fun clearValue() {
value = ""
}

View File

@ -12,6 +12,7 @@ import java.util.function.Consumer
class AdvancedMappingControlAdapter(
private val parentLifecycle: Lifecycle,
private val isInput: Boolean,
private val onClickCallback: Consumer<String>
) : RecyclerView.Adapter<AdvancedMappingControlViewHolder>() {
@ -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) =

View File

@ -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<String>
) : 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())
}
}

View File

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

View File

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

View File

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

View File

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