summaryrefslogtreecommitdiff
path: root/Source/Android
diff options
context:
space:
mode:
authorJosJuice <josjuice@gmail.com>2023-08-26 19:10:56 +0200
committerGitHub <noreply@github.com>2023-08-26 19:10:56 +0200
commitf9959656e763555cf6314ec5d5502feefb1e7740 (patch)
tree82eba971d931724d373998f517a90be1c0084a25 /Source/Android
parentcd31da97d6362922e3eb63e58ef4d099928fdab7 (diff)
parent0e3b33d90114e5d42fdc9f3fe6bb9fadc86eef4a (diff)
Merge pull request #11919 from t895/kotlin-controls
Android: Convert "features.input" package to Kotlin
Diffstat (limited to 'Source/Android')
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControlGroupEnabledSetting.java52
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControlGroupEnabledSetting.kt25
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java178
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.kt150
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/CoreDevice.java48
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/CoreDevice.kt28
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java440
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.kt504
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager.java20
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager.kt16
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerCompat.java35
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerCompat.kt24
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerPassthrough.java33
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerPassthrough.kt16
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.java50
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.kt25
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.java51
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.kt25
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingIntSetting.java50
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingIntSetting.kt23
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputOverrider.java84
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputOverrider.kt82
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java34
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.kt29
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.kt (renamed from Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.java)21
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java66
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.kt59
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java39
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.kt28
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java52
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.kt52
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java104
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.kt97
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.java70
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.kt42
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java71
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.kt33
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlAdapter.java55
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlAdapter.kt33
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlViewHolder.java34
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlViewHolder.kt23
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingDialog.java151
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingDialog.kt122
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java99
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.kt68
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileAdapter.java65
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileAdapter.kt42
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialog.kt6
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialogPresenter.java157
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialogPresenter.kt119
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileViewHolder.java76
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileViewHolder.kt56
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.java77
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.kt55
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/PostProcessing.kt6
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.kt16
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.kt4
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt49
-rw-r--r--Source/Android/jni/AndroidCommon/IDCache.cpp14
59 files changed, 1827 insertions, 2256 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControlGroupEnabledSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControlGroupEnabledSetting.java
deleted file mode 100644
index 2e22ce84aa..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControlGroupEnabledSetting.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model;
-
-import androidx.annotation.NonNull;
-
-import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup;
-import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting;
-import org.dolphinemu.dolphinemu.features.settings.model.Settings;
-
-public class ControlGroupEnabledSetting implements AbstractBooleanSetting
-{
- private final ControlGroup mControlGroup;
-
- public ControlGroupEnabledSetting(ControlGroup controlGroup)
- {
- mControlGroup = controlGroup;
- }
-
- @Override
- public boolean getBoolean()
- {
- return mControlGroup.getEnabled();
- }
-
- @Override
- public void setBoolean(@NonNull Settings settings, boolean newValue)
- {
- mControlGroup.setEnabled(newValue);
- }
-
- @Override
- public boolean isOverridden()
- {
- return false;
- }
-
- @Override
- public boolean isRuntimeEditable()
- {
- return true;
- }
-
- @Override
- public boolean delete(@NonNull Settings settings)
- {
- boolean newValue = mControlGroup.getDefaultEnabledValue() != ControlGroup.DEFAULT_ENABLED_NO;
- mControlGroup.setEnabled(newValue);
-
- return true;
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControlGroupEnabledSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControlGroupEnabledSetting.kt
new file mode 100644
index 0000000000..43c983cc41
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControlGroupEnabledSetting.kt
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model
+
+import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlGroup
+import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting
+import org.dolphinemu.dolphinemu.features.settings.model.Settings
+
+class ControlGroupEnabledSetting(private val controlGroup: ControlGroup) : AbstractBooleanSetting {
+ override val boolean: Boolean
+ get() = controlGroup.getEnabled()
+
+ override fun setBoolean(settings: Settings, newValue: Boolean) =
+ controlGroup.setEnabled(newValue)
+
+ override val isOverridden: Boolean = false
+
+ override val isRuntimeEditable: Boolean = true
+
+ override fun delete(settings: Settings): Boolean {
+ val newValue = controlGroup.getDefaultEnabledValue() != ControlGroup.DEFAULT_ENABLED_NO
+ controlGroup.setEnabled(newValue)
+ return true
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java
deleted file mode 100644
index 732ed97a7c..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.java
+++ /dev/null
@@ -1,178 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model;
-
-import android.content.Context;
-import android.hardware.input.InputManager;
-import android.os.Build;
-import android.os.Handler;
-import android.os.VibrationEffect;
-import android.os.Vibrator;
-import android.os.VibratorManager;
-import android.view.InputDevice;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-import androidx.annotation.Keep;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.dolphinemu.dolphinemu.DolphinApplication;
-import org.dolphinemu.dolphinemu.utils.LooperThread;
-
-/**
- * This class interfaces with the native ControllerInterface,
- * which is where the emulator core gets inputs from.
- */
-public final class ControllerInterface
-{
- private static final class InputDeviceListener implements InputManager.InputDeviceListener
- {
- @Override
- public void onInputDeviceAdded(int deviceId)
- {
- // Simple implementation for now. We could do something fancier if we wanted to.
- refreshDevices();
- }
-
- @Override
- public void onInputDeviceRemoved(int deviceId)
- {
- // Simple implementation for now. We could do something fancier if we wanted to.
- refreshDevices();
- }
-
- @Override
- public void onInputDeviceChanged(int deviceId)
- {
- // Simple implementation for now. We could do something fancier if we wanted to.
- refreshDevices();
- }
- }
-
- private static InputDeviceListener mInputDeviceListener;
- private static LooperThread mLooperThread;
-
- /**
- * Activities which want to pass on inputs to native code
- * should call this in their own dispatchKeyEvent method.
- *
- * @return true if the emulator core seems to be interested in this event.
- * false if the event should be passed on to the default dispatchKeyEvent.
- */
- public static native boolean dispatchKeyEvent(KeyEvent event);
-
- /**
- * Activities which want to pass on inputs to native code
- * should call this in their own dispatchGenericMotionEvent method.
- *
- * @return true if the emulator core seems to be interested in this event.
- * false if the event should be passed on to the default dispatchGenericMotionEvent.
- */
- public static native boolean dispatchGenericMotionEvent(MotionEvent event);
-
- /**
- * {@link DolphinSensorEventListener} calls this for each axis of a received SensorEvent.
- *
- * @return true if the emulator core seems to be interested in this event.
- * false if the sensor can be suspended to save battery.
- */
- public static native boolean dispatchSensorEvent(String deviceQualifier, String axisName,
- float value);
-
- /**
- * Called when a sensor is suspended or unsuspended.
- *
- * @param deviceQualifier A string used by native code for uniquely identifying devices.
- * @param axisNames The name of all axes for the sensor.
- * @param suspended Whether the sensor is now suspended.
- */
- public static native void notifySensorSuspendedState(String deviceQualifier, String[] axisNames,
- boolean suspended);
-
- /**
- * Rescans for input devices.
- */
- public static native void refreshDevices();
-
- public static native String[] getAllDeviceStrings();
-
- @Nullable
- public static native CoreDevice getDevice(String deviceString);
-
- @Keep
- private static void registerInputDeviceListener()
- {
- if (mLooperThread == null)
- {
- mLooperThread = new LooperThread("Hotplug thread");
- mLooperThread.start();
- }
-
- if (mInputDeviceListener == null)
- {
- InputManager im = (InputManager)
- DolphinApplication.getAppContext().getSystemService(Context.INPUT_SERVICE);
-
- mInputDeviceListener = new InputDeviceListener();
- im.registerInputDeviceListener(mInputDeviceListener, new Handler(mLooperThread.getLooper()));
- }
- }
-
- @Keep
- private static void unregisterInputDeviceListener()
- {
- if (mInputDeviceListener != null)
- {
- InputManager im = (InputManager)
- DolphinApplication.getAppContext().getSystemService(Context.INPUT_SERVICE);
-
- im.unregisterInputDeviceListener(mInputDeviceListener);
- mInputDeviceListener = null;
- }
- }
-
- @Keep @NonNull
- private static DolphinVibratorManager getVibratorManager(InputDevice device)
- {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
- {
- return new DolphinVibratorManagerPassthrough(device.getVibratorManager());
- }
- else
- {
- return new DolphinVibratorManagerCompat(device.getVibrator());
- }
- }
-
- @Keep @NonNull
- private static DolphinVibratorManager getSystemVibratorManager()
- {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
- {
- VibratorManager vibratorManager = (VibratorManager)
- DolphinApplication.getAppContext().getSystemService(Context.VIBRATOR_MANAGER_SERVICE);
-
- if (vibratorManager != null)
- return new DolphinVibratorManagerPassthrough(vibratorManager);
- }
-
- Vibrator vibrator = (Vibrator)
- DolphinApplication.getAppContext().getSystemService(Context.VIBRATOR_SERVICE);
-
- return new DolphinVibratorManagerCompat(vibrator);
- }
-
- @Keep
- private static void vibrate(@NonNull Vibrator vibrator)
- {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
- {
- vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE));
- }
- else
- {
- vibrator.vibrate(100);
- }
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.kt
new file mode 100644
index 0000000000..3bca59f7b7
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/ControllerInterface.kt
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model
+
+import android.content.Context
+import android.hardware.input.InputManager
+import android.os.Build
+import android.os.Handler
+import android.os.VibrationEffect
+import android.os.Vibrator
+import android.os.VibratorManager
+import android.view.InputDevice
+import android.view.KeyEvent
+import android.view.MotionEvent
+import androidx.annotation.Keep
+import org.dolphinemu.dolphinemu.DolphinApplication
+import org.dolphinemu.dolphinemu.utils.LooperThread
+
+/**
+ * This class interfaces with the native ControllerInterface,
+ * which is where the emulator core gets inputs from.
+ */
+object ControllerInterface {
+ private var inputDeviceListener: InputDeviceListener? = null
+ private lateinit var looperThread: LooperThread
+
+ /**
+ * Activities which want to pass on inputs to native code
+ * should call this in their own dispatchKeyEvent method.
+ *
+ * @return true if the emulator core seems to be interested in this event.
+ * false if the event should be passed on to the default dispatchKeyEvent.
+ */
+ external fun dispatchKeyEvent(event: KeyEvent): Boolean
+
+ /**
+ * Activities which want to pass on inputs to native code
+ * should call this in their own dispatchGenericMotionEvent method.
+ *
+ * @return true if the emulator core seems to be interested in this event.
+ * false if the event should be passed on to the default dispatchGenericMotionEvent.
+ */
+ external fun dispatchGenericMotionEvent(event: MotionEvent): Boolean
+
+ /**
+ * [DolphinSensorEventListener] calls this for each axis of a received SensorEvent.
+ *
+ * @return true if the emulator core seems to be interested in this event.
+ * false if the sensor can be suspended to save battery.
+ */
+ external fun dispatchSensorEvent(
+ deviceQualifier: String,
+ axisName: String,
+ value: Float
+ ): Boolean
+
+ /**
+ * Called when a sensor is suspended or unsuspended.
+ *
+ * @param deviceQualifier A string used by native code for uniquely identifying devices.
+ * @param axisNames The name of all axes for the sensor.
+ * @param suspended Whether the sensor is now suspended.
+ */
+ external fun notifySensorSuspendedState(
+ deviceQualifier: String,
+ axisNames: Array<String>,
+ suspended: Boolean
+ )
+
+ /**
+ * Rescans for input devices.
+ */
+ external fun refreshDevices()
+
+ external fun getAllDeviceStrings(): Array<String>
+
+ external fun getDevice(deviceString: String): CoreDevice?
+
+ @Keep
+ @JvmStatic
+ private fun registerInputDeviceListener() {
+ looperThread = LooperThread("Hotplug thread")
+ looperThread.start()
+
+ if (inputDeviceListener == null) {
+ val im = DolphinApplication.getAppContext()
+ .getSystemService(Context.INPUT_SERVICE) as InputManager?
+
+ inputDeviceListener = InputDeviceListener()
+ im!!.registerInputDeviceListener(inputDeviceListener, Handler(looperThread.looper))
+ }
+ }
+
+ @Keep
+ @JvmStatic
+ private fun unregisterInputDeviceListener() {
+ if (inputDeviceListener != null) {
+ val im = DolphinApplication.getAppContext()
+ .getSystemService(Context.INPUT_SERVICE) as InputManager?
+
+ im!!.unregisterInputDeviceListener(inputDeviceListener)
+ inputDeviceListener = null
+ }
+ }
+
+ @Keep
+ @JvmStatic
+ private fun getVibratorManager(device: InputDevice): DolphinVibratorManager {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ DolphinVibratorManagerPassthrough(device.vibratorManager)
+ } else {
+ DolphinVibratorManagerCompat(device.vibrator)
+ }
+ }
+
+ @Keep
+ @JvmStatic
+ private fun getSystemVibratorManager(): DolphinVibratorManager {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ val vibratorManager = DolphinApplication.getAppContext()
+ .getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager?
+ if (vibratorManager != null)
+ return DolphinVibratorManagerPassthrough(vibratorManager)
+ }
+ val vibrator = DolphinApplication.getAppContext()
+ .getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
+ return DolphinVibratorManagerCompat(vibrator)
+ }
+
+ @Keep
+ @JvmStatic
+ private fun vibrate(vibrator: Vibrator) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ vibrator.vibrate(VibrationEffect.createOneShot(100, VibrationEffect.DEFAULT_AMPLITUDE))
+ } else {
+ vibrator.vibrate(100)
+ }
+ }
+
+ private class InputDeviceListener : InputManager.InputDeviceListener {
+ // Simple implementation for now. We could do something fancier if we wanted to.
+ override fun onInputDeviceAdded(deviceId: Int) = refreshDevices()
+
+ // Simple implementation for now. We could do something fancier if we wanted to.
+ override fun onInputDeviceRemoved(deviceId: Int) = refreshDevices()
+
+ // Simple implementation for now. We could do something fancier if we wanted to.
+ override fun onInputDeviceChanged(deviceId: Int) = refreshDevices()
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/CoreDevice.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/CoreDevice.java
deleted file mode 100644
index b69e9a0236..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/CoreDevice.java
+++ /dev/null
@@ -1,48 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model;
-
-import androidx.annotation.Keep;
-
-/**
- * Represents a C++ ciface::Core::Device.
- */
-public final class CoreDevice
-{
- /**
- * Represents a C++ ciface::Core::Device::Control.
- *
- * This class is non-static to ensure that the CoreDevice parent does not get garbage collected
- * while a Control is still accessible. (CoreDevice's finalizer may delete the native controls.)
- */
- @SuppressWarnings("InnerClassMayBeStatic")
- public final class Control
- {
- @Keep
- private final long mPointer;
-
- @Keep
- private Control(long pointer)
- {
- mPointer = pointer;
- }
-
- public native String getName();
- }
-
- @Keep
- private final long mPointer;
-
- @Keep
- private CoreDevice(long pointer)
- {
- mPointer = pointer;
- }
-
- @Override
- protected native void finalize();
-
- public native Control[] getInputs();
-
- public native Control[] getOutputs();
-}
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
new file mode 100644
index 0000000000..1483422c50
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/CoreDevice.kt
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model
+
+import androidx.annotation.Keep
+
+/**
+ * Represents a C++ ciface::Core::Device.
+ */
+@Keep
+class CoreDevice private constructor(private val pointer: Long) {
+ /**
+ * Represents a C++ ciface::Core::Device::Control.
+ *
+ * This class is marked inner to ensure that the CoreDevice parent does not get garbage collected
+ * while a Control is still accessible. (CoreDevice's finalizer may delete the native controls.)
+ */
+ @Keep
+ inner class Control private constructor(private val pointer: Long) {
+ external fun getName(): String
+ }
+
+ protected external fun finalize()
+
+ external fun getInputs(): Array<Control>
+
+ external fun getOutputs(): Array<Control>
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java
deleted file mode 100644
index 78d0e4785d..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.java
+++ /dev/null
@@ -1,440 +0,0 @@
-package org.dolphinemu.dolphinemu.features.input.model;
-
-import android.content.Context;
-import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
-import android.hardware.SensorManager;
-import android.os.Build;
-import android.view.InputDevice;
-import android.view.Surface;
-
-import androidx.annotation.Keep;
-
-import org.dolphinemu.dolphinemu.DolphinApplication;
-import org.dolphinemu.dolphinemu.utils.Log;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class DolphinSensorEventListener implements SensorEventListener
-{
- // Set of three axes. Creates a negative companion to each axis, and corrects for device rotation.
- private static final int AXIS_SET_TYPE_DEVICE_COORDINATES = 0;
- // Set of three axes. Creates a negative companion to each axis.
- private static final int AXIS_SET_TYPE_OTHER_COORDINATES = 1;
-
- private static class AxisSetDetails
- {
- public final int firstAxisOfSet;
- public final int axisSetType;
-
- public AxisSetDetails(int firstAxisOfSet, int axisSetType)
- {
- this.firstAxisOfSet = firstAxisOfSet;
- this.axisSetType = axisSetType;
- }
- }
-
- private static class SensorDetails
- {
- public final int sensorType;
- public final String[] axisNames;
- public final AxisSetDetails[] axisSetDetails;
- public boolean isSuspended = true;
-
- public SensorDetails(int sensorType, String[] axisNames, AxisSetDetails[] axisSetDetails)
- {
- this.sensorType = sensorType;
- this.axisNames = axisNames;
- this.axisSetDetails = axisSetDetails;
- }
- }
-
- private static int sDeviceRotation = Surface.ROTATION_0;
-
- private final SensorManager mSensorManager;
-
- private final HashMap<Sensor, SensorDetails> mSensorDetails = new HashMap<>();
-
- private final boolean mRotateCoordinatesForScreenOrientation;
-
- private String mDeviceQualifier = "";
-
- // The fastest sampling rate Android lets us use without declaring the HIGH_SAMPLING_RATE_SENSORS
- // permission is 200 Hz. This is also the sampling rate of a Wii Remote, so it fits us perfectly.
- private static final int SAMPLING_PERIOD_US = 1000000 / 200;
-
- @Keep
- public DolphinSensorEventListener()
- {
- mSensorManager = (SensorManager)
- DolphinApplication.getAppContext().getSystemService(Context.SENSOR_SERVICE);
- mRotateCoordinatesForScreenOrientation = true;
-
- addSensors();
- }
-
- @Keep
- public DolphinSensorEventListener(InputDevice inputDevice)
- {
- mRotateCoordinatesForScreenOrientation = false;
-
- if (Build.VERSION.SDK_INT >= 31)
- {
- mSensorManager = inputDevice.getSensorManager();
-
- // TODO: There is a bug where after suspending sensors, onSensorChanged can get called for
- // a sensor that we never registered as a listener for. The way our code is currently written,
- // this causes a NullPointerException, but if we checked for null we would instead have the
- // problem of being spammed with onSensorChanged calls even though the sensor shouldn't be
- // enabled. For now, let's comment out the ability to use InputDevice sensors.
-
- //addSensors();
- }
- else
- {
- mSensorManager = null;
- }
- }
-
- private void addSensors()
- {
- tryAddSensor(Sensor.TYPE_ACCELEROMETER, new String[]{"Accel Right", "Accel Left",
- "Accel Forward", "Accel Backward", "Accel Up", "Accel Down"},
- new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)});
-
- tryAddSensor(Sensor.TYPE_GYROSCOPE, new String[]{"Gyro Pitch Up", "Gyro Pitch Down",
- "Gyro Roll Right", "Gyro Roll Left", "Gyro Yaw Left", "Gyro Yaw Right"},
- new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)});
-
- tryAddSensor(Sensor.TYPE_LIGHT, "Light");
-
- tryAddSensor(Sensor.TYPE_PRESSURE, "Pressure");
-
- tryAddSensor(Sensor.TYPE_TEMPERATURE, "Device Temperature");
-
- tryAddSensor(Sensor.TYPE_PROXIMITY, "Proximity");
-
- tryAddSensor(Sensor.TYPE_GRAVITY, new String[]{"Gravity Right", "Gravity Left",
- "Gravity Forward", "Gravity Backward", "Gravity Up", "Gravity Down"},
- new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)});
-
- tryAddSensor(Sensor.TYPE_LINEAR_ACCELERATION,
- new String[]{"Linear Acceleration Right", "Linear Acceleration Left",
- "Linear Acceleration Forward", "Linear Acceleration Backward",
- "Linear Acceleration Up", "Linear Acceleration Down"},
- new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)});
-
- // The values provided by this sensor can be interpreted as an Euler vector or a quaternion.
- // The directions of X and Y are flipped to match the Wii Remote coordinate system.
- tryAddSensor(Sensor.TYPE_ROTATION_VECTOR,
- new String[]{"Rotation Vector X-", "Rotation Vector X+", "Rotation Vector Y-",
- "Rotation Vector Y+", "Rotation Vector Z+",
- "Rotation Vector Z-", "Rotation Vector R", "Rotation Vector Heading Accuracy"},
- new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)});
-
- tryAddSensor(Sensor.TYPE_RELATIVE_HUMIDITY, "Relative Humidity");
-
- tryAddSensor(Sensor.TYPE_AMBIENT_TEMPERATURE, "Ambient Temperature");
-
- // The values provided by this sensor can be interpreted as an Euler vector or a quaternion.
- // The directions of X and Y are flipped to match the Wii Remote coordinate system.
- tryAddSensor(Sensor.TYPE_GAME_ROTATION_VECTOR,
- new String[]{"Game Rotation Vector X-", "Game Rotation Vector X+",
- "Game Rotation Vector Y-", "Game Rotation Vector Y+", "Game Rotation Vector Z+",
- "Game Rotation Vector Z-", "Game Rotation Vector R"},
- new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES)});
-
- tryAddSensor(Sensor.TYPE_GYROSCOPE_UNCALIBRATED,
- new String[]{"Gyro Uncalibrated Pitch Up", "Gyro Uncalibrated Pitch Down",
- "Gyro Uncalibrated Roll Right", "Gyro Uncalibrated Roll Left",
- "Gyro Uncalibrated Yaw Left", "Gyro Uncalibrated Yaw Right",
- "Gyro Drift Pitch Up", "Gyro Drift Pitch Down", "Gyro Drift Roll Right",
- "Gyro Drift Roll Left", "Gyro Drift Yaw Left", "Gyro Drift Yaw Right"},
- new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES),
- new AxisSetDetails(3, AXIS_SET_TYPE_DEVICE_COORDINATES)});
-
- tryAddSensor(Sensor.TYPE_HEART_RATE, "Heart Rate");
-
- if (Build.VERSION.SDK_INT >= 24)
- {
- tryAddSensor(Sensor.TYPE_HEART_BEAT, "Heart Beat");
- }
-
- if (Build.VERSION.SDK_INT >= 26)
- {
- tryAddSensor(Sensor.TYPE_ACCELEROMETER_UNCALIBRATED,
- new String[]{"Accel Uncalibrated Right", "Accel Uncalibrated Left",
- "Accel Uncalibrated Forward", "Accel Uncalibrated Backward",
- "Accel Uncalibrated Up", "Accel Uncalibrated Down",
- "Accel Bias Right", "Accel Bias Left", "Accel Bias Forward",
- "Accel Bias Backward", "Accel Bias Up", "Accel Bias Down"},
- new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES),
- new AxisSetDetails(3, AXIS_SET_TYPE_DEVICE_COORDINATES)});
- }
-
- if (Build.VERSION.SDK_INT >= 30)
- {
- tryAddSensor(Sensor.TYPE_HINGE_ANGLE, "Hinge Angle");
- }
-
- if (Build.VERSION.SDK_INT >= 33)
- {
- // The values provided by this sensor can be interpreted as an Euler vector.
- // The directions of X and Y are flipped to match the Wii Remote coordinate system.
- tryAddSensor(Sensor.TYPE_HEAD_TRACKER,
- new String[]{"Head Rotation Vector X-", "Head Rotation Vector X+",
- "Head Rotation Vector Y-", "Head Rotation Vector Y+",
- "Head Rotation Vector Z+", "Head Rotation Vector Z-",
- "Head Pitch Up", "Head Pitch Down", "Head Roll Right", "Head Roll Left",
- "Head Yaw Left", "Head Yaw Right"},
- new AxisSetDetails[]{new AxisSetDetails(0, AXIS_SET_TYPE_OTHER_COORDINATES),
- new AxisSetDetails(3, AXIS_SET_TYPE_OTHER_COORDINATES)});
-
- tryAddSensor(Sensor.TYPE_HEADING, new String[]{"Heading", "Heading Accuracy"},
- new AxisSetDetails[]{});
- }
- }
-
- private void tryAddSensor(int sensorType, String axisName)
- {
- tryAddSensor(sensorType, new String[]{axisName}, new AxisSetDetails[]{});
- }
-
- private void tryAddSensor(int sensorType, String[] axisNames, AxisSetDetails[] axisSetDetails)
- {
- Sensor sensor = mSensorManager.getDefaultSensor(sensorType);
- if (sensor != null)
- {
- mSensorDetails.put(sensor, new SensorDetails(sensorType, axisNames, axisSetDetails));
- }
- }
-
- @Override
- public void onSensorChanged(SensorEvent sensorEvent)
- {
- final SensorDetails sensorDetails = mSensorDetails.get(sensorEvent.sensor);
-
- final float[] values = sensorEvent.values;
- final String[] axisNames = sensorDetails.axisNames;
- final AxisSetDetails[] axisSetDetails = sensorDetails.axisSetDetails;
-
- int eventAxisIndex = 0;
- int detailsAxisIndex = 0;
- int detailsAxisSetIndex = 0;
- boolean keepSensorAlive = false;
- while (eventAxisIndex < values.length && detailsAxisIndex < axisNames.length)
- {
- if (detailsAxisSetIndex < axisSetDetails.length &&
- axisSetDetails[detailsAxisSetIndex].firstAxisOfSet == eventAxisIndex)
- {
- int rotation = Surface.ROTATION_0;
- if (mRotateCoordinatesForScreenOrientation &&
- axisSetDetails[detailsAxisSetIndex].axisSetType == AXIS_SET_TYPE_DEVICE_COORDINATES)
- {
- rotation = sDeviceRotation;
- }
-
- float x, y;
- switch (rotation)
- {
- default:
- case Surface.ROTATION_0:
- x = values[eventAxisIndex];
- y = values[eventAxisIndex + 1];
- break;
- case Surface.ROTATION_90:
- x = -values[eventAxisIndex + 1];
- y = values[eventAxisIndex];
- break;
- case Surface.ROTATION_180:
- x = -values[eventAxisIndex];
- y = -values[eventAxisIndex + 1];
- break;
- case Surface.ROTATION_270:
- x = values[eventAxisIndex + 1];
- y = -values[eventAxisIndex];
- break;
- }
-
- float z = values[eventAxisIndex + 2];
-
- keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
- axisNames[detailsAxisIndex], x);
- keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
- axisNames[detailsAxisIndex + 1], x);
- keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
- axisNames[detailsAxisIndex + 2], y);
- keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
- axisNames[detailsAxisIndex + 3], y);
- keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
- axisNames[detailsAxisIndex + 4], z);
- keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
- axisNames[detailsAxisIndex + 5], z);
-
- eventAxisIndex += 3;
- detailsAxisIndex += 6;
- detailsAxisSetIndex++;
- }
- else
- {
- keepSensorAlive |= ControllerInterface.dispatchSensorEvent(mDeviceQualifier,
- axisNames[detailsAxisIndex], values[eventAxisIndex]);
-
- eventAxisIndex++;
- detailsAxisIndex++;
- }
- }
-
- if (!keepSensorAlive)
- {
- setSensorSuspended(sensorEvent.sensor, sensorDetails, true);
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int i)
- {
- // We don't care about this
- }
-
- /**
- * The device qualifier set here will be passed on to native code,
- * for the purpose of letting native code identify which device this object belongs to.
- */
- @Keep
- public void setDeviceQualifier(String deviceQualifier)
- {
- mDeviceQualifier = deviceQualifier;
- }
-
- /**
- * If a sensor has been suspended to save battery, this unsuspends it.
- * If the sensor isn't currently suspended, nothing happens.
- *
- * @param axisName The name of any of the sensor's axes.
- */
- @Keep
- public void requestUnsuspendSensor(String axisName)
- {
- for (Map.Entry<Sensor, SensorDetails> entry : mSensorDetails.entrySet())
- {
- if (Arrays.asList(entry.getValue().axisNames).contains(axisName))
- {
- setSensorSuspended(entry.getKey(), entry.getValue(), false);
- }
- }
- }
-
- private void setSensorSuspended(Sensor sensor, SensorDetails sensorDetails, boolean suspend)
- {
- boolean changeOccurred = false;
-
- synchronized (sensorDetails)
- {
- if (sensorDetails.isSuspended != suspend)
- {
- ControllerInterface.notifySensorSuspendedState(mDeviceQualifier, sensorDetails.axisNames,
- suspend);
-
- if (suspend)
- mSensorManager.unregisterListener(this, sensor);
- else
- mSensorManager.registerListener(this, sensor, SAMPLING_PERIOD_US);
-
- sensorDetails.isSuspended = suspend;
-
- changeOccurred = true;
- }
- }
-
- if (changeOccurred)
- {
- Log.info((suspend ? "Suspended sensor " : "Unsuspended sensor ") + sensor.getName());
- }
- }
-
- @Keep
- public String[] getAxisNames()
- {
- ArrayList<String> axisNames = new ArrayList<>();
-
- for (SensorDetails sensorDetails : getSensorDetailsSorted())
- {
- Collections.addAll(axisNames, sensorDetails.axisNames);
- }
-
- return axisNames.toArray(new String[]{});
- }
-
- @Keep
- public boolean[] getNegativeAxes()
- {
- ArrayList<Boolean> negativeAxes = new ArrayList<>();
-
- for (SensorDetails sensorDetails : getSensorDetailsSorted())
- {
- int eventAxisIndex = 0;
- int detailsAxisIndex = 0;
- int detailsAxisSetIndex = 0;
- while (detailsAxisIndex < sensorDetails.axisNames.length)
- {
- if (detailsAxisSetIndex < sensorDetails.axisSetDetails.length &&
- sensorDetails.axisSetDetails[detailsAxisSetIndex].firstAxisOfSet == eventAxisIndex)
- {
- negativeAxes.add(false);
- negativeAxes.add(true);
- negativeAxes.add(false);
- negativeAxes.add(true);
- negativeAxes.add(false);
- negativeAxes.add(true);
-
- eventAxisIndex += 3;
- detailsAxisIndex += 6;
- detailsAxisSetIndex++;
- }
- else
- {
- negativeAxes.add(false);
-
- eventAxisIndex++;
- detailsAxisIndex++;
- }
- }
- }
-
- boolean[] result = new boolean[negativeAxes.size()];
- for (int i = 0; i < result.length; i++)
- {
- result[i] = negativeAxes.get(i);
- }
-
- return result;
- }
-
- private List<SensorDetails> getSensorDetailsSorted()
- {
- ArrayList<SensorDetails> sensorDetails = new ArrayList<>(mSensorDetails.values());
- Collections.sort(sensorDetails, Comparator.comparingInt(s -> s.sensorType));
- return sensorDetails;
- }
-
- /**
- * Should be called when an activity or other component that uses sensor events is resumed.
- *
- * Sensor events that contain device coordinates will have the coordinates rotated by the value
- * passed to this function.
- *
- * @param deviceRotation The current rotation of the device (i.e. rotation of the default display)
- */
- public static void setDeviceRotation(int deviceRotation)
- {
- sDeviceRotation = deviceRotation;
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.kt
new file mode 100644
index 0000000000..e89143aa31
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinSensorEventListener.kt
@@ -0,0 +1,504 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model
+
+import android.content.Context
+import android.hardware.Sensor
+import android.hardware.SensorEvent
+import android.hardware.SensorEventListener
+import android.hardware.SensorManager
+import android.os.Build
+import android.view.InputDevice
+import android.view.Surface
+import androidx.annotation.Keep
+import org.dolphinemu.dolphinemu.DolphinApplication
+import org.dolphinemu.dolphinemu.utils.Log
+import java.util.Collections
+
+class DolphinSensorEventListener : SensorEventListener {
+ private class AxisSetDetails(val firstAxisOfSet: Int, val axisSetType: Int)
+
+ private class SensorDetails(
+ val sensorType: Int,
+ val axisNames: Array<String>,
+ val axisSetDetails: Array<AxisSetDetails>
+ ) {
+ var isSuspended = true
+ }
+
+ private val sensorManager: SensorManager?
+
+ private val sensorDetails = HashMap<Sensor, SensorDetails>()
+
+ private val rotateCoordinatesForScreenOrientation: Boolean
+
+ private var deviceQualifier = ""
+
+ @Keep
+ constructor() {
+ sensorManager = DolphinApplication.getAppContext()
+ .getSystemService(Context.SENSOR_SERVICE) as SensorManager?
+ rotateCoordinatesForScreenOrientation = true
+ addSensors()
+ }
+
+ @Keep
+ constructor(inputDevice: InputDevice) {
+ rotateCoordinatesForScreenOrientation = false
+ sensorManager = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ inputDevice.sensorManager
+
+ // TODO: There is a bug where after suspending sensors, onSensorChanged can get called for
+ // a sensor that we never registered as a listener for. The way our code is currently written,
+ // this causes a NullPointerException, but if we checked for null we would instead have the
+ // problem of being spammed with onSensorChanged calls even though the sensor shouldn't be
+ // enabled. For now, let's comment out the ability to use InputDevice sensors.
+
+ //addSensors();
+ } else {
+ null
+ }
+ }
+
+ private fun addSensors() {
+ tryAddSensor(
+ Sensor.TYPE_ACCELEROMETER,
+ arrayOf(
+ "Accel Right",
+ "Accel Left",
+ "Accel Forward",
+ "Accel Backward",
+ "Accel Up",
+ "Accel Down"
+ ),
+ arrayOf(AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES))
+ )
+
+ tryAddSensor(
+ Sensor.TYPE_GYROSCOPE,
+ arrayOf(
+ "Gyro Pitch Up",
+ "Gyro Pitch Down",
+ "Gyro Roll Right",
+ "Gyro Roll Left",
+ "Gyro Yaw Left",
+ "Gyro Yaw Right"
+ ),
+ arrayOf(AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES))
+ )
+
+ tryAddSensor(Sensor.TYPE_LIGHT, "Light")
+
+ tryAddSensor(Sensor.TYPE_PRESSURE, "Pressure")
+
+ tryAddSensor(Sensor.TYPE_TEMPERATURE, "Device Temperature")
+
+ tryAddSensor(Sensor.TYPE_PROXIMITY, "Proximity")
+
+ tryAddSensor(
+ Sensor.TYPE_GRAVITY,
+ arrayOf(
+ "Gravity Right",
+ "Gravity Left",
+ "Gravity Forward",
+ "Gravity Backward",
+ "Gravity Up",
+ "Gravity Down"
+ ),
+ arrayOf(AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES))
+ )
+
+ tryAddSensor(
+ Sensor.TYPE_LINEAR_ACCELERATION,
+ arrayOf(
+ "Linear Acceleration Right",
+ "Linear Acceleration Left",
+ "Linear Acceleration Forward",
+ "Linear Acceleration Backward",
+ "Linear Acceleration Up",
+ "Linear Acceleration Down"
+ ),
+ arrayOf(AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES))
+ )
+
+ // The values provided by this sensor can be interpreted as an Euler vector or a quaternion.
+ // The directions of X and Y are flipped to match the Wii Remote coordinate system.
+ tryAddSensor(
+ Sensor.TYPE_ROTATION_VECTOR,
+ arrayOf(
+ "Rotation Vector X-",
+ "Rotation Vector X+",
+ "Rotation Vector Y-",
+ "Rotation Vector Y+",
+ "Rotation Vector Z+",
+ "Rotation Vector Z-",
+ "Rotation Vector R",
+ "Rotation Vector Heading Accuracy"
+ ),
+ arrayOf(AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES))
+ )
+
+ tryAddSensor(Sensor.TYPE_RELATIVE_HUMIDITY, "Relative Humidity")
+
+ tryAddSensor(Sensor.TYPE_AMBIENT_TEMPERATURE, "Ambient Temperature")
+
+ // The values provided by this sensor can be interpreted as an Euler vector or a quaternion.
+ // The directions of X and Y are flipped to match the Wii Remote coordinate system.
+ tryAddSensor(
+ Sensor.TYPE_GAME_ROTATION_VECTOR,
+ arrayOf(
+ "Game Rotation Vector X-",
+ "Game Rotation Vector X+",
+ "Game Rotation Vector Y-",
+ "Game Rotation Vector Y+",
+ "Game Rotation Vector Z+",
+ "Game Rotation Vector Z-",
+ "Game Rotation Vector R"
+ ),
+ arrayOf(AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES))
+ )
+
+ tryAddSensor(
+ Sensor.TYPE_GYROSCOPE_UNCALIBRATED,
+ arrayOf(
+ "Gyro Uncalibrated Pitch Up",
+ "Gyro Uncalibrated Pitch Down",
+ "Gyro Uncalibrated Roll Right",
+ "Gyro Uncalibrated Roll Left",
+ "Gyro Uncalibrated Yaw Left",
+ "Gyro Uncalibrated Yaw Right",
+ "Gyro Drift Pitch Up",
+ "Gyro Drift Pitch Down",
+ "Gyro Drift Roll Right",
+ "Gyro Drift Roll Left",
+ "Gyro Drift Yaw Left",
+ "Gyro Drift Yaw Right"
+ ),
+ arrayOf(
+ AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES),
+ AxisSetDetails(3, AXIS_SET_TYPE_DEVICE_COORDINATES)
+ )
+ )
+
+ tryAddSensor(Sensor.TYPE_HEART_RATE, "Heart Rate")
+
+ if (Build.VERSION.SDK_INT >= 24) {
+ tryAddSensor(Sensor.TYPE_HEART_BEAT, "Heart Beat")
+ }
+
+ if (Build.VERSION.SDK_INT >= 26) {
+ tryAddSensor(
+ Sensor.TYPE_ACCELEROMETER_UNCALIBRATED,
+ arrayOf(
+ "Accel Uncalibrated Right",
+ "Accel Uncalibrated Left",
+ "Accel Uncalibrated Forward",
+ "Accel Uncalibrated Backward",
+ "Accel Uncalibrated Up",
+ "Accel Uncalibrated Down",
+ "Accel Bias Right",
+ "Accel Bias Left",
+ "Accel Bias Forward",
+ "Accel Bias Backward",
+ "Accel Bias Up",
+ "Accel Bias Down"
+ ),
+ arrayOf(
+ AxisSetDetails(0, AXIS_SET_TYPE_DEVICE_COORDINATES),
+ AxisSetDetails(3, AXIS_SET_TYPE_DEVICE_COORDINATES)
+ )
+ )
+ }
+
+ if (Build.VERSION.SDK_INT >= 30) {
+ tryAddSensor(Sensor.TYPE_HINGE_ANGLE, "Hinge Angle")
+ }
+
+ if (Build.VERSION.SDK_INT >= 33) {
+ // The values provided by this sensor can be interpreted as an Euler vector.
+ // The directions of X and Y are flipped to match the Wii Remote coordinate system.
+ tryAddSensor(
+ Sensor.TYPE_HEAD_TRACKER,
+ arrayOf(
+ "Head Rotation Vector X-",
+ "Head Rotation Vector X+",
+ "Head Rotation Vector Y-",
+ "Head Rotation Vector Y+",
+ "Head Rotation Vector Z+",
+ "Head Rotation Vector Z-",
+ "Head Pitch Up",
+ "Head Pitch Down",
+ "Head Roll Right",
+ "Head Roll Left",
+ "Head Yaw Left",
+ "Head Yaw Right"
+ ),
+ arrayOf(
+ AxisSetDetails(0, AXIS_SET_TYPE_OTHER_COORDINATES),
+ AxisSetDetails(3, AXIS_SET_TYPE_OTHER_COORDINATES)
+ )
+ )
+
+ tryAddSensor(Sensor.TYPE_HEADING, arrayOf("Heading", "Heading Accuracy"), arrayOf())
+ }
+ }
+
+ private fun tryAddSensor(sensorType: Int, axisName: String) {
+ tryAddSensor(sensorType, arrayOf(axisName), arrayOf())
+ }
+
+ private fun tryAddSensor(
+ sensorType: Int,
+ axisNames: Array<String>,
+ axisSetDetails: Array<AxisSetDetails>
+ ) {
+ val sensor = sensorManager!!.getDefaultSensor(sensorType)
+ if (sensor != null) {
+ sensorDetails[sensor] = SensorDetails(sensorType, axisNames, axisSetDetails)
+ }
+ }
+
+ override fun onSensorChanged(sensorEvent: SensorEvent) {
+ val sensorDetails = sensorDetails[sensorEvent.sensor]
+
+ val values = sensorEvent.values
+ val axisNames = sensorDetails!!.axisNames
+ val axisSetDetails = sensorDetails.axisSetDetails
+
+ var eventAxisIndex = 0
+ var detailsAxisIndex = 0
+ var detailsAxisSetIndex = 0
+ var keepSensorAlive = false
+ while (eventAxisIndex < values.size && detailsAxisIndex < axisNames.size) {
+ if (detailsAxisSetIndex < axisSetDetails.size &&
+ axisSetDetails[detailsAxisSetIndex].firstAxisOfSet == eventAxisIndex
+ ) {
+ var rotation = Surface.ROTATION_0
+ if (rotateCoordinatesForScreenOrientation &&
+ axisSetDetails[detailsAxisSetIndex].axisSetType == AXIS_SET_TYPE_DEVICE_COORDINATES
+ ) {
+ rotation = deviceRotation
+ }
+
+ var x: Float
+ var y: Float
+ when (rotation) {
+ Surface.ROTATION_0 -> {
+ x = values[eventAxisIndex]
+ y = values[eventAxisIndex + 1]
+ }
+
+ Surface.ROTATION_90 -> {
+ x = -values[eventAxisIndex + 1]
+ y = values[eventAxisIndex]
+ }
+
+ Surface.ROTATION_180 -> {
+ x = -values[eventAxisIndex]
+ y = -values[eventAxisIndex + 1]
+ }
+
+ Surface.ROTATION_270 -> {
+ x = values[eventAxisIndex + 1]
+ y = -values[eventAxisIndex]
+ }
+
+ else -> {
+ x = values[eventAxisIndex]
+ y = values[eventAxisIndex + 1]
+ }
+ }
+
+ val z = values[eventAxisIndex + 2]
+
+ keepSensorAlive = keepSensorAlive or ControllerInterface.dispatchSensorEvent(
+ deviceQualifier,
+ axisNames[detailsAxisIndex],
+ x
+ )
+ keepSensorAlive = keepSensorAlive or ControllerInterface.dispatchSensorEvent(
+ deviceQualifier,
+ axisNames[detailsAxisIndex + 1],
+ x
+ )
+ keepSensorAlive = keepSensorAlive or ControllerInterface.dispatchSensorEvent(
+ deviceQualifier,
+ axisNames[detailsAxisIndex + 2],
+ y
+ )
+ keepSensorAlive = keepSensorAlive or ControllerInterface.dispatchSensorEvent(
+ deviceQualifier,
+ axisNames[detailsAxisIndex + 3],
+ y
+ )
+ keepSensorAlive = keepSensorAlive or ControllerInterface.dispatchSensorEvent(
+ deviceQualifier,
+ axisNames[detailsAxisIndex + 4],
+ z
+ )
+ keepSensorAlive = keepSensorAlive or ControllerInterface.dispatchSensorEvent(
+ deviceQualifier,
+ axisNames[detailsAxisIndex + 5],
+ z
+ )
+
+ eventAxisIndex += 3
+ detailsAxisIndex += 6
+ detailsAxisSetIndex++
+ } else {
+ keepSensorAlive = keepSensorAlive or ControllerInterface.dispatchSensorEvent(
+ deviceQualifier,
+ axisNames[detailsAxisIndex], values[eventAxisIndex]
+ )
+
+ eventAxisIndex++
+ detailsAxisIndex++
+ }
+ }
+ if (!keepSensorAlive) {
+ setSensorSuspended(sensorEvent.sensor, sensorDetails, true)
+ }
+ }
+
+ override fun onAccuracyChanged(sensor: Sensor, i: Int) {
+ // We don't care about this
+ }
+
+ /**
+ * The device qualifier set here will be passed on to native code,
+ * for the purpose of letting native code identify which device this object belongs to.
+ */
+ @Keep
+ fun setDeviceQualifier(deviceQualifier: String) {
+ this.deviceQualifier = deviceQualifier
+ }
+
+ /**
+ * If a sensor has been suspended to save battery, this unsuspends it.
+ * If the sensor isn't currently suspended, nothing happens.
+ *
+ * @param axisName The name of any of the sensor's axes.
+ */
+ @Keep
+ fun requestUnsuspendSensor(axisName: String) {
+ for ((key, value) in sensorDetails) {
+ if (listOf(*value.axisNames).contains(axisName)) {
+ setSensorSuspended(key, value, false)
+ }
+ }
+ }
+
+ private fun setSensorSuspended(
+ sensor: Sensor,
+ sensorDetails: SensorDetails,
+ suspend: Boolean
+ ) {
+ var changeOccurred = false
+
+ synchronized(sensorDetails) {
+ if (sensorDetails.isSuspended != suspend) {
+ ControllerInterface.notifySensorSuspendedState(
+ deviceQualifier,
+ sensorDetails.axisNames,
+ suspend
+ )
+
+ if (suspend)
+ sensorManager!!.unregisterListener(this, sensor)
+ else
+ sensorManager!!.registerListener(this, sensor, SAMPLING_PERIOD_US)
+
+ sensorDetails.isSuspended = suspend
+
+ changeOccurred = true
+ }
+ }
+
+ if (changeOccurred) {
+ Log.info((if (suspend) "Suspended sensor " else "Unsuspended sensor ") + sensor.name)
+ }
+ }
+
+ @Keep
+ fun getAxisNames(): Array<String> {
+ val axisNames = ArrayList<String>()
+ for (sensorDetails in sensorDetailsSorted) {
+ sensorDetails.axisNames.forEach { axisNames.add(it) }
+ }
+ return axisNames.toArray(arrayOf())
+ }
+
+ @Keep
+ fun getNegativeAxes(): BooleanArray {
+ val negativeAxes = ArrayList<Boolean>()
+
+ for (sensorDetails in sensorDetailsSorted) {
+ var eventAxisIndex = 0
+ var detailsAxisIndex = 0
+ var detailsAxisSetIndex = 0
+ while (detailsAxisIndex < sensorDetails.axisNames.size) {
+ if (detailsAxisSetIndex < sensorDetails.axisSetDetails.size &&
+ sensorDetails.axisSetDetails[detailsAxisSetIndex].firstAxisOfSet == eventAxisIndex
+ ) {
+ negativeAxes.add(false)
+ negativeAxes.add(true)
+ negativeAxes.add(false)
+ negativeAxes.add(true)
+ negativeAxes.add(false)
+ negativeAxes.add(true)
+
+ eventAxisIndex += 3
+ detailsAxisIndex += 6
+ detailsAxisSetIndex++
+ } else {
+ negativeAxes.add(false)
+
+ eventAxisIndex++
+ detailsAxisIndex++
+ }
+ }
+ }
+
+ val result = BooleanArray(negativeAxes.size)
+ for (i in result.indices) {
+ result[i] = negativeAxes[i]
+ }
+
+ return result
+ }
+
+ private val sensorDetailsSorted: List<SensorDetails>
+ get() {
+ val sensorDetails = ArrayList(sensorDetails.values)
+ Collections.sort(
+ sensorDetails,
+ Comparator.comparingInt { s: SensorDetails -> s.sensorType }
+ )
+ return sensorDetails
+ }
+
+ companion object {
+ // Set of three axes. Creates a negative companion to each axis, and corrects for device rotation.
+ private const val AXIS_SET_TYPE_DEVICE_COORDINATES = 0
+
+ // Set of three axes. Creates a negative companion to each axis.
+ private const val AXIS_SET_TYPE_OTHER_COORDINATES = 1
+ private var deviceRotation = Surface.ROTATION_0
+
+ // The fastest sampling rate Android lets us use without declaring the HIGH_SAMPLING_RATE_SENSORS
+ // permission is 200 Hz. This is also the sampling rate of a Wii Remote, so it fits us perfectly.
+ private const val SAMPLING_PERIOD_US = 1000000 / 200
+
+ /**
+ * Should be called when an activity or other component that uses sensor events is resumed.
+ *
+ * Sensor events that contain device coordinates will have the coordinates rotated by the value
+ * passed to this function.
+ *
+ * @param deviceRotation The current rotation of the device (i.e. rotation of the default display)
+ */
+ fun setDeviceRotation(deviceRotation: Int) {
+ this.deviceRotation = deviceRotation
+ }
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager.java
deleted file mode 100644
index abc04d969d..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager.java
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model;
-
-import android.os.Vibrator;
-
-import androidx.annotation.Keep;
-import androidx.annotation.NonNull;
-
-/**
- * A wrapper around {@link android.os.VibratorManager}, for backwards compatibility.
- */
-public interface DolphinVibratorManager
-{
- @Keep @NonNull
- Vibrator getVibrator(int vibratorId);
-
- @Keep @NonNull
- int[] getVibratorIds();
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager.kt
new file mode 100644
index 0000000000..c3c6ba28da
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManager.kt
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model
+
+import android.os.Vibrator
+import androidx.annotation.Keep
+
+/**
+ * A wrapper around [android.os.VibratorManager], for backwards compatibility.
+ */
+@Keep
+interface DolphinVibratorManager {
+ fun getVibrator(vibratorId: Int): Vibrator
+
+ fun getVibratorIds(): IntArray
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerCompat.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerCompat.java
deleted file mode 100644
index 40c4fa95d1..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerCompat.java
+++ /dev/null
@@ -1,35 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model;
-
-import android.os.Vibrator;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-public final class DolphinVibratorManagerCompat implements DolphinVibratorManager
-{
- private final Vibrator mVibrator;
- private final int[] mIds;
-
- public DolphinVibratorManagerCompat(@Nullable Vibrator vibrator)
- {
- mVibrator = vibrator;
- mIds = vibrator != null && vibrator.hasVibrator() ? new int[]{0} : new int[]{};
- }
-
- @Override @NonNull
- public Vibrator getVibrator(int vibratorId)
- {
- if (vibratorId > mIds.length)
- throw new IndexOutOfBoundsException();
-
- return mVibrator;
- }
-
- @Override @NonNull
- public int[] getVibratorIds()
- {
- return mIds;
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerCompat.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerCompat.kt
new file mode 100644
index 0000000000..039f0ecb6b
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerCompat.kt
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model
+
+import android.os.Vibrator
+
+class DolphinVibratorManagerCompat(vibrator: Vibrator) : DolphinVibratorManager {
+ private val vibrator: Vibrator
+ private val vibratorIds: IntArray
+
+ init {
+ this.vibrator = vibrator
+ vibratorIds = if (vibrator.hasVibrator()) intArrayOf(0) else intArrayOf()
+ }
+
+ override fun getVibrator(vibratorId: Int): Vibrator {
+ if (vibratorId > vibratorIds.size)
+ throw IndexOutOfBoundsException()
+
+ return vibrator
+ }
+
+ override fun getVibratorIds(): IntArray = vibratorIds
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerPassthrough.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerPassthrough.java
deleted file mode 100644
index 2ca747f54f..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerPassthrough.java
+++ /dev/null
@@ -1,33 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model;
-
-import android.os.Build;
-import android.os.Vibrator;
-import android.os.VibratorManager;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RequiresApi;
-
-@RequiresApi(api = Build.VERSION_CODES.S)
-public final class DolphinVibratorManagerPassthrough implements DolphinVibratorManager
-{
- private final VibratorManager mVibratorManager;
-
- public DolphinVibratorManagerPassthrough(@NonNull VibratorManager vibratorManager)
- {
- mVibratorManager = vibratorManager;
- }
-
- @Override @NonNull
- public Vibrator getVibrator(int vibratorId)
- {
- return mVibratorManager.getVibrator(vibratorId);
- }
-
- @Override @NonNull
- public int[] getVibratorIds()
- {
- return mVibratorManager.getVibratorIds();
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerPassthrough.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerPassthrough.kt
new file mode 100644
index 0000000000..0895484314
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/DolphinVibratorManagerPassthrough.kt
@@ -0,0 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model
+
+import android.os.Build
+import android.os.Vibrator
+import android.os.VibratorManager
+import androidx.annotation.RequiresApi
+
+@RequiresApi(api = Build.VERSION_CODES.S)
+class DolphinVibratorManagerPassthrough(private val vibratorManager: VibratorManager) :
+ DolphinVibratorManager {
+ override fun getVibrator(vibratorId: Int): Vibrator = vibratorManager.getVibrator(vibratorId)
+
+ override fun getVibratorIds(): IntArray = vibratorManager.vibratorIds
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.java
deleted file mode 100644
index 6e80fef6dd..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model;
-
-import androidx.annotation.NonNull;
-
-import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting;
-import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting;
-import org.dolphinemu.dolphinemu.features.settings.model.Settings;
-
-public class InputMappingBooleanSetting implements AbstractBooleanSetting
-{
- private final NumericSetting mNumericSetting;
-
- public InputMappingBooleanSetting(NumericSetting numericSetting)
- {
- mNumericSetting = numericSetting;
- }
-
- @Override
- public boolean getBoolean()
- {
- return mNumericSetting.getBooleanValue();
- }
-
- @Override
- public void setBoolean(@NonNull Settings settings, boolean newValue)
- {
- mNumericSetting.setBooleanValue(newValue);
- }
-
- @Override
- public boolean isOverridden()
- {
- return false;
- }
-
- @Override
- public boolean isRuntimeEditable()
- {
- return true;
- }
-
- @Override
- public boolean delete(@NonNull Settings settings)
- {
- mNumericSetting.setBooleanValue(mNumericSetting.getBooleanDefaultValue());
- return true;
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.kt
new file mode 100644
index 0000000000..5771db54e0
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingBooleanSetting.kt
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model
+
+import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting
+import org.dolphinemu.dolphinemu.features.settings.model.AbstractBooleanSetting
+import org.dolphinemu.dolphinemu.features.settings.model.Settings
+
+class InputMappingBooleanSetting(private val numericSetting: NumericSetting) :
+ AbstractBooleanSetting {
+ override val boolean: Boolean
+ get() = numericSetting.getBooleanValue()
+
+ override fun setBoolean(settings: Settings, newValue: Boolean) =
+ numericSetting.setBooleanValue(newValue)
+
+ override val isOverridden: Boolean = false
+
+ override val isRuntimeEditable: Boolean = true
+
+ override fun delete(settings: Settings): Boolean {
+ numericSetting.setBooleanValue(numericSetting.getBooleanDefaultValue())
+ return true
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.java
deleted file mode 100644
index 0311e6d2b3..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.java
+++ /dev/null
@@ -1,51 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model;
-
-import androidx.annotation.NonNull;
-
-import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting;
-import org.dolphinemu.dolphinemu.features.settings.model.AbstractFloatSetting;
-import org.dolphinemu.dolphinemu.features.settings.model.Settings;
-
-// Yes, floats are not the same thing as doubles... They're close enough, though
-public class InputMappingDoubleSetting implements AbstractFloatSetting
-{
- private final NumericSetting mNumericSetting;
-
- public InputMappingDoubleSetting(NumericSetting numericSetting)
- {
- mNumericSetting = numericSetting;
- }
-
- @Override
- public float getFloat()
- {
- return (float) mNumericSetting.getDoubleValue();
- }
-
- @Override
- public void setFloat(@NonNull Settings settings, float newValue)
- {
- mNumericSetting.setDoubleValue(newValue);
- }
-
- @Override
- public boolean isOverridden()
- {
- return false;
- }
-
- @Override
- public boolean isRuntimeEditable()
- {
- return true;
- }
-
- @Override
- public boolean delete(@NonNull Settings settings)
- {
- mNumericSetting.setDoubleValue(mNumericSetting.getDoubleDefaultValue());
- return true;
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.kt
new file mode 100644
index 0000000000..e2da062250
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingDoubleSetting.kt
@@ -0,0 +1,25 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model
+
+import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting
+import org.dolphinemu.dolphinemu.features.settings.model.AbstractFloatSetting
+import org.dolphinemu.dolphinemu.features.settings.model.Settings
+
+// Yes, floats are not the same thing as doubles... They're close enough, though
+class InputMappingDoubleSetting(private val numericSetting: NumericSetting) : AbstractFloatSetting {
+ override val float: Float
+ get() = numericSetting.getDoubleValue().toFloat()
+
+ override fun setFloat(settings: Settings, newValue: Float) =
+ numericSetting.setDoubleValue(newValue.toDouble())
+
+ override val isOverridden: Boolean = false
+
+ override val isRuntimeEditable: Boolean = true
+
+ override fun delete(settings: Settings): Boolean {
+ numericSetting.setDoubleValue(numericSetting.getDoubleDefaultValue())
+ return true
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingIntSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingIntSetting.java
deleted file mode 100644
index 176346c63a..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingIntSetting.java
+++ /dev/null
@@ -1,50 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model;
-
-import androidx.annotation.NonNull;
-
-import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting;
-import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting;
-import org.dolphinemu.dolphinemu.features.settings.model.Settings;
-
-public class InputMappingIntSetting implements AbstractIntSetting
-{
- private final NumericSetting mNumericSetting;
-
- public InputMappingIntSetting(NumericSetting numericSetting)
- {
- mNumericSetting = numericSetting;
- }
-
- @Override
- public int getInt()
- {
- return mNumericSetting.getIntValue();
- }
-
- @Override
- public void setInt(@NonNull Settings settings, int newValue)
- {
- mNumericSetting.setIntValue(newValue);
- }
-
- @Override
- public boolean isOverridden()
- {
- return false;
- }
-
- @Override
- public boolean isRuntimeEditable()
- {
- return true;
- }
-
- @Override
- public boolean delete(@NonNull Settings settings)
- {
- mNumericSetting.setIntValue(mNumericSetting.getIntDefaultValue());
- return true;
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingIntSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingIntSetting.kt
new file mode 100644
index 0000000000..3e8a14afc7
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputMappingIntSetting.kt
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model
+
+import org.dolphinemu.dolphinemu.features.input.model.controlleremu.NumericSetting
+import org.dolphinemu.dolphinemu.features.settings.model.AbstractIntSetting
+import org.dolphinemu.dolphinemu.features.settings.model.Settings
+
+class InputMappingIntSetting(private val numericSetting: NumericSetting) : AbstractIntSetting {
+ override val int: Int
+ get() = numericSetting.getIntValue()
+
+ override fun setInt(settings: Settings, newValue: Int) = numericSetting.setIntValue(newValue)
+
+ override val isOverridden: Boolean = false
+
+ override val isRuntimeEditable: Boolean = true
+
+ override fun delete(settings: Settings): Boolean {
+ numericSetting.setIntValue(numericSetting.getIntDefaultValue())
+ return true
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputOverrider.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputOverrider.java
deleted file mode 100644
index 6596c8dd91..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputOverrider.java
+++ /dev/null
@@ -1,84 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model;
-
-public final class InputOverrider
-{
- public static final class ControlId
- {
- public static final int GCPAD_A_BUTTON = 0;
- public static final int GCPAD_B_BUTTON = 1;
- public static final int GCPAD_X_BUTTON = 2;
- public static final int GCPAD_Y_BUTTON = 3;
- public static final int GCPAD_Z_BUTTON = 4;
- public static final int GCPAD_START_BUTTON = 5;
- public static final int GCPAD_DPAD_UP = 6;
- public static final int GCPAD_DPAD_DOWN = 7;
- public static final int GCPAD_DPAD_LEFT = 8;
- public static final int GCPAD_DPAD_RIGHT = 9;
- public static final int GCPAD_L_DIGITAL = 10;
- public static final int GCPAD_R_DIGITAL = 11;
- public static final int GCPAD_L_ANALOG = 12;
- public static final int GCPAD_R_ANALOG = 13;
- public static final int GCPAD_MAIN_STICK_X = 14;
- public static final int GCPAD_MAIN_STICK_Y = 15;
- public static final int GCPAD_C_STICK_X = 16;
- public static final int GCPAD_C_STICK_Y = 17;
-
- public static final int WIIMOTE_A_BUTTON = 18;
- public static final int WIIMOTE_B_BUTTON = 19;
- public static final int WIIMOTE_ONE_BUTTON = 20;
- public static final int WIIMOTE_TWO_BUTTON = 21;
- public static final int WIIMOTE_PLUS_BUTTON = 22;
- public static final int WIIMOTE_MINUS_BUTTON = 23;
- public static final int WIIMOTE_HOME_BUTTON = 24;
- public static final int WIIMOTE_DPAD_UP = 25;
- public static final int WIIMOTE_DPAD_DOWN = 26;
- public static final int WIIMOTE_DPAD_LEFT = 27;
- public static final int WIIMOTE_DPAD_RIGHT = 28;
- public static final int WIIMOTE_IR_X = 29;
- public static final int WIIMOTE_IR_Y = 30;
-
- public static final int NUNCHUK_C_BUTTON = 31;
- public static final int NUNCHUK_Z_BUTTON = 32;
- public static final int NUNCHUK_STICK_X = 33;
- public static final int NUNCHUK_STICK_Y = 34;
-
- public static final int CLASSIC_A_BUTTON = 35;
- public static final int CLASSIC_B_BUTTON = 36;
- public static final int CLASSIC_X_BUTTON = 37;
- public static final int CLASSIC_Y_BUTTON = 38;
- public static final int CLASSIC_ZL_BUTTON = 39;
- public static final int CLASSIC_ZR_BUTTON = 40;
- public static final int CLASSIC_PLUS_BUTTON = 41;
- public static final int CLASSIC_MINUS_BUTTON = 42;
- public static final int CLASSIC_HOME_BUTTON = 43;
- public static final int CLASSIC_DPAD_UP = 44;
- public static final int CLASSIC_DPAD_DOWN = 45;
- public static final int CLASSIC_DPAD_LEFT = 46;
- public static final int CLASSIC_DPAD_RIGHT = 47;
- public static final int CLASSIC_L_DIGITAL = 48;
- public static final int CLASSIC_R_DIGITAL = 49;
- public static final int CLASSIC_L_ANALOG = 50;
- public static final int CLASSIC_R_ANALOG = 51;
- public static final int CLASSIC_LEFT_STICK_X = 52;
- public static final int CLASSIC_LEFT_STICK_Y = 53;
- public static final int CLASSIC_RIGHT_STICK_X = 54;
- public static final int CLASSIC_RIGHT_STICK_Y = 55;
- }
-
- public static native void registerGameCube(int controllerIndex);
-
- public static native void registerWii(int controllerIndex);
-
- public static native void unregisterGameCube(int controllerIndex);
-
- public static native void unregisterWii(int controllerIndex);
-
- public static native void setControlState(int controllerIndex, int control, double state);
-
- public static native void clearControlState(int controllerIndex, int control);
-
- // Angle is in radians and should be non-negative
- public static native double getGateRadiusAtAngle(int emuPadId, int stick, double angle);
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputOverrider.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputOverrider.kt
new file mode 100644
index 0000000000..8224434ee6
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/InputOverrider.kt
@@ -0,0 +1,82 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model
+
+object InputOverrider {
+ external fun registerGameCube(controllerIndex: Int)
+
+ external fun registerWii(controllerIndex: Int)
+
+ external fun unregisterGameCube(controllerIndex: Int)
+
+ external fun unregisterWii(controllerIndex: Int)
+
+ external fun setControlState(controllerIndex: Int, control: Int, state: Double)
+
+ external fun clearControlState(controllerIndex: Int, control: Int)
+
+ // Angle is in radians and should be non-negative
+ external fun getGateRadiusAtAngle(emuPadId: Int, stick: Int, angle: Double): Double
+
+ object ControlId {
+ const val GCPAD_A_BUTTON = 0
+ const val GCPAD_B_BUTTON = 1
+ const val GCPAD_X_BUTTON = 2
+ const val GCPAD_Y_BUTTON = 3
+ const val GCPAD_Z_BUTTON = 4
+ const val GCPAD_START_BUTTON = 5
+ const val GCPAD_DPAD_UP = 6
+ const val GCPAD_DPAD_DOWN = 7
+ const val GCPAD_DPAD_LEFT = 8
+ const val GCPAD_DPAD_RIGHT = 9
+ const val GCPAD_L_DIGITAL = 10
+ const val GCPAD_R_DIGITAL = 11
+ const val GCPAD_L_ANALOG = 12
+ const val GCPAD_R_ANALOG = 13
+ const val GCPAD_MAIN_STICK_X = 14
+ const val GCPAD_MAIN_STICK_Y = 15
+ const val GCPAD_C_STICK_X = 16
+ const val GCPAD_C_STICK_Y = 17
+
+ const val WIIMOTE_A_BUTTON = 18
+ const val WIIMOTE_B_BUTTON = 19
+ const val WIIMOTE_ONE_BUTTON = 20
+ const val WIIMOTE_TWO_BUTTON = 21
+ const val WIIMOTE_PLUS_BUTTON = 22
+ const val WIIMOTE_MINUS_BUTTON = 23
+ const val WIIMOTE_HOME_BUTTON = 24
+ const val WIIMOTE_DPAD_UP = 25
+ const val WIIMOTE_DPAD_DOWN = 26
+ const val WIIMOTE_DPAD_LEFT = 27
+ const val WIIMOTE_DPAD_RIGHT = 28
+ const val WIIMOTE_IR_X = 29
+ const val WIIMOTE_IR_Y = 30
+
+ const val NUNCHUK_C_BUTTON = 31
+ const val NUNCHUK_Z_BUTTON = 32
+ const val NUNCHUK_STICK_X = 33
+ const val NUNCHUK_STICK_Y = 34
+
+ const val CLASSIC_A_BUTTON = 35
+ const val CLASSIC_B_BUTTON = 36
+ const val CLASSIC_X_BUTTON = 37
+ const val CLASSIC_Y_BUTTON = 38
+ const val CLASSIC_ZL_BUTTON = 39
+ const val CLASSIC_ZR_BUTTON = 40
+ const val CLASSIC_PLUS_BUTTON = 41
+ const val CLASSIC_MINUS_BUTTON = 42
+ const val CLASSIC_HOME_BUTTON = 43
+ const val CLASSIC_DPAD_UP = 44
+ const val CLASSIC_DPAD_DOWN = 45
+ const val CLASSIC_DPAD_LEFT = 46
+ const val CLASSIC_DPAD_RIGHT = 47
+ const val CLASSIC_L_DIGITAL = 48
+ const val CLASSIC_R_DIGITAL = 49
+ const val CLASSIC_L_ANALOG = 50
+ const val CLASSIC_R_ANALOG = 51
+ const val CLASSIC_LEFT_STICK_X = 52
+ const val CLASSIC_LEFT_STICK_Y = 53
+ const val CLASSIC_RIGHT_STICK_X = 54
+ const val CLASSIC_RIGHT_STICK_Y = 55
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java
deleted file mode 100644
index 5e4c0076ea..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model;
-
-import androidx.annotation.NonNull;
-
-import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
-
-public final class MappingCommon
-{
- private 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.
- */
- public static native String detectInput(@NonNull EmulatedController controller,
- boolean allDevices);
-
- public static native String getExpressionForControl(String control, String device,
- String defaultDevice);
-
- public static native void save();
-}
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
new file mode 100644
index 0000000000..32775a7ebc
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/MappingCommon.kt
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+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,
+ defaultDevice: String
+ ): String
+
+ external fun save()
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.kt
index 6033e17e37..fcc420c740 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control.kt
@@ -1,8 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
-package org.dolphinemu.dolphinemu.features.input.model.controlleremu;
+package org.dolphinemu.dolphinemu.features.input.model.controlleremu
-import androidx.annotation.Keep;
+import androidx.annotation.Keep
/**
* Represents a C++ ControllerEmu::Control.
@@ -10,18 +10,9 @@ import androidx.annotation.Keep;
* The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
* in C++ is undefined behavior!
*/
-public class Control
-{
- @Keep
- private final long mPointer;
+@Keep
+class Control private constructor(private val pointer: Long) {
+ external fun getUiName(): String
- @Keep
- private Control(long pointer)
- {
- mPointer = pointer;
- }
-
- public native String getUiName();
-
- public native ControlReference getControlReference();
+ external fun getControlReference(): ControlReference
}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java
deleted file mode 100644
index 1bafd4a1f2..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.java
+++ /dev/null
@@ -1,66 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model.controlleremu;
-
-import androidx.annotation.Keep;
-
-/**
- * Represents a C++ ControllerEmu::ControlGroup.
- *
- * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
- * in C++ is undefined behavior!
- */
-public class ControlGroup
-{
- public static final int TYPE_OTHER = 0;
- public static final int TYPE_STICK = 1;
- public static final int TYPE_MIXED_TRIGGERS = 2;
- public static final int TYPE_BUTTONS = 3;
- public static final int TYPE_FORCE = 4;
- public static final int TYPE_ATTACHMENTS = 5;
- public static final int TYPE_TILT = 6;
- public static final int TYPE_CURSOR = 7;
- public static final int TYPE_TRIGGERS = 8;
- public static final int TYPE_SLIDER = 9;
- public static final int TYPE_SHAKE = 10;
- public static final int TYPE_IMU_ACCELEROMETER = 11;
- public static final int TYPE_IMU_GYROSCOPE = 12;
- public static final int TYPE_IMU_CURSOR = 13;
-
- public static final int DEFAULT_ENABLED_ALWAYS = 0;
- public static final int DEFAULT_ENABLED_YES = 1;
- public static final int DEFAULT_ENABLED_NO = 2;
-
- @Keep
- private final long mPointer;
-
- @Keep
- private ControlGroup(long pointer)
- {
- mPointer = pointer;
- }
-
- public native String getUiName();
-
- public native int getGroupType();
-
- public native int getDefaultEnabledValue();
-
- public native boolean getEnabled();
-
- public native void setEnabled(boolean value);
-
- public native int getControlCount();
-
- public native Control getControl(int i);
-
- public native int getNumericSettingCount();
-
- public native NumericSetting getNumericSetting(int i);
-
- /**
- * If getGroupType returns TYPE_ATTACHMENTS, this returns the attachment selection setting.
- * Otherwise, undefined behavior!
- */
- public native NumericSetting getAttachmentSetting();
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.kt
new file mode 100644
index 0000000000..9c5132b1a2
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup.kt
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model.controlleremu
+
+import androidx.annotation.Keep
+
+/**
+ * Represents a C++ ControllerEmu::ControlGroup.
+ *
+ * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
+ * in C++ is undefined behavior!
+ */
+@Keep
+class ControlGroup private constructor(private val pointer: Long) {
+ external fun getUiName(): String
+
+ external fun getGroupType(): Int
+
+ external fun getDefaultEnabledValue(): Int
+
+ external fun getEnabled(): Boolean
+
+ external fun setEnabled(value: Boolean)
+
+ external fun getControlCount(): Int
+
+ external fun getControl(i: Int): Control
+
+ external fun getNumericSettingCount(): Int
+
+ external fun getNumericSetting(i: Int): NumericSetting
+
+ /**
+ * If getGroupType returns TYPE_ATTACHMENTS, this returns the attachment selection setting.
+ * Otherwise, undefined behavior!
+ */
+ external fun getAttachmentSetting(): NumericSetting
+
+ companion object {
+ const val TYPE_OTHER = 0
+ const val TYPE_STICK = 1
+ const val TYPE_MIXED_TRIGGERS = 2
+ const val TYPE_BUTTONS = 3
+ const val TYPE_FORCE = 4
+ const val TYPE_ATTACHMENTS = 5
+ const val TYPE_TILT = 6
+ const val TYPE_CURSOR = 7
+ const val TYPE_TRIGGERS = 8
+ const val TYPE_SLIDER = 9
+ const val TYPE_SHAKE = 10
+ const val TYPE_IMU_ACCELEROMETER = 11
+ const val TYPE_IMU_GYROSCOPE = 12
+ const val TYPE_IMU_CURSOR = 13
+
+ const val DEFAULT_ENABLED_ALWAYS = 0
+ const val DEFAULT_ENABLED_YES = 1
+ const val DEFAULT_ENABLED_NO = 2
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java
deleted file mode 100644
index 66fd9fa02d..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.java
+++ /dev/null
@@ -1,39 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model.controlleremu;
-
-import androidx.annotation.Keep;
-import androidx.annotation.Nullable;
-
-/**
- * Represents a C++ ControlReference.
- *
- * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
- * in C++ is undefined behavior!
- */
-public class ControlReference
-{
- @Keep
- private final long mPointer;
-
- @Keep
- private ControlReference(long pointer)
- {
- mPointer = pointer;
- }
-
- public native double getState();
-
- public native String getExpression();
-
- /**
- * Sets the expression for this control reference.
- *
- * @param expr The new expression
- * @return null on success, a human-readable error on failure
- */
- @Nullable
- public native String setExpression(String expr);
-
- public native boolean isInput();
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.kt
new file mode 100644
index 0000000000..cd0c7848a6
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference.kt
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model.controlleremu
+
+import androidx.annotation.Keep
+
+/**
+ * Represents a C++ ControlReference.
+ *
+ * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
+ * in C++ is undefined behavior!
+ */
+@Keep
+class ControlReference private constructor(private val pointer: Long) {
+ external fun getState(): Double
+
+ external fun getExpression(): String
+
+ /**
+ * Sets the expression for this control reference.
+ *
+ * @param expr The new expression
+ * @return null on success, a human-readable error on failure
+ */
+ external fun setExpression(expr: String): String?
+
+ external fun isInput(): Boolean
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java
deleted file mode 100644
index 39645ffbba..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.java
+++ /dev/null
@@ -1,52 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model.controlleremu;
-
-import androidx.annotation.Keep;
-
-/**
- * Represents a C++ ControllerEmu::EmulatedController.
- *
- * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
- * in C++ is undefined behavior!
- */
-public class EmulatedController
-{
- @Keep
- private final long mPointer;
-
- @Keep
- private EmulatedController(long pointer)
- {
- mPointer = pointer;
- }
-
- public native String getDefaultDevice();
-
- public native void setDefaultDevice(String device);
-
- public native int getGroupCount();
-
- public native ControlGroup getGroup(int index);
-
- public native void updateSingleControlReference(ControlReference controlReference);
-
- public native void loadDefaultSettings();
-
- public native void clearSettings();
-
- public native void loadProfile(String path);
-
- public native void saveProfile(String path);
-
- public static native EmulatedController getGcPad(int controllerIndex);
-
- public static native EmulatedController getWiimote(int controllerIndex);
-
- public static native EmulatedController getWiimoteAttachment(int controllerIndex,
- int attachmentIndex);
-
- public static native int getSelectedWiimoteAttachment(int controllerIndex);
-
- public static native NumericSetting getSidewaysWiimoteSetting(int controllerIndex);
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.kt
new file mode 100644
index 0000000000..348e75b6a0
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController.kt
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model.controlleremu
+
+import androidx.annotation.Keep
+
+/**
+ * Represents a C++ ControllerEmu::EmulatedController.
+ *
+ * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
+ * in C++ is undefined behavior!
+ */
+@Keep
+class EmulatedController private constructor(private val pointer: Long) {
+ external fun getDefaultDevice(): String
+
+ external fun setDefaultDevice(device: String)
+
+ external fun getGroupCount(): Int
+
+ external fun getGroup(index: Int): ControlGroup
+
+ external fun updateSingleControlReference(controlReference: ControlReference)
+
+ external fun loadDefaultSettings()
+
+ external fun clearSettings()
+
+ external fun loadProfile(path: String)
+
+ external fun saveProfile(path: String)
+
+ companion object {
+ @JvmStatic
+ external fun getGcPad(controllerIndex: Int): EmulatedController
+
+ @JvmStatic
+ external fun getWiimote(controllerIndex: Int): EmulatedController
+
+ @JvmStatic
+ external fun getWiimoteAttachment(
+ controllerIndex: Int,
+ attachmentIndex: Int
+ ): EmulatedController
+
+ @JvmStatic
+ external fun getSelectedWiimoteAttachment(controllerIndex: Int): Int
+
+ @JvmStatic
+ external fun getSidewaysWiimoteSetting(controllerIndex: Int): NumericSetting
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java
deleted file mode 100644
index bcb9dce181..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.java
+++ /dev/null
@@ -1,104 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model.controlleremu;
-
-import androidx.annotation.Keep;
-
-/**
- * Represents a C++ ControllerEmu::NumericSetting.
- *
- * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
- * in C++ is undefined behavior!
- */
-public class NumericSetting
-{
- public static final int TYPE_INT = 0;
- public static final int TYPE_DOUBLE = 1;
- public static final int TYPE_BOOLEAN = 2;
-
- @Keep
- private final long mPointer;
-
- @Keep
- private NumericSetting(long pointer)
- {
- mPointer = pointer;
- }
-
- /**
- * @return The name used in the UI.
- */
- public native String getUiName();
-
- /**
- * @return A string applied to the number in the UI (unit of measure).
- */
- public native String getUiSuffix();
-
- /**
- * @return Detailed description of the setting.
- */
- public native String getUiDescription();
-
- /**
- * @return TYPE_INT, TYPE_DOUBLE or TYPE_BOOLEAN
- */
- public native int getType();
-
- public native ControlReference getControlReference();
-
- /**
- * If the type is TYPE_INT, gets the current value. Otherwise, undefined behavior!
- */
- public native int getIntValue();
-
- /**
- * If the type is TYPE_INT, sets the current value. Otherwise, undefined behavior!
- */
- public native void setIntValue(int value);
-
- /**
- * If the type is TYPE_INT, gets the default value. Otherwise, undefined behavior!
- */
- public native int getIntDefaultValue();
-
- /**
- * If the type is TYPE_DOUBLE, gets the current value. Otherwise, undefined behavior!
- */
- public native double getDoubleValue();
-
- /**
- * If the type is TYPE_DOUBLE, sets the current value. Otherwise, undefined behavior!
- */
- public native void setDoubleValue(double value);
-
- /**
- * If the type is TYPE_DOUBLE, gets the default value. Otherwise, undefined behavior!
- */
- public native double getDoubleDefaultValue();
-
- /**
- * If the type is TYPE_DOUBLE, returns the minimum valid value. Otherwise, undefined behavior!
- */
- public native double getDoubleMin();
-
- /**
- * If the type is TYPE_DOUBLE, returns the maximum valid value. Otherwise, undefined behavior!
- */
- public native double getDoubleMax();
-
- /**
- * If the type is TYPE_BOOLEAN, gets the current value. Otherwise, undefined behavior!
- */
- public native boolean getBooleanValue();
-
- /**
- * If the type is TYPE_BOOLEAN, sets the current value. Otherwise, undefined behavior!
- */
- public native void setBooleanValue(boolean value);
-
- /**
- * If the type is TYPE_BOOLEAN, gets the default value. Otherwise, undefined behavior!
- */
- public native boolean getBooleanDefaultValue();
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.kt
new file mode 100644
index 0000000000..0a2485deab
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting.kt
@@ -0,0 +1,97 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model.controlleremu
+
+import androidx.annotation.Keep
+
+/**
+ * Represents a C++ ControllerEmu::NumericSetting.
+ *
+ * The lifetime of this class is managed by C++ code. Calling methods on it after it's destroyed
+ * in C++ is undefined behavior!
+ */
+@Keep
+class NumericSetting private constructor(private val pointer: Long) {
+ /**
+ * @return The name used in the UI.
+ */
+ external fun getUiName(): String
+
+ /**
+ * @return A string applied to the number in the UI (unit of measure).
+ */
+ external fun getUiSuffix(): String
+
+ /**
+ * @return Detailed description of the setting.
+ */
+ external fun getUiDescription(): String
+
+ /**
+ * @return TYPE_INT, TYPE_DOUBLE or TYPE_BOOLEAN
+ */
+ external fun getType(): Int
+
+ external fun getControlReference(): ControlReference
+
+ /**
+ * If the type is TYPE_INT, gets the current value. Otherwise, undefined behavior!
+ */
+ external fun getIntValue(): Int
+
+ /**
+ * If the type is TYPE_INT, sets the current value. Otherwise, undefined behavior!
+ */
+ external fun setIntValue(value: Int)
+
+ /**
+ * If the type is TYPE_INT, gets the default value. Otherwise, undefined behavior!
+ */
+ external fun getIntDefaultValue(): Int
+
+ /**
+ * If the type is TYPE_DOUBLE, gets the current value. Otherwise, undefined behavior!
+ */
+ external fun getDoubleValue(): Double
+
+ /**
+ * If the type is TYPE_DOUBLE, sets the current value. Otherwise, undefined behavior!
+ */
+ external fun setDoubleValue(value: Double)
+
+ /**
+ * If the type is TYPE_DOUBLE, gets the default value. Otherwise, undefined behavior!
+ */
+ external fun getDoubleDefaultValue(): Double
+
+ /**
+ * If the type is TYPE_DOUBLE, returns the minimum valid value. Otherwise, undefined behavior!
+ */
+ external fun getDoubleMin(): Double
+
+ /**
+ * If the type is TYPE_DOUBLE, returns the maximum valid value. Otherwise, undefined behavior!
+ */
+ external fun getDoubleMax(): Double
+
+ /**
+ * If the type is TYPE_BOOLEAN, gets the current value. Otherwise, undefined behavior!
+ */
+ external fun getBooleanValue(): Boolean
+
+ /**
+ * If the type is TYPE_BOOLEAN, sets the current value. Otherwise, undefined behavior!
+ */
+ external fun setBooleanValue(value: Boolean)
+
+ /**
+ * If the type is TYPE_BOOLEAN, gets the default value. Otherwise, undefined behavior!
+ */
+ external fun getBooleanDefaultValue(): Boolean
+
+ companion object {
+ const val TYPE_INT = 0
+ const val TYPE_DOUBLE = 1
+ const val TYPE_BOOLEAN = 2
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.java
deleted file mode 100644
index f02de2e3f3..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.java
+++ /dev/null
@@ -1,70 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model.view;
-
-import android.content.Context;
-
-import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface;
-import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
-import org.dolphinemu.dolphinemu.features.settings.model.Settings;
-import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting;
-
-public class InputDeviceSetting extends StringSingleChoiceSetting
-{
- private final EmulatedController mController;
-
- public InputDeviceSetting(Context context, int titleId, int descriptionId,
- EmulatedController controller)
- {
- super(context, null, titleId, descriptionId, null, null, null);
-
- mController = controller;
-
- refreshChoicesAndValues();
- }
-
- @Override
- public String getSelectedChoice()
- {
- return mController.getDefaultDevice();
- }
-
- @Override
- public String getSelectedValue()
- {
- return mController.getDefaultDevice();
- }
-
- @Override
- public void setSelectedValue(Settings settings, String newValue)
- {
- mController.setDefaultDevice(newValue);
- }
-
- @Override
- public void refreshChoicesAndValues()
- {
- String[] devices = ControllerInterface.getAllDeviceStrings();
-
- setChoices(devices);
- setValues(devices);
- }
-
- @Override
- public boolean isEditable()
- {
- return true;
- }
-
- @Override
- public boolean canClear()
- {
- return true;
- }
-
- @Override
- public void clear(Settings settings)
- {
- setSelectedValue(settings, "");
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.kt
new file mode 100644
index 0000000000..c98af542c7
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputDeviceSetting.kt
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model.view
+
+import android.content.Context
+import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface
+import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController
+import org.dolphinemu.dolphinemu.features.settings.model.Settings
+import org.dolphinemu.dolphinemu.features.settings.model.view.StringSingleChoiceSetting
+
+class InputDeviceSetting(
+ context: Context,
+ titleId: Int,
+ descriptionId: Int,
+ private val controller: EmulatedController
+) : StringSingleChoiceSetting(context, null, titleId, descriptionId, null, null, null) {
+ init {
+ refreshChoicesAndValues()
+ }
+
+ override val selectedChoice: String
+ get() = controller.getDefaultDevice()
+
+ override val selectedValue: String
+ get() = controller.getDefaultDevice()
+
+ override fun setSelectedValue(settings: Settings, selection: String) =
+ controller.setDefaultDevice(selection)
+
+ override fun refreshChoicesAndValues() {
+ val devices = ControllerInterface.getAllDeviceStrings()
+
+ choices = devices
+ values = devices
+ }
+
+ override val isEditable: Boolean = true
+
+ override fun canClear(): Boolean = true
+
+ override fun clear(settings: Settings) = setSelectedValue(settings, "")
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java
deleted file mode 100644
index 86379bf5a6..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.java
+++ /dev/null
@@ -1,71 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.model.view;
-
-import org.dolphinemu.dolphinemu.features.input.model.controlleremu.Control;
-import org.dolphinemu.dolphinemu.features.input.model.controlleremu.ControlReference;
-import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController;
-import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting;
-import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem;
-
-public final class InputMappingControlSetting extends SettingsItem
-{
- private final ControlReference mControlReference;
- private final EmulatedController mController;
-
- public InputMappingControlSetting(Control control, EmulatedController controller)
- {
- super(control.getUiName(), "");
- mControlReference = control.getControlReference();
- mController = controller;
- }
-
- public String getValue()
- {
- return mControlReference.getExpression();
- }
-
- public void setValue(String expr)
- {
- mControlReference.setExpression(expr);
- mController.updateSingleControlReference(mControlReference);
- }
-
- public void clearValue()
- {
- setValue("");
- }
-
- @Override
- public int getType()
- {
- return TYPE_INPUT_MAPPING_CONTROL;
- }
-
- @Override
- public AbstractSetting getSetting()
- {
- return null;
- }
-
- @Override
- public boolean isEditable()
- {
- return true;
- }
-
- public EmulatedController getController()
- {
- return mController;
- }
-
- public ControlReference getControlReference()
- {
- return mControlReference;
- }
-
- public boolean isInput()
- {
- return mControlReference.isInput();
- }
-}
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
new file mode 100644
index 0000000000..26a86a9411
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/model/view/InputMappingControlSetting.kt
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.model.view
+
+import org.dolphinemu.dolphinemu.features.input.model.controlleremu.Control
+import org.dolphinemu.dolphinemu.features.input.model.controlleremu.EmulatedController
+import org.dolphinemu.dolphinemu.features.settings.model.AbstractSetting
+import org.dolphinemu.dolphinemu.features.settings.model.view.SettingsItem
+
+class InputMappingControlSetting(var control: Control, val controller: EmulatedController) :
+ SettingsItem(control.getUiName(), "") {
+ val controlReference get() = control.getControlReference()
+
+ var value: String
+ get() = controlReference.getExpression()
+ set(expr) {
+ controlReference.setExpression(expr)
+ controller.updateSingleControlReference(controlReference)
+ }
+
+ fun clearValue() {
+ value = ""
+ }
+
+ override val type: Int = TYPE_INPUT_MAPPING_CONTROL
+
+ override val setting: AbstractSetting? = null
+
+ override val isEditable: Boolean = true
+
+ val isInput: Boolean
+ get() = controlReference.isInput()
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlAdapter.java
deleted file mode 100644
index 0ad589fe40..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlAdapter.java
+++ /dev/null
@@ -1,55 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.ui;
-
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.dolphinemu.dolphinemu.databinding.ListItemAdvancedMappingControlBinding;
-
-import java.util.function.Consumer;
-
-public final class AdvancedMappingControlAdapter
- extends RecyclerView.Adapter<AdvancedMappingControlViewHolder>
-{
- private final Consumer<String> mOnClickCallback;
-
- private String[] mControls = new String[0];
-
- public AdvancedMappingControlAdapter(Consumer<String> onClickCallback)
- {
- mOnClickCallback = onClickCallback;
- }
-
- @NonNull @Override
- public AdvancedMappingControlViewHolder onCreateViewHolder(@NonNull ViewGroup parent,
- int viewType)
- {
- LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-
- ListItemAdvancedMappingControlBinding binding =
- ListItemAdvancedMappingControlBinding.inflate(inflater);
- return new AdvancedMappingControlViewHolder(binding, mOnClickCallback);
- }
-
- @Override
- public void onBindViewHolder(@NonNull AdvancedMappingControlViewHolder holder, int position)
- {
- holder.bind(mControls[position]);
- }
-
- @Override
- public int getItemCount()
- {
- return mControls.length;
- }
-
- public void setControls(String[] controls)
- {
- mControls = controls;
- notifyDataSetChanged();
- }
-}
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
new file mode 100644
index 0000000000..1c82616726
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlAdapter.kt
@@ -0,0 +1,33 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.ui
+
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import org.dolphinemu.dolphinemu.databinding.ListItemAdvancedMappingControlBinding
+import java.util.function.Consumer
+
+class AdvancedMappingControlAdapter(private val onClickCallback: Consumer<String>) :
+ RecyclerView.Adapter<AdvancedMappingControlViewHolder>() {
+ private var controls = emptyArray<String>()
+
+ override fun onCreateViewHolder(
+ parent: ViewGroup,
+ viewType: Int
+ ): AdvancedMappingControlViewHolder {
+ val inflater = LayoutInflater.from(parent.context)
+ val binding = ListItemAdvancedMappingControlBinding.inflate(inflater)
+ return AdvancedMappingControlViewHolder(binding, onClickCallback)
+ }
+
+ override fun onBindViewHolder(holder: AdvancedMappingControlViewHolder, position: Int) =
+ holder.bind(controls[position])
+
+ override fun getItemCount(): Int = controls.size
+
+ fun setControls(controls: Array<String>) {
+ this.controls = controls
+ notifyDataSetChanged()
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlViewHolder.java
deleted file mode 100644
index 2ce925235b..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlViewHolder.java
+++ /dev/null
@@ -1,34 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.ui;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.dolphinemu.dolphinemu.databinding.ListItemAdvancedMappingControlBinding;
-
-import java.util.function.Consumer;
-
-public class AdvancedMappingControlViewHolder extends RecyclerView.ViewHolder
-{
- private final ListItemAdvancedMappingControlBinding mBinding;
-
- private String mName;
-
- public AdvancedMappingControlViewHolder(@NonNull ListItemAdvancedMappingControlBinding binding,
- Consumer<String> onClickCallback)
- {
- super(binding.getRoot());
-
- mBinding = binding;
-
- binding.getRoot().setOnClickListener(view -> onClickCallback.accept(mName));
- }
-
- public void bind(String name)
- {
- mName = name;
-
- mBinding.textName.setText(name);
- }
-}
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
new file mode 100644
index 0000000000..dbfecfc94d
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/AdvancedMappingControlViewHolder.kt
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.ui
+
+import androidx.recyclerview.widget.RecyclerView
+import org.dolphinemu.dolphinemu.databinding.ListItemAdvancedMappingControlBinding
+import java.util.function.Consumer
+
+class AdvancedMappingControlViewHolder(
+ private val binding: ListItemAdvancedMappingControlBinding,
+ onClickCallback: Consumer<String>
+) : RecyclerView.ViewHolder(binding.root) {
+ private lateinit var name: String
+
+ init {
+ binding.root.setOnClickListener { onClickCallback.accept(name) }
+ }
+
+ fun bind(name: String) {
+ this.name = name
+ binding.textName.text = name
+ }
+}
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<String> 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<String> 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<String> = 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<CoreDevice.Control>) =
+ 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()
+ }
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java
deleted file mode 100644
index 9872bdd01e..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.java
+++ /dev/null
@@ -1,99 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.ui;
-
-import android.app.Activity;
-import android.view.InputDevice;
-import android.view.KeyEvent;
-import android.view.MotionEvent;
-
-import androidx.annotation.NonNull;
-import androidx.appcompat.app.AlertDialog;
-
-import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface;
-import org.dolphinemu.dolphinemu.features.input.model.MappingCommon;
-import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting;
-
-/**
- * {@link AlertDialog} derivative that listens for
- * motion events from controllers and joysticks.
- */
-public final class MotionAlertDialog extends AlertDialog
-{
- private final Activity mActivity;
- private final InputMappingControlSetting mSetting;
- private final boolean mAllDevices;
- private boolean mRunning = false;
-
- /**
- * Constructor
- *
- * @param activity The current {@link Activity}.
- * @param setting The setting to show this dialog for.
- * @param allDevices Whether to detect inputs from devices other than the configured one.
- */
- public MotionAlertDialog(Activity activity, InputMappingControlSetting setting,
- boolean allDevices)
- {
- super(activity);
-
- mActivity = activity;
- mSetting = setting;
- mAllDevices = allDevices;
- }
-
- @Override
- protected void onStart()
- {
- super.onStart();
-
- mRunning = true;
- new Thread(() ->
- {
- String result = MappingCommon.detectInput(mSetting.getController(), mAllDevices);
- mActivity.runOnUiThread(() ->
- {
- if (mRunning)
- {
- mSetting.setValue(result);
- dismiss();
- }
- });
- }).start();
- }
-
- @Override
- protected void onStop()
- {
- super.onStop();
- mRunning = false;
- }
-
- @Override
- public boolean dispatchKeyEvent(KeyEvent event)
- {
- ControllerInterface.dispatchKeyEvent(event);
-
- if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.isLongPress())
- {
- // Special case: Let the user cancel by long-pressing Back (intended for non-touch devices)
- mSetting.clearValue();
- dismiss();
- }
-
- return true;
- }
-
- @Override
- public boolean dispatchGenericMotionEvent(@NonNull MotionEvent event)
- {
- if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0)
- {
- // Special case: Let the user cancel by touching an on-screen button
- return super.dispatchGenericMotionEvent(event);
- }
-
- ControllerInterface.dispatchGenericMotionEvent(event);
- return true;
- }
-}
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
new file mode 100644
index 0000000000..ff4a278f4c
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/MotionAlertDialog.kt
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.ui
+
+import android.app.Activity
+import android.view.InputDevice
+import android.view.KeyEvent
+import android.view.MotionEvent
+import androidx.appcompat.app.AlertDialog
+import org.dolphinemu.dolphinemu.features.input.model.ControllerInterface
+import org.dolphinemu.dolphinemu.features.input.model.MappingCommon
+import org.dolphinemu.dolphinemu.features.input.model.view.InputMappingControlSetting
+
+/**
+ * [AlertDialog] derivative that listens for
+ * motion events from controllers and joysticks.
+ *
+ * @param activity The current [Activity].
+ * @param setting The setting to show this dialog for.
+ * @param allDevices Whether to detect inputs from devices other than the configured one.
+ */
+class MotionAlertDialog(
+ private val activity: Activity,
+ private val setting: InputMappingControlSetting,
+ private val allDevices: Boolean
+) : AlertDialog(activity) {
+ 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()
+ }
+
+ override fun onStop() {
+ super.onStop()
+ running = false
+ }
+
+ override fun dispatchKeyEvent(event: KeyEvent): Boolean {
+ ControllerInterface.dispatchKeyEvent(event)
+ 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()
+ dismiss()
+ }
+ return true
+ }
+
+ override fun dispatchGenericMotionEvent(event: MotionEvent): Boolean {
+ if (event.source and InputDevice.SOURCE_CLASS_POINTER != 0) {
+ // Special case: Let the user cancel by touching an on-screen button
+ return super.dispatchGenericMotionEvent(event)
+ }
+
+ ControllerInterface.dispatchGenericMotionEvent(event)
+ return true
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileAdapter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileAdapter.java
deleted file mode 100644
index 9908d6f5fd..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileAdapter.java
+++ /dev/null
@@ -1,65 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.ui;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-import android.view.ViewGroup;
-
-import androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.dolphinemu.dolphinemu.databinding.ListItemProfileBinding;
-
-public final class ProfileAdapter extends RecyclerView.Adapter<ProfileViewHolder>
-{
- private final Context mContext;
- private final ProfileDialogPresenter mPresenter;
-
- private final String[] mStockProfileNames;
- private final String[] mUserProfileNames;
-
- public ProfileAdapter(Context context, ProfileDialogPresenter presenter)
- {
- mContext = context;
- mPresenter = presenter;
-
- mStockProfileNames = presenter.getProfileNames(true);
- mUserProfileNames = presenter.getProfileNames(false);
- }
-
- @NonNull @Override
- public ProfileViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType)
- {
- LayoutInflater inflater = LayoutInflater.from(parent.getContext());
-
- ListItemProfileBinding binding = ListItemProfileBinding.inflate(inflater, parent, false);
- return new ProfileViewHolder(mPresenter, binding);
- }
-
- @Override
- public void onBindViewHolder(@NonNull ProfileViewHolder holder, int position)
- {
- if (position < mStockProfileNames.length)
- {
- holder.bind(mStockProfileNames[position], true);
- return;
- }
-
- position -= mStockProfileNames.length;
-
- if (position < mUserProfileNames.length)
- {
- holder.bind(mUserProfileNames[position], false);
- return;
- }
-
- holder.bindAsEmpty(mContext);
- }
-
- @Override
- public int getItemCount()
- {
- return mStockProfileNames.length + mUserProfileNames.length + 1;
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileAdapter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileAdapter.kt
new file mode 100644
index 0000000000..71aa166cc9
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileAdapter.kt
@@ -0,0 +1,42 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.ui
+
+import android.content.Context
+import android.view.LayoutInflater
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import org.dolphinemu.dolphinemu.databinding.ListItemProfileBinding
+
+class ProfileAdapter(
+ private val context: Context,
+ private val presenter: ProfileDialogPresenter
+) : RecyclerView.Adapter<ProfileViewHolder>() {
+ private val stockProfileNames: Array<String> = presenter.getProfileNames(true)
+ private val userProfileNames: Array<String> = presenter.getProfileNames(false)
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ProfileViewHolder {
+ val inflater = LayoutInflater.from(parent.context)
+ val binding = ListItemProfileBinding.inflate(inflater, parent, false)
+ return ProfileViewHolder(presenter, binding)
+ }
+
+ override fun onBindViewHolder(holder: ProfileViewHolder, position: Int) {
+ var profilePosition = position
+ if (profilePosition < stockProfileNames.size) {
+ holder.bind(stockProfileNames[profilePosition], true)
+ return
+ }
+
+ profilePosition -= stockProfileNames.size
+
+ if (profilePosition < userProfileNames.size) {
+ holder.bind(userProfileNames[profilePosition], false)
+ return
+ }
+
+ holder.bindAsEmpty(context)
+ }
+
+ override fun getItemCount(): Int = stockProfileNames.size + userProfileNames.size + 1
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialog.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialog.kt
index acc3f605a9..4ef3dae51f 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialog.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialog.kt
@@ -16,13 +16,13 @@ import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag
import org.dolphinemu.dolphinemu.utils.SerializableHelper.serializable
class ProfileDialog : BottomSheetDialogFragment() {
- private var presenter: ProfileDialogPresenter? = null
+ private lateinit var presenter: ProfileDialogPresenter
private var _binding: DialogInputProfilesBinding? = null
private val binding get() = _binding!!
override fun onCreate(savedInstanceState: Bundle?) {
- val menuTag = requireArguments().serializable<MenuTag>(KEY_MENU_TAG)
+ val menuTag = requireArguments().serializable<MenuTag>(KEY_MENU_TAG)!!
presenter = ProfileDialogPresenter(this, menuTag)
@@ -39,7 +39,7 @@ class ProfileDialog : BottomSheetDialogFragment() {
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
- binding.profileList.adapter = ProfileAdapter(context, presenter)
+ binding.profileList.adapter = ProfileAdapter(requireContext(), presenter)
binding.profileList.layoutManager = LinearLayoutManager(context)
val divider = MaterialDividerItemDecoration(requireActivity(), LinearLayoutManager.VERTICAL)
divider.isLastItemDecorated = false
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialogPresenter.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialogPresenter.java
deleted file mode 100644
index 667cc9017c..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialogPresenter.java
+++ /dev/null
@@ -1,157 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.ui;
-
-import android.content.Context;
-import android.view.LayoutInflater;
-
-import androidx.annotation.NonNull;
-import androidx.fragment.app.DialogFragment;
-
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import com.google.android.material.textfield.TextInputEditText;
-
-import org.dolphinemu.dolphinemu.R;
-import org.dolphinemu.dolphinemu.databinding.DialogInputStringBinding;
-import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag;
-import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView;
-import org.dolphinemu.dolphinemu.utils.DirectoryInitialization;
-
-import java.io.File;
-import java.text.Collator;
-import java.util.Arrays;
-
-public final class ProfileDialogPresenter
-{
- private static final String EXTENSION = ".ini";
-
- private final Context mContext;
- private final DialogFragment mDialog;
- private final MenuTag mMenuTag;
-
- public ProfileDialogPresenter(MenuTag menuTag)
- {
- mContext = null;
- mDialog = null;
- mMenuTag = menuTag;
- }
-
- public ProfileDialogPresenter(DialogFragment dialog, MenuTag menuTag)
- {
- mContext = dialog.getContext();
- mDialog = dialog;
- mMenuTag = menuTag;
- }
-
- public String[] getProfileNames(boolean stock)
- {
- File[] profiles = new File(getProfileDirectoryPath(stock)).listFiles(
- file -> !file.isDirectory() && file.getName().endsWith(EXTENSION));
-
- if (profiles == null)
- return new String[0];
-
- return Arrays.stream(profiles)
- .map(file -> file.getName().substring(0, file.getName().length() - EXTENSION.length()))
- .sorted(Collator.getInstance())
- .toArray(String[]::new);
- }
-
- public void loadProfile(@NonNull String profileName, boolean stock)
- {
- new MaterialAlertDialogBuilder(mContext)
- .setMessage(mContext.getString(R.string.input_profile_confirm_load, profileName))
- .setPositiveButton(R.string.yes, (dialogInterface, i) ->
- {
- mMenuTag.getCorrespondingEmulatedController()
- .loadProfile(getProfilePath(profileName, stock));
- ((SettingsActivityView) mDialog.requireActivity()).onControllerSettingsChanged();
- mDialog.dismiss();
- })
- .setNegativeButton(R.string.no, null)
- .show();
- }
-
- public void saveProfile(@NonNull String profileName)
- {
- // If the user is saving over an existing profile, we should show an overwrite warning.
- // If the user is creating a new profile, we normally shouldn't show a warning,
- // but if they've entered the name of an existing profile, we should shown an overwrite warning.
-
- String profilePath = getProfilePath(profileName, false);
- if (!new File(profilePath).exists())
- {
- mMenuTag.getCorrespondingEmulatedController().saveProfile(profilePath);
- mDialog.dismiss();
- }
- else
- {
- new MaterialAlertDialogBuilder(mContext)
- .setMessage(mContext.getString(R.string.input_profile_confirm_save, profileName))
- .setPositiveButton(R.string.yes, (dialogInterface, i) ->
- {
- mMenuTag.getCorrespondingEmulatedController().saveProfile(profilePath);
- mDialog.dismiss();
- })
- .setNegativeButton(R.string.no, null)
- .show();
- }
- }
-
- public void saveProfileAndPromptForName()
- {
- LayoutInflater inflater = LayoutInflater.from(mContext);
-
- DialogInputStringBinding binding = DialogInputStringBinding.inflate(inflater);
- TextInputEditText input = binding.input;
-
- new MaterialAlertDialogBuilder(mContext)
- .setView(binding.getRoot())
- .setPositiveButton(R.string.ok, (dialogInterface, i) ->
- saveProfile(input.getText().toString()))
- .setNegativeButton(R.string.cancel, null)
- .show();
- }
-
- public void deleteProfile(@NonNull String profileName)
- {
- new MaterialAlertDialogBuilder(mContext)
- .setMessage(mContext.getString(R.string.input_profile_confirm_delete, profileName))
- .setPositiveButton(R.string.yes, (dialogInterface, i) ->
- {
- new File(getProfilePath(profileName, false)).delete();
- mDialog.dismiss();
- })
- .setNegativeButton(R.string.no, null)
- .show();
- }
-
- private String getProfileDirectoryName()
- {
- if (mMenuTag.isGCPadMenu())
- return "GCPad";
- else if (mMenuTag.isWiimoteMenu())
- return "Wiimote";
- else
- throw new UnsupportedOperationException();
- }
-
- private String getProfileDirectoryPath(boolean stock)
- {
- if (stock)
- {
- return DirectoryInitialization.getSysDirectory() + "/Profiles/" + getProfileDirectoryName() +
- '/';
- }
- else
- {
- return DirectoryInitialization.getUserDirectory() + "/Config/Profiles/" +
- getProfileDirectoryName() + '/';
- }
- }
-
- private String getProfilePath(String profileName, boolean stock)
- {
- return getProfileDirectoryPath(stock) + profileName + EXTENSION;
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialogPresenter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialogPresenter.kt
new file mode 100644
index 0000000000..624441aa73
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileDialogPresenter.kt
@@ -0,0 +1,119 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.ui
+
+import android.content.Context
+import android.content.DialogInterface
+import android.view.LayoutInflater
+import androidx.fragment.app.DialogFragment
+import com.google.android.material.dialog.MaterialAlertDialogBuilder
+import org.dolphinemu.dolphinemu.R
+import org.dolphinemu.dolphinemu.databinding.DialogInputStringBinding
+import org.dolphinemu.dolphinemu.features.settings.ui.MenuTag
+import org.dolphinemu.dolphinemu.features.settings.ui.SettingsActivityView
+import org.dolphinemu.dolphinemu.utils.DirectoryInitialization
+import java.io.File
+import java.util.Locale
+
+class ProfileDialogPresenter {
+ private val context: Context?
+ private val dialog: DialogFragment?
+ private val menuTag: MenuTag
+
+ constructor(menuTag: MenuTag) {
+ context = null
+ dialog = null
+ this.menuTag = menuTag
+ }
+
+ constructor(dialog: DialogFragment, menuTag: MenuTag) {
+ context = dialog.context
+ this.dialog = dialog
+ this.menuTag = menuTag
+ }
+
+ fun getProfileNames(stock: Boolean): Array<String> {
+ val profiles = File(getProfileDirectoryPath(stock)).listFiles { file: File ->
+ !file.isDirectory && file.name.endsWith(EXTENSION)
+ } ?: return emptyArray()
+
+ return profiles.map { it.name.substring(0, it.name.length - EXTENSION.length) }
+ .sortedBy { it.lowercase(Locale.getDefault()) }
+ .toTypedArray()
+ }
+
+ fun loadProfile(profileName: String, stock: Boolean) {
+ MaterialAlertDialogBuilder(context!!)
+ .setMessage(context.getString(R.string.input_profile_confirm_load, profileName))
+ .setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int ->
+ menuTag.correspondingEmulatedController
+ .loadProfile(getProfilePath(profileName, stock))
+ (dialog!!.requireActivity() as SettingsActivityView).onControllerSettingsChanged()
+ dialog.dismiss()
+ }
+ .setNegativeButton(R.string.no, null)
+ .show()
+ }
+
+ fun saveProfile(profileName: String) {
+ // If the user is saving over an existing profile, we should show an overwrite warning.
+ // If the user is creating a new profile, we normally shouldn't show a warning,
+ // but if they've entered the name of an existing profile, we should shown an overwrite warning.
+ val profilePath = getProfilePath(profileName, false)
+ if (!File(profilePath).exists()) {
+ menuTag.correspondingEmulatedController.saveProfile(profilePath)
+ dialog!!.dismiss()
+ } else {
+ MaterialAlertDialogBuilder(context!!)
+ .setMessage(context.getString(R.string.input_profile_confirm_save, profileName))
+ .setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int ->
+ menuTag.correspondingEmulatedController.saveProfile(profilePath)
+ dialog!!.dismiss()
+ }
+ .setNegativeButton(R.string.no, null)
+ .show()
+ }
+ }
+
+ fun saveProfileAndPromptForName() {
+ val inflater = LayoutInflater.from(context)
+ val binding = DialogInputStringBinding.inflate(inflater)
+ val input = binding.input
+
+ MaterialAlertDialogBuilder(context!!)
+ .setView(binding.root)
+ .setPositiveButton(android.R.string.ok) { _: DialogInterface?, _: Int ->
+ saveProfile(input.text.toString())
+ }
+ .setNegativeButton(android.R.string.cancel, null)
+ .show()
+ }
+
+ fun deleteProfile(profileName: String) {
+ MaterialAlertDialogBuilder(context!!)
+ .setMessage(context.getString(R.string.input_profile_confirm_delete, profileName))
+ .setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int ->
+ File(getProfilePath(profileName, false)).delete()
+ dialog!!.dismiss()
+ }
+ .setNegativeButton(R.string.no, null)
+ .show()
+ }
+
+ private val profileDirectoryName: String
+ get() = if (menuTag.isGCPadMenu) "GCPad" else if (menuTag.isWiimoteMenu) "Wiimote" else throw UnsupportedOperationException()
+
+ private fun getProfileDirectoryPath(stock: Boolean): String =
+ if (stock) {
+ "${DirectoryInitialization.getSysDirectory()}/Profiles/$profileDirectoryName/"
+ } else {
+ "${DirectoryInitialization.getUserDirectory()}/Config/Profiles/$profileDirectoryName/"
+ }
+
+ private fun getProfilePath(profileName: String, stock: Boolean): String =
+ getProfileDirectoryPath(stock) + profileName + EXTENSION
+
+ companion object {
+ private const val EXTENSION = ".ini"
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileViewHolder.java
deleted file mode 100644
index d4d40c0c65..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileViewHolder.java
+++ /dev/null
@@ -1,76 +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 androidx.annotation.NonNull;
-import androidx.recyclerview.widget.RecyclerView;
-
-import org.dolphinemu.dolphinemu.R;
-import org.dolphinemu.dolphinemu.databinding.ListItemProfileBinding;
-
-public class ProfileViewHolder extends RecyclerView.ViewHolder
-{
- private final ProfileDialogPresenter mPresenter;
- private final ListItemProfileBinding mBinding;
-
- private String mProfileName;
- private boolean mStock;
-
- public ProfileViewHolder(@NonNull ProfileDialogPresenter presenter,
- @NonNull ListItemProfileBinding binding)
- {
- super(binding.getRoot());
-
- mPresenter = presenter;
- mBinding = binding;
-
- binding.buttonLoad.setOnClickListener(view -> loadProfile());
- binding.buttonSave.setOnClickListener(view -> saveProfile());
- binding.buttonDelete.setOnClickListener(view -> deleteProfile());
- }
-
- public void bind(String profileName, boolean stock)
- {
- mProfileName = profileName;
- mStock = stock;
-
- mBinding.textName.setText(profileName);
-
- mBinding.buttonLoad.setVisibility(View.VISIBLE);
- mBinding.buttonSave.setVisibility(stock ? View.GONE : View.VISIBLE);
- mBinding.buttonDelete.setVisibility(stock ? View.GONE : View.VISIBLE);
- }
-
- public void bindAsEmpty(Context context)
- {
- mProfileName = null;
- mStock = false;
-
- mBinding.textName.setText(context.getText(R.string.input_profile_new));
-
- mBinding.buttonLoad.setVisibility(View.GONE);
- mBinding.buttonSave.setVisibility(View.VISIBLE);
- mBinding.buttonDelete.setVisibility(View.GONE);
- }
-
- private void loadProfile()
- {
- mPresenter.loadProfile(mProfileName, mStock);
- }
-
- private void saveProfile()
- {
- if (mProfileName == null)
- mPresenter.saveProfileAndPromptForName();
- else
- mPresenter.saveProfile(mProfileName);
- }
-
- private void deleteProfile()
- {
- mPresenter.deleteProfile(mProfileName);
- }
-}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileViewHolder.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileViewHolder.kt
new file mode 100644
index 0000000000..671461c9aa
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/ProfileViewHolder.kt
@@ -0,0 +1,56 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.ui
+
+import android.content.Context
+import android.view.View
+import androidx.recyclerview.widget.RecyclerView
+import org.dolphinemu.dolphinemu.R
+import org.dolphinemu.dolphinemu.databinding.ListItemProfileBinding
+
+class ProfileViewHolder(
+ private val presenter: ProfileDialogPresenter,
+ private val binding: ListItemProfileBinding
+) : RecyclerView.ViewHolder(binding.getRoot()) {
+ private var profileName: String? = null
+ private var stock = false
+
+ init {
+ binding.buttonLoad.setOnClickListener { loadProfile() }
+ binding.buttonSave.setOnClickListener { saveProfile() }
+ binding.buttonDelete.setOnClickListener { deleteProfile() }
+ }
+
+ fun bind(profileName: String, stock: Boolean) {
+ this.profileName = profileName
+ this.stock = stock
+
+ binding.textName.text = profileName
+
+ binding.buttonLoad.visibility = View.VISIBLE
+ binding.buttonSave.visibility = if (stock) View.GONE else View.VISIBLE
+ binding.buttonDelete.visibility = if (stock) View.GONE else View.VISIBLE
+ }
+
+ fun bindAsEmpty(context: Context) {
+ profileName = null
+ stock = false
+
+ binding.textName.text = context.getText(R.string.input_profile_new)
+
+ binding.buttonLoad.visibility = View.GONE
+ binding.buttonSave.visibility = View.VISIBLE
+ binding.buttonDelete.visibility = View.GONE
+ }
+
+ private fun loadProfile() = presenter.loadProfile(profileName!!, stock)
+
+ private fun saveProfile() {
+ if (profileName == null)
+ presenter.saveProfileAndPromptForName()
+ else
+ presenter.saveProfile(profileName!!)
+ }
+
+ private fun deleteProfile() = presenter.deleteProfile(profileName!!)
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.java
deleted file mode 100644
index fd4b92cf95..0000000000
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.java
+++ /dev/null
@@ -1,77 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-
-package org.dolphinemu.dolphinemu.features.input.ui.viewholder;
-
-import android.view.View;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-
-import org.dolphinemu.dolphinemu.databinding.ListItemMappingBinding;
-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;
-
-public final class InputMappingControlSettingViewHolder extends SettingViewHolder
-{
- private InputMappingControlSetting mItem;
-
- private final ListItemMappingBinding mBinding;
-
- public InputMappingControlSettingViewHolder(@NonNull ListItemMappingBinding binding,
- SettingsAdapter adapter)
- {
- super(binding.getRoot(), adapter);
- mBinding = binding;
- }
-
- @Override
- public void bind(SettingsItem item)
- {
- mItem = (InputMappingControlSetting) item;
-
- mBinding.textSettingName.setText(mItem.getName());
- mBinding.textSettingDescription.setText(mItem.getValue());
- mBinding.buttonAdvancedSettings.setOnClickListener(this::onLongClick);
-
- setStyle(mBinding.textSettingName, mItem);
- }
-
- @Override
- public void onClick(View clicked)
- {
- if (!mItem.isEditable())
- {
- showNotRuntimeEditableError();
- return;
- }
-
- if (mItem.isInput())
- getAdapter().onInputMappingClick(mItem, getBindingAdapterPosition());
- else
- getAdapter().onAdvancedInputMappingClick(mItem, getBindingAdapterPosition());
-
- setStyle(mBinding.textSettingName, mItem);
- }
-
- @Override
- public boolean onLongClick(View clicked)
- {
- if (!mItem.isEditable())
- {
- showNotRuntimeEditableError();
- return true;
- }
-
- getAdapter().onAdvancedInputMappingClick(mItem, getBindingAdapterPosition());
-
- return true;
- }
-
- @Nullable @Override
- protected SettingsItem getItem()
- {
- return mItem;
- }
-}
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
new file mode 100644
index 0000000000..5bbcbf07c4
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/input/ui/viewholder/InputMappingControlSettingViewHolder.kt
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+package org.dolphinemu.dolphinemu.features.input.ui.viewholder
+
+import android.view.View
+import org.dolphinemu.dolphinemu.databinding.ListItemMappingBinding
+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
+
+class InputMappingControlSettingViewHolder(
+ private val binding: ListItemMappingBinding,
+ adapter: SettingsAdapter
+) : SettingViewHolder(binding.getRoot(), adapter) {
+ lateinit var setting: InputMappingControlSetting
+
+ override val item: SettingsItem
+ get() = setting
+
+ override fun bind(item: SettingsItem) {
+ setting = item as InputMappingControlSetting
+
+ binding.textSettingName.text = setting.name
+ binding.textSettingDescription.text = setting.value
+ binding.buttonAdvancedSettings.setOnClickListener { clicked: View -> onLongClick(clicked) }
+
+ setStyle(binding.textSettingName, setting)
+ }
+
+ override fun onClick(clicked: View) {
+ if (!setting.isEditable) {
+ showNotRuntimeEditableError()
+ return
+ }
+
+ if (setting.isInput)
+ adapter.onInputMappingClick(setting, bindingAdapterPosition)
+ else
+ adapter.onAdvancedInputMappingClick(setting, bindingAdapterPosition)
+
+ setStyle(binding.textSettingName, setting)
+ }
+
+ override fun onLongClick(clicked: View): Boolean {
+ if (!setting.isEditable) {
+ showNotRuntimeEditableError()
+ return true
+ }
+
+ adapter.onAdvancedInputMappingClick(setting, bindingAdapterPosition)
+
+ return true
+ }
+}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/PostProcessing.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/PostProcessing.kt
index 1bb1aa04d7..04fe2734bb 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/PostProcessing.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/PostProcessing.kt
@@ -4,14 +4,14 @@ package org.dolphinemu.dolphinemu.features.settings.model
object PostProcessing {
@JvmStatic
- val shaderList: Array<String?>
+ val shaderList: Array<String>
external get
@JvmStatic
- val anaglyphShaderList: Array<String?>
+ val anaglyphShaderList: Array<String>
external get
@JvmStatic
- val passiveShaderList: Array<String?>
+ val passiveShaderList: Array<String>
external get
}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.kt
index 62301e23ef..a67992ce2b 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/model/view/StringSingleChoiceSetting.kt
@@ -17,9 +17,9 @@ open class StringSingleChoiceSetting : SettingsItem {
override val setting: AbstractSetting?
get() = stringSetting
- var choices: Array<String?>?
+ var choices: Array<String>?
protected set
- var values: Array<String?>?
+ var values: Array<String>?
protected set
val menuTag: MenuTag?
var noChoicesAvailableString = 0
@@ -37,8 +37,8 @@ open class StringSingleChoiceSetting : SettingsItem {
setting: AbstractStringSetting?,
titleId: Int,
descriptionId: Int,
- choices: Array<String?>?,
- values: Array<String?>?,
+ choices: Array<String>?,
+ values: Array<String>?,
menuTag: MenuTag? = null
) : super(context, titleId, descriptionId) {
stringSetting = setting
@@ -52,8 +52,8 @@ open class StringSingleChoiceSetting : SettingsItem {
setting: AbstractStringSetting,
titleId: Int,
descriptionId: Int,
- choices: Array<String?>,
- values: Array<String?>,
+ choices: Array<String>,
+ values: Array<String>,
noChoicesAvailableString: Int
) : this(context, setting, titleId, descriptionId, choices, values) {
this.noChoicesAvailableString = noChoicesAvailableString
@@ -102,8 +102,8 @@ open class StringSingleChoiceSetting : SettingsItem {
return -1
}
- open fun setSelectedValue(settings: Settings?, selection: String?) {
- stringSetting!!.setString(settings!!, selection!!)
+ open fun setSelectedValue(settings: Settings, selection: String) {
+ stringSetting!!.setString(settings, selection)
}
open fun refreshChoicesAndValues() {}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.kt
index d5df7c0e09..3623f167bb 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsAdapter.kt
@@ -258,7 +258,7 @@ class SettingsAdapter(
}
fun onInputMappingClick(item: InputMappingControlSetting, position: Int) {
- if (item.controller.defaultDevice.isEmpty() && !fragmentView.isMappingAllDevices) {
+ if (item.controller.getDefaultDevice().isEmpty() && !fragmentView.isMappingAllDevices) {
MaterialAlertDialogBuilder(fragmentView.fragmentActivity)
.setMessage(R.string.input_binding_no_device)
.setPositiveButton(R.string.ok, this)
@@ -474,7 +474,7 @@ class SettingsAdapter(
val value = scSetting.getValueAt(which)
if (scSetting.selectedValue != value) fragmentView.onSettingChanged()
- scSetting.setSelectedValue(settings, value)
+ scSetting.setSelectedValue(settings!!, value!!)
closeDialog()
}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt
index 59858193df..0947e4f294 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/features/settings/ui/SettingsFragmentPresenter.kt
@@ -41,7 +41,7 @@ class SettingsFragmentPresenter(
private val fragmentView: SettingsFragmentView,
private val context: Context
) {
- private var menuTag: MenuTag? = null
+ private lateinit var menuTag: MenuTag
private var gameId: String? = null
private var settingsList: ArrayList<SettingsItem>? = null
@@ -78,7 +78,7 @@ class SettingsFragmentPresenter(
}
}
- fun onViewCreated(menuTag: MenuTag?, settings: Settings?) {
+ fun onViewCreated(menuTag: MenuTag, settings: Settings?) {
this.menuTag = menuTag
if (!TextUtils.isEmpty(gameId)) {
@@ -1339,13 +1339,8 @@ class SettingsFragmentPresenter(
val shaderList =
if (stereoModeValue == anaglyphMode) PostProcessing.anaglyphShaderList else PostProcessing.shaderList
- val shaderListEntries = arrayOfNulls<String>(shaderList.size + 1)
- shaderListEntries[0] = context.getString(R.string.off)
- System.arraycopy(shaderList, 0, shaderListEntries, 1, shaderList.size)
-
- val shaderListValues = arrayOfNulls<String>(shaderList.size + 1)
- shaderListValues[0] = ""
- System.arraycopy(shaderList, 0, shaderListValues, 1, shaderList.size)
+ val shaderListEntries = arrayOf(context.getString(R.string.off), *shaderList)
+ val shaderListValues = arrayOf("", *shaderList)
sl.add(
StringSingleChoiceSetting(
@@ -2153,7 +2148,7 @@ class SettingsFragmentPresenter(
profileString: String,
controllerNumber: Int
) {
- val profiles = ProfileDialogPresenter(menuTag!!).getProfileNames(false)
+ val profiles = ProfileDialogPresenter(menuTag).getProfileNames(false)
val profileKey = profileString + "Profile" + (controllerNumber + 1)
sl.add(
StringSingleChoiceSetting(
@@ -2232,7 +2227,7 @@ class SettingsFragmentPresenter(
0,
0,
true
- ) { fragmentView.showDialogFragment(ProfileDialog.create(menuTag!!)) })
+ ) { fragmentView.showDialogFragment(ProfileDialog.create(menuTag)) })
updateOldControllerSettingsWarningVisibility(controller)
}
@@ -2251,15 +2246,15 @@ class SettingsFragmentPresenter(
) {
updateOldControllerSettingsWarningVisibility(controller)
- val groupCount = controller.groupCount
+ val groupCount = controller.getGroupCount()
for (i in 0 until groupCount) {
val group = controller.getGroup(i)
- val groupType = group.groupType
+ val groupType = group.getGroupType()
if (groupTypeFilter != null && !groupTypeFilter.contains(groupType)) continue
- sl.add(HeaderSetting(group.uiName, ""))
+ sl.add(HeaderSetting(group.getUiName(), ""))
- if (group.defaultEnabledValue != ControlGroup.DEFAULT_ENABLED_ALWAYS) {
+ if (group.getDefaultEnabledValue() != ControlGroup.DEFAULT_ENABLED_ALWAYS) {
sl.add(
SwitchSetting(
context,
@@ -2270,13 +2265,13 @@ class SettingsFragmentPresenter(
)
}
- val controlCount = group.controlCount
+ val controlCount = group.getControlCount()
for (j in 0 until controlCount) {
sl.add(InputMappingControlSetting(group.getControl(j), controller))
}
if (groupType == ControlGroup.TYPE_ATTACHMENTS) {
- val attachmentSetting = group.attachmentSetting
+ val attachmentSetting = group.getAttachmentSetting()
sl.add(
SingleChoiceSetting(
context, InputMappingIntSetting(attachmentSetting),
@@ -2287,7 +2282,7 @@ class SettingsFragmentPresenter(
)
}
- val numericSettingCount = group.numericSettingCount
+ val numericSettingCount = group.getNumericSettingCount()
for (j in 0 until numericSettingCount) {
addNumericSetting(sl, group.getNumericSetting(j))
}
@@ -2295,34 +2290,34 @@ class SettingsFragmentPresenter(
}
private fun addNumericSetting(sl: ArrayList<SettingsItem>, setting: NumericSetting) {
- when (setting.type) {
+ when (setting.getType()) {
NumericSetting.TYPE_DOUBLE -> sl.add(
FloatSliderSetting(
InputMappingDoubleSetting(setting),
- setting.uiName,
+ setting.getUiName(),
"",
- ceil(setting.doubleMin).toInt(),
- floor(setting.doubleMax).toInt(),
- setting.uiSuffix
+ ceil(setting.getDoubleMin()).toInt(),
+ floor(setting.getDoubleMax()).toInt(),
+ setting.getUiSuffix()
)
)
NumericSetting.TYPE_BOOLEAN -> sl.add(
SwitchSetting(
InputMappingBooleanSetting(setting),
- setting.uiName,
- setting.uiDescription
+ setting.getUiName(),
+ setting.getUiDescription()
)
)
}
}
fun updateOldControllerSettingsWarningVisibility() {
- updateOldControllerSettingsWarningVisibility(menuTag!!.correspondingEmulatedController)
+ updateOldControllerSettingsWarningVisibility(menuTag.correspondingEmulatedController)
}
private fun updateOldControllerSettingsWarningVisibility(controller: EmulatedController) {
- val defaultDevice = controller.defaultDevice
+ val defaultDevice = controller.getDefaultDevice()
hasOldControllerSettings = defaultDevice.startsWith("Android/") &&
defaultDevice.endsWith("/Touchscreen")
diff --git a/Source/Android/jni/AndroidCommon/IDCache.cpp b/Source/Android/jni/AndroidCommon/IDCache.cpp
index f83707cebe..968d1b7e82 100644
--- a/Source/Android/jni/AndroidCommon/IDCache.cpp
+++ b/Source/Android/jni/AndroidCommon/IDCache.cpp
@@ -703,21 +703,21 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
const jclass control_class =
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/controlleremu/Control");
s_control_class = reinterpret_cast<jclass>(env->NewGlobalRef(control_class));
- s_control_pointer = env->GetFieldID(control_class, "mPointer", "J");
+ s_control_pointer = env->GetFieldID(control_class, "pointer", "J");
s_control_constructor = env->GetMethodID(control_class, "<init>", "(J)V");
env->DeleteLocalRef(control_class);
const jclass control_group_class =
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlGroup");
s_control_group_class = reinterpret_cast<jclass>(env->NewGlobalRef(control_group_class));
- s_control_group_pointer = env->GetFieldID(control_group_class, "mPointer", "J");
+ s_control_group_pointer = env->GetFieldID(control_group_class, "pointer", "J");
s_control_group_constructor = env->GetMethodID(control_group_class, "<init>", "(J)V");
env->DeleteLocalRef(control_group_class);
const jclass control_reference_class = env->FindClass(
"org/dolphinemu/dolphinemu/features/input/model/controlleremu/ControlReference");
s_control_reference_class = reinterpret_cast<jclass>(env->NewGlobalRef(control_reference_class));
- s_control_reference_pointer = env->GetFieldID(control_reference_class, "mPointer", "J");
+ s_control_reference_pointer = env->GetFieldID(control_reference_class, "pointer", "J");
s_control_reference_constructor = env->GetMethodID(control_reference_class, "<init>", "(J)V");
env->DeleteLocalRef(control_reference_class);
@@ -725,21 +725,21 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
"org/dolphinemu/dolphinemu/features/input/model/controlleremu/EmulatedController");
s_emulated_controller_class =
reinterpret_cast<jclass>(env->NewGlobalRef(emulated_controller_class));
- s_emulated_controller_pointer = env->GetFieldID(emulated_controller_class, "mPointer", "J");
+ s_emulated_controller_pointer = env->GetFieldID(emulated_controller_class, "pointer", "J");
s_emulated_controller_constructor = env->GetMethodID(emulated_controller_class, "<init>", "(J)V");
env->DeleteLocalRef(emulated_controller_class);
const jclass numeric_setting_class =
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/controlleremu/NumericSetting");
s_numeric_setting_class = reinterpret_cast<jclass>(env->NewGlobalRef(numeric_setting_class));
- s_numeric_setting_pointer = env->GetFieldID(numeric_setting_class, "mPointer", "J");
+ s_numeric_setting_pointer = env->GetFieldID(numeric_setting_class, "pointer", "J");
s_numeric_setting_constructor = env->GetMethodID(numeric_setting_class, "<init>", "(J)V");
env->DeleteLocalRef(numeric_setting_class);
const jclass core_device_class =
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/CoreDevice");
s_core_device_class = reinterpret_cast<jclass>(env->NewGlobalRef(core_device_class));
- s_core_device_pointer = env->GetFieldID(core_device_class, "mPointer", "J");
+ s_core_device_pointer = env->GetFieldID(core_device_class, "pointer", "J");
s_core_device_constructor = env->GetMethodID(core_device_class, "<init>", "(J)V");
env->DeleteLocalRef(core_device_class);
@@ -747,7 +747,7 @@ JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved)
env->FindClass("org/dolphinemu/dolphinemu/features/input/model/CoreDevice$Control");
s_core_device_control_class =
reinterpret_cast<jclass>(env->NewGlobalRef(core_device_control_class));
- s_core_device_control_pointer = env->GetFieldID(core_device_control_class, "mPointer", "J");
+ s_core_device_control_pointer = env->GetFieldID(core_device_control_class, "pointer", "J");
s_core_device_control_constructor =
env->GetMethodID(core_device_control_class, "<init>",
"(Lorg/dolphinemu/dolphinemu/features/input/model/CoreDevice;J)V");