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 @Keep
inner class Control private constructor(private val pointer: Long) { inner class Control private constructor(private val pointer: Long) {
external fun getName(): String external fun getName(): String
external fun getState(): Double
} }
protected external fun finalize() protected external fun finalize()

View File

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

View File

@ -12,6 +12,7 @@ import java.util.function.Consumer
class AdvancedMappingControlAdapter( class AdvancedMappingControlAdapter(
private val parentLifecycle: Lifecycle, private val parentLifecycle: Lifecycle,
private val isInput: Boolean,
private val onClickCallback: Consumer<String> private val onClickCallback: Consumer<String>
) : RecyclerView.Adapter<AdvancedMappingControlViewHolder>() { ) : RecyclerView.Adapter<AdvancedMappingControlViewHolder>() {
@ -23,7 +24,7 @@ class AdvancedMappingControlAdapter(
): AdvancedMappingControlViewHolder { ): AdvancedMappingControlViewHolder {
val inflater = LayoutInflater.from(parent.context) val inflater = LayoutInflater.from(parent.context)
val binding = ListItemAdvancedMappingControlBinding.inflate(inflater) val binding = ListItemAdvancedMappingControlBinding.inflate(inflater)
return AdvancedMappingControlViewHolder(binding, parentLifecycle, onClickCallback) return AdvancedMappingControlViewHolder(binding, parentLifecycle, isInput, onClickCallback)
} }
override fun onBindViewHolder(holder: AdvancedMappingControlViewHolder, position: Int) = 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 androidx.lifecycle.Lifecycle
import org.dolphinemu.dolphinemu.databinding.ListItemAdvancedMappingControlBinding 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.features.input.model.CoreDevice
import org.dolphinemu.dolphinemu.utils.LifecycleViewHolder import org.dolphinemu.dolphinemu.utils.LifecycleViewHolder
import org.dolphinemu.dolphinemu.utils.Log
import java.util.function.Consumer import java.util.function.Consumer
class AdvancedMappingControlViewHolder( class AdvancedMappingControlViewHolder(
private val binding: ListItemAdvancedMappingControlBinding, private val binding: ListItemAdvancedMappingControlBinding,
parentLifecycle: Lifecycle, private val parentLifecycle: Lifecycle,
private val isInput: Boolean,
onClickCallback: Consumer<String> onClickCallback: Consumer<String>
) : LifecycleViewHolder(binding.root, parentLifecycle) { ) : LifecycleViewHolder(binding.root, parentLifecycle) {
@ -18,10 +21,28 @@ class AdvancedMappingControlViewHolder(
init { init {
binding.root.setOnClickListener { onClickCallback.accept(control.getName()) } binding.root.setOnClickListener { onClickCallback.accept(control.getName()) }
if (isInput) {
ControllerInterface.inputStateChanged.observe(this) {
updateInputValue()
}
}
} }
fun bind(control: CoreDevice.Control) { fun bind(control: CoreDevice.Control) {
this.control = control this.control = control
binding.textName.text = control.getName() 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 package org.dolphinemu.dolphinemu.features.input.ui
import android.content.Context import android.content.Context
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View import android.view.View
import android.widget.AdapterView import android.widget.AdapterView
import android.widget.AdapterView.OnItemClickListener import android.widget.AdapterView.OnItemClickListener
@ -38,7 +40,7 @@ class AdvancedMappingDialog(
ArrayAdapter(context, android.R.layout.simple_spinner_dropdown_item, devices) ArrayAdapter(context, android.R.layout.simple_spinner_dropdown_item, devices)
binding.dropdownDevice.setAdapter(deviceAdapter) binding.dropdownDevice.setAdapter(deviceAdapter)
controlAdapter = AdvancedMappingControlAdapter(lifecycle) { controlAdapter = AdvancedMappingControlAdapter(lifecycle, controlReference.isInput()) {
control: String -> onControlClicked(control) control: String -> onControlClicked(control)
} }
binding.listControl.adapter = controlAdapter binding.listControl.adapter = controlAdapter
@ -60,6 +62,16 @@ class AdvancedMappingDialog(
override fun onItemClick(adapterView: AdapterView<*>?, view: View, position: Int, id: Long) = override fun onItemClick(adapterView: AdapterView<*>?, view: View, position: Int, id: Long) =
setSelectedDevice(devices[position]) 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) { private fun setSelectedDevice(deviceString: String) {
selectedDevice = deviceString selectedDevice = deviceString

View File

@ -3,11 +3,14 @@
package org.dolphinemu.dolphinemu.features.input.ui.viewholder package org.dolphinemu.dolphinemu.features.input.ui.viewholder
import android.view.View import android.view.View
import androidx.lifecycle.Lifecycle
import org.dolphinemu.dolphinemu.databinding.ListItemMappingBinding 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.input.model.view.InputMappingControlSetting
import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter import org.dolphinemu.dolphinemu.features.settings.ui.SettingsAdapter
import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder import org.dolphinemu.dolphinemu.features.settings.ui.viewholder.SettingViewHolder
import org.dolphinemu.dolphinemu.utils.Log
class InputMappingControlSettingViewHolder( class InputMappingControlSettingViewHolder(
private val binding: ListItemMappingBinding, private val binding: ListItemMappingBinding,
@ -15,6 +18,12 @@ class InputMappingControlSettingViewHolder(
) : SettingViewHolder(binding.getRoot(), adapter) { ) : SettingViewHolder(binding.getRoot(), adapter) {
lateinit var setting: InputMappingControlSetting lateinit var setting: InputMappingControlSetting
init {
ControllerInterface.inputStateChanged.observe(this) {
updateInputValue()
}
}
override val item: SettingsItem override val item: SettingsItem
get() = setting get() = setting
@ -26,6 +35,7 @@ class InputMappingControlSettingViewHolder(
binding.buttonAdvancedSettings.setOnClickListener { clicked: View -> onLongClick(clicked) } binding.buttonAdvancedSettings.setOnClickListener { clicked: View -> onLongClick(clicked) }
setStyle(binding.textSettingName, setting) setStyle(binding.textSettingName, setting)
updateInputValue()
} }
override fun onClick(clicked: View) { override fun onClick(clicked: View) {
@ -52,4 +62,16 @@ class InputMappingControlSettingViewHolder(
return true 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.content.Intent
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.KeyEvent
import android.view.Menu import android.view.Menu
import android.view.MotionEvent
import android.view.View import android.view.View
import android.widget.Toast import android.widget.Toast
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
@ -22,11 +24,11 @@ import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.DialogFragment import androidx.fragment.app.DialogFragment
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.google.android.material.appbar.CollapsingToolbarLayout import com.google.android.material.appbar.CollapsingToolbarLayout
import com.google.android.material.color.MaterialColors
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
import org.dolphinemu.dolphinemu.NativeLibrary import org.dolphinemu.dolphinemu.NativeLibrary
import org.dolphinemu.dolphinemu.R import org.dolphinemu.dolphinemu.R
import org.dolphinemu.dolphinemu.databinding.ActivitySettingsBinding 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.model.Settings
import org.dolphinemu.dolphinemu.features.settings.ui.SettingsFragment.Companion.newInstance import org.dolphinemu.dolphinemu.features.settings.ui.SettingsFragment.Companion.newInstance
import org.dolphinemu.dolphinemu.ui.main.MainPresenter 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 { private fun canonicalizeIfPossible(uri: Uri): Uri {
val canonicalizedUri = contentResolver.canonicalize(uri) val canonicalizedUri = contentResolver.canonicalize(uri)
return canonicalizedUri ?: 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()); 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 JNIEXPORT void JNICALL
Java_org_dolphinemu_dolphinemu_features_input_model_CoreDevice_finalize(JNIEnv* env, jobject obj) Java_org_dolphinemu_dolphinemu_features_input_model_CoreDevice_finalize(JNIEnv* env, jobject obj)
{ {