summaryrefslogtreecommitdiff
path: root/Source/Android/app/src/main/java
diff options
context:
space:
mode:
authorMike <7153163+hackbar@users.noreply.github.com>2017-10-31 22:43:30 -0700
committerMike <7153163+hackbar@users.noreply.github.com>2017-11-07 17:39:56 -0800
commit6787fcb71277e69f43fc32f4b232af916a043c7f (patch)
tree6ad3002ba621b6837633256f12e647eb3bc19a7c /Source/Android/app/src/main/java
parentd9d4bd7eef7dd9af331be1ed3d3e6e737b284178 (diff)
Android: Handle duplicate axis, and fix known bad axis.
Android reports the same physical axis multiple times for analog triggers, and this handles this case. There are also some controllers with broken mappings (eg the analog triggers on a PS4 DualShock 4). These axis don't center correctly. There are also some controllers (again, the PS4) that send both a button press and an axis movement. This ignores the buttons so we can use the analog axis. Otherwise, since the button comes before the axis moves far we would always take the button.
Diffstat (limited to 'Source/Android/app/src/main/java')
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java8
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java35
-rw-r--r--Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ControllerMappingHelper.java62
3 files changed, 96 insertions, 9 deletions
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java
index 2adb2f209d..4688cbce05 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/activities/EmulationActivity.java
@@ -39,6 +39,7 @@ import org.dolphinemu.dolphinemu.fragments.SaveLoadStateFragment;
import org.dolphinemu.dolphinemu.ui.main.MainPresenter;
import org.dolphinemu.dolphinemu.ui.platform.Platform;
import org.dolphinemu.dolphinemu.utils.Animations;
+import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper;
import org.dolphinemu.dolphinemu.utils.Java_GCAdapter;
import org.dolphinemu.dolphinemu.utils.Java_WiimoteAdapter;
import org.dolphinemu.dolphinemu.utils.Log;
@@ -57,6 +58,7 @@ public final class EmulationActivity extends AppCompatActivity
private EmulationFragment mEmulationFragment;
private SharedPreferences mPreferences;
+ private ControllerMappingHelper mControllerMappingHelper;
// So that MainActivity knows which view to invalidate before the return animation.
private int mPosition;
@@ -164,6 +166,7 @@ public final class EmulationActivity extends AppCompatActivity
mScreenPath = gameToEmulate.getStringExtra("ScreenPath");
mPosition = gameToEmulate.getIntExtra("GridPosition", -1);
mDeviceHasTouchScreen = getPackageManager().hasSystemFeature("android.hardware.touchscreen");
+ mControllerMappingHelper = new ControllerMappingHelper();
int themeId;
if (mDeviceHasTouchScreen)
@@ -729,7 +732,10 @@ public final class EmulationActivity extends AppCompatActivity
for (InputDevice.MotionRange range : motions)
{
- NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), range.getAxis(), event.getAxisValue(range.getAxis()));
+ int axis = range.getAxis();
+ float origValue = event.getAxisValue(axis);
+ float value = mControllerMappingHelper.scaleAxis(input, axis, origValue);
+ NativeLibrary.onGamePadMoveEvent(input.getDescriptor(), axis, value);
}
return true;
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java
index 592aee6838..844d877ac6 100644
--- a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/dialogs/MotionAlertDialog.java
@@ -9,6 +9,7 @@ import android.view.KeyEvent;
import android.view.MotionEvent;
import org.dolphinemu.dolphinemu.model.settings.view.InputBindingSetting;
+import org.dolphinemu.dolphinemu.utils.ControllerMappingHelper;
import org.dolphinemu.dolphinemu.utils.Log;
import java.util.List;
@@ -21,6 +22,7 @@ public final class MotionAlertDialog extends AlertDialog
{
// The selected input preference
private final InputBindingSetting setting;
+ private final ControllerMappingHelper mControllerMappingHelper;
private boolean mWaitingForEvent = true;
/**
@@ -34,6 +36,7 @@ public final class MotionAlertDialog extends AlertDialog
super(context);
this.setting = setting;
+ this.mControllerMappingHelper = new ControllerMappingHelper();
}
public boolean onKeyEvent(int keyCode, KeyEvent event)
@@ -42,8 +45,11 @@ public final class MotionAlertDialog extends AlertDialog
switch (event.getAction())
{
case KeyEvent.ACTION_DOWN:
- saveKeyInput(event);
-
+ if (!mControllerMappingHelper.shouldKeyBeIgnored(event.getDevice(), keyCode))
+ {
+ saveKeyInput(event);
+ }
+ // Even if we ignore the key, we still consume it. Thus return true regardless.
return true;
default:
@@ -69,13 +75,15 @@ public final class MotionAlertDialog extends AlertDialog
{
if ((event.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) == 0)
return false;
-
- Log.debug("[MotionAlertDialog] Received motion event: " + event.getAction());
+ if (event.getAction() != MotionEvent.ACTION_MOVE)
+ return false;
InputDevice input = event.getDevice();
+
List<InputDevice.MotionRange> motionRanges = input.getMotionRanges();
int numMovedAxis = 0;
+ float axisMoveValue = 0.0f;
InputDevice.MotionRange lastMovedRange = null;
char lastMovedDir = '?';
if (mWaitingForEvent)
@@ -84,12 +92,23 @@ public final class MotionAlertDialog extends AlertDialog
for (InputDevice.MotionRange range : motionRanges)
{
int axis = range.getAxis();
- float value = event.getAxisValue(axis);
+ float origValue = event.getAxisValue(axis);
+ float value = mControllerMappingHelper.scaleAxis(input, axis, origValue);
if (Math.abs(value) > 0.5f)
{
- numMovedAxis++;
- lastMovedRange = range;
- lastMovedDir = value < 0.0f ? '-' : '+';
+ // It is common to have multiple axis with the same physical input. For example,
+ // shoulder butters are provided as both AXIS_LTRIGGER and AXIS_BRAKE.
+ // To handle this, we ignore an axis motion that's the exact same as a motion
+ // we already saw. This way, we ignore axis with two names, but catch the case
+ // where a joystick is moved in two directions.
+ // ref: bottom of https://developer.android.com/training/game-controllers/controller-input.html
+ if (value != axisMoveValue)
+ {
+ axisMoveValue = value;
+ numMovedAxis++;
+ lastMovedRange = range;
+ lastMovedDir = value < 0.0f ? '-' : '+';
+ }
}
}
diff --git a/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ControllerMappingHelper.java b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ControllerMappingHelper.java
new file mode 100644
index 0000000000..1cdc07604a
--- /dev/null
+++ b/Source/Android/app/src/main/java/org/dolphinemu/dolphinemu/utils/ControllerMappingHelper.java
@@ -0,0 +1,62 @@
+package org.dolphinemu.dolphinemu.utils;
+
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+/** Some controllers have incorrect mappings. This class has special-case fixes for them. */
+public class ControllerMappingHelper
+{
+ /** Some controllers report extra button presses that can be ignored. */
+ public boolean shouldKeyBeIgnored(InputDevice inputDevice, int keyCode)
+ {
+ if (isDualShock4(inputDevice)) {
+ // The two analog triggers generate analog motion events as well as a keycode.
+ // We always prefer to use the analog values, so throw away the button press
+ // Even though the triggers are L/R2, without mappings they generate L/R1 events.
+ return keyCode == KeyEvent.KEYCODE_BUTTON_L1 || keyCode == KeyEvent.KEYCODE_BUTTON_R1;
+ }
+ return false;
+ }
+
+ /** Scale an axis to be zero-centered with a proper range. */
+ public float scaleAxis(InputDevice inputDevice, int axis, float value)
+ {
+ if (isDualShock4(inputDevice))
+ {
+ // Android doesn't have correct mappings for this controller's triggers. It reports them
+ // as RX & RY, centered at -1.0, and with a range of [-1.0, 1.0]
+ // Scale them to properly zero-centered with a range of [0.0, 1.0].
+ if (axis == MotionEvent.AXIS_RX || axis == MotionEvent.AXIS_RY)
+ {
+ return (value + 1) / 2.0f;
+ }
+ }
+ else if (isXboxOneWireless(inputDevice))
+ {
+ // Same as the DualShock 4, the mappings are missing.
+ if (axis == MotionEvent.AXIS_Z || axis == MotionEvent.AXIS_RZ)
+ {
+ return (value + 1) / 2.0f;
+ }
+ if (axis == MotionEvent.AXIS_GENERIC_1)
+ {
+ // This axis is stuck at ~.5. Ignore it.
+ return 0.0f;
+ }
+ }
+ return value;
+ }
+
+ private boolean isDualShock4(InputDevice inputDevice)
+ {
+ // Sony DualShock 4 controller
+ return inputDevice.getVendorId() == 0x54c && inputDevice.getProductId() == 0x9cc;
+ }
+
+ private boolean isXboxOneWireless(InputDevice inputDevice)
+ {
+ // Microsoft Xbox One controller
+ return inputDevice.getVendorId() == 0x45e && inputDevice.getProductId() == 0x2e0;
+ }
+}