Merge pull request #11919 from t895/kotlin-controls

Android: Convert "features.input" package to Kotlin
This commit is contained in:
JosJuice
2023-08-26 19:10:56 +02:00
committed by GitHub
59 changed files with 1827 additions and 2256 deletions
@@ -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;
}
}
@@ -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
}
}
@@ -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);
}
}
}
@@ -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()
}
}
@@ -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();
}
@@ -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>
}
@@ -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;
}
}
@@ -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();
}
@@ -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
}
@@ -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;
}
}
@@ -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
}
@@ -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();
}
}
@@ -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
}
@@ -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;
}
}
@@ -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
}
}
@@ -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;
}
}
@@ -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
}
}
@@ -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;
}
}
@@ -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
}
}

Some files were not shown because too many files have changed in this diff Show More