From 5171290bdb3483cdf4c878b16940844abaa821ef Mon Sep 17 00:00:00 2001 From: Charles Lombardo Date: Sat, 10 Jun 2023 05:17:23 -0400 Subject: [PATCH] Android: Convert AdvancedMappingDialog to Kotlin --- .../input/ui/AdvancedMappingDialog.java | 151 ------------------ .../input/ui/AdvancedMappingDialog.kt | 122 ++++++++++++++ 2 files changed, 122 insertions(+), 151 deletions(-) delete mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingDialog.java create mode 100644 Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingDialog.kt diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingDialog.java deleted file mode 100644 index de8e8265a6..0000000000 --- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingDialog.java +++ /dev/null @@ -1,151 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later - -package org.dolphinemu.dolphinemu.features.input.ui; - -import android.content.Context; -import android.view.View; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; - -import androidx.appcompat.app.AlertDialog; -import androidx.recyclerview.widget.LinearLayoutManager; - -import com.google.android.material.divider.MaterialDividerItemDecoration; - -import org.dolphinemu.dolphinemu.databinding.DialogAdvancedMappingBinding; -import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface; -import org.dolphinemu.dolphinemu.features.input.model.CoreDevice; -import org.dolphinemu.dolphinemu.features.input.model.MappingCommon; -import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlReference; -import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController; - -import java.util.Arrays; -import java.util.Optional; - -public final class AdvancedMappingDialog extends AlertDialog - implements AdapterView.OnItemClickListener -{ - private final DialogAdvancedMappingBinding mBinding; - private final ControlReference mControlReference; - private final EmulatedController mController; - private final String[] mDevices; - private final AdvancedMappingControlAdapter mControlAdapter; - - private String mSelectedDevice; - - public AdvancedMappingDialog(Context context, DialogAdvancedMappingBinding binding, - ControlReference controlReference, EmulatedController controller) - { - super(context); - - mBinding = binding; - mControlReference = controlReference; - mController = controller; - - mDevices = ControllerInterface.getAllDeviceStrings(); - - // TODO: Remove workaround for text filtering issue in material components when fixed - // https://github.com/material-components/material-components-android/issues/1464 - mBinding.dropdownDevice.setSaveEnabled(false); - - binding.dropdownDevice.setOnItemClickListener(this); - - ArrayAdapter deviceAdapter = new ArrayAdapter<>( - context, android.R.layout.simple_spinner_dropdown_item, mDevices); - binding.dropdownDevice.setAdapter(deviceAdapter); - - mControlAdapter = new AdvancedMappingControlAdapter(this::onControlClicked); - mBinding.listControl.setAdapter(mControlAdapter); - mBinding.listControl.setLayoutManager(new LinearLayoutManager(context)); - - MaterialDividerItemDecoration divider = - new MaterialDividerItemDecoration(context, LinearLayoutManager.VERTICAL); - divider.setLastItemDecorated(false); - mBinding.listControl.addItemDecoration(divider); - - binding.editExpression.setText(controlReference.getExpression()); - - selectDefaultDevice(); - } - - public String getExpression() - { - return mBinding.editExpression.getText().toString(); - } - - @Override - public void onItemClick(AdapterView adapterView, View view, int position, long id) - { - setSelectedDevice(mDevices[position]); - } - - private void setSelectedDevice(String deviceString) - { - mSelectedDevice = deviceString; - - CoreDevice device = ControllerInterface.getDevice(deviceString); - if (device == null) - setControls(new CoreDevice.Control[0]); - else if (mControlReference.isInput()) - setControls(device.getInputs()); - else - setControls(device.getOutputs()); - } - - private void setControls(CoreDevice.Control[] controls) - { - mControlAdapter.setControls( - Arrays.stream(controls) - .map(CoreDevice.Control::getName) - .toArray(String[]::new)); - } - - private void onControlClicked(String control) - { - String expression = MappingCommon.getExpressionForControl(control, mSelectedDevice, - mController.getDefaultDevice()); - - int start = Math.max(mBinding.editExpression.getSelectionStart(), 0); - int end = Math.max(mBinding.editExpression.getSelectionEnd(), 0); - mBinding.editExpression.getText().replace( - Math.min(start, end), Math.max(start, end), expression, 0, expression.length()); - } - - private void selectDefaultDevice() - { - String defaultDevice = mController.getDefaultDevice(); - boolean isInput = mControlReference.isInput(); - - if (Arrays.asList(mDevices).contains(defaultDevice) && - (isInput || deviceHasOutputs(defaultDevice))) - { - // The default device is available, and it's an appropriate choice. Pick it - setSelectedDevice(defaultDevice); - mBinding.dropdownDevice.setText(defaultDevice, false); - return; - } - else if (!isInput) - { - // Find the first device that has an output. (Most built-in devices don't have any) - Optional deviceWithOutputs = Arrays.stream(mDevices) - .filter(AdvancedMappingDialog::deviceHasOutputs) - .findFirst(); - - if (deviceWithOutputs.isPresent()) - { - setSelectedDevice(deviceWithOutputs.get()); - mBinding.dropdownDevice.setText(deviceWithOutputs.get(), false); - return; - } - } - - // Nothing found - setSelectedDevice(""); - } - - private static boolean deviceHasOutputs(String deviceString) - { - CoreDevice device = ControllerInterface.getDevice(deviceString); - return device != null && device.getOutputs().length > 0; - } -} 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 new file mode 100644 index 0000000000..72d08c26aa --- /dev/null +++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingDialog.kt @@ -0,0 +1,122 @@ +// SPDX-License-Identifier: GPL-2.0-or-later + +package org.dolphinemu.dolphinemu.features.input.ui + +import android.content.Context +import android.view.View +import android.widget.AdapterView +import android.widget.AdapterView.OnItemClickListener +import android.widget.ArrayAdapter +import androidx.appcompat.app.AlertDialog +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.divider.MaterialDividerItemDecoration +import org.dolphinemu.dolphinemu.databinding.DialogAdvancedMappingBinding +import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface +import org.dolphinemu.dolphinemu.features.input.model.CoreDevice +import org.dolphinemu.dolphinemu.features.input.model.MappingCommon +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlReference +import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController + +class AdvancedMappingDialog( + context: Context, + private val binding: DialogAdvancedMappingBinding, + private val controlReference: ControlReference, + private val controller: EmulatedController +) : AlertDialog(context), OnItemClickListener { + private val devices: Array = ControllerInterface.getAllDeviceStrings() + private val controlAdapter: AdvancedMappingControlAdapter + private lateinit var selectedDevice: String + + init { + // TODO: Remove workaround for text filtering issue in material components when fixed + // https://github.com/material-components/material-components-android/issues/1464 + binding.dropdownDevice.isSaveEnabled = false + + binding.dropdownDevice.onItemClickListener = this + + val deviceAdapter = + ArrayAdapter(context, android.R.layout.simple_spinner_dropdown_item, devices) + binding.dropdownDevice.setAdapter(deviceAdapter) + + controlAdapter = + AdvancedMappingControlAdapter { control: String -> onControlClicked(control) } + binding.listControl.adapter = controlAdapter + binding.listControl.layoutManager = LinearLayoutManager(context) + + val divider = MaterialDividerItemDecoration(context, LinearLayoutManager.VERTICAL) + divider.isLastItemDecorated = false + binding.listControl.addItemDecoration(divider) + + binding.editExpression.setText(controlReference.getExpression()) + + selectDefaultDevice() + } + + val expression: String + get() = binding.editExpression.text.toString() + + override fun onItemClick(adapterView: AdapterView<*>?, view: View, position: Int, id: Long) = + setSelectedDevice(devices[position]) + + private fun setSelectedDevice(deviceString: String) { + selectedDevice = deviceString + + val device = ControllerInterface.getDevice(deviceString) + if (device == null) + setControls(emptyArray()) + else if (controlReference.isInput()) + setControls(device.getInputs()) + else + setControls(device.getOutputs()) + } + + private fun setControls(controls: Array) = + controlAdapter.setControls(controls.map { it.getName() }.toTypedArray()) + + private fun onControlClicked(control: String) { + val expression = + MappingCommon.getExpressionForControl(control, selectedDevice, controller.getDefaultDevice()) + + val start = binding.editExpression.selectionStart.coerceAtLeast(0) + val end = binding.editExpression.selectionEnd.coerceAtLeast(0) + binding.editExpression.text?.replace( + start.coerceAtMost(end), + start.coerceAtLeast(end), + expression, + 0, + expression.length + ) + } + + private fun selectDefaultDevice() { + val defaultDevice = controller.getDefaultDevice() + val isInput = controlReference.isInput() + + if (listOf(*devices).contains(defaultDevice) && + (isInput || deviceHasOutputs(defaultDevice)) + ) { + // The default device is available, and it's an appropriate choice. Pick it + setSelectedDevice(defaultDevice) + binding.dropdownDevice.setText(defaultDevice, false) + return + } else if (!isInput) { + // Find the first device that has an output. (Most built-in devices don't have any) + val deviceWithOutputs = devices.first { deviceHasOutputs(it) } + if (deviceWithOutputs.isNotEmpty()) { + setSelectedDevice(deviceWithOutputs) + binding.dropdownDevice.setText(deviceWithOutputs, false) + return + } + } + + // Nothing found + setSelectedDevice("") + } + + companion object { + private fun deviceHasOutputs(deviceString: String): Boolean { + val device = ControllerInterface.getDevice(deviceString) + return device != null && device.getOutputs().isNotEmpty() + } + } +}