 Chromium Code Reviews
 Chromium Code Reviews Issue 165483003:
  Gamepad API for Android  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master
    
  
    Issue 165483003:
  Gamepad API for Android  (Closed) 
  Base URL: https://chromium.googlesource.com/chromium/src.git@master| Index: content/public/android/java/src/org/chromium/content/browser/input/GamepadDataMapper.java | 
| diff --git a/content/public/android/java/src/org/chromium/content/browser/input/GamepadDataMapper.java b/content/public/android/java/src/org/chromium/content/browser/input/GamepadDataMapper.java | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..b0f344067013163f5a4fdf41d9af8d5854ba3170 | 
| --- /dev/null | 
| +++ b/content/public/android/java/src/org/chromium/content/browser/input/GamepadDataMapper.java | 
| @@ -0,0 +1,198 @@ | 
| +// Copyright 2014 The Chromium Authors. All rights reserved. | 
| +// Use of this source code is governed by a BSD-style license that can be | 
| +// found in the LICENSE file. | 
| + | 
| +package org.chromium.content.browser.input; | 
| + | 
| +import android.util.SparseArray; | 
| +import android.view.KeyEvent; | 
| +import android.view.MotionEvent; | 
| + | 
| +import org.chromium.base.JNINamespace; | 
| +import org.chromium.content.browser.input.gamepad_mapping.CanonicalAxisIndex; | 
| +import org.chromium.content.browser.input.gamepad_mapping.CanonicalButtonIndex; | 
| + | 
| +/** | 
| + * Device specific input data converter for Gamepad API. | 
| + * Implemented per device by subclasses. | 
| + */ | 
| +@JNINamespace("content") | 
| +abstract class GamepadDataMapper { | 
| + protected static final String PS3_SIXAXIS_DEVICE_NAME = "Sony PLAYSTATION(R)3 Controller"; | 
| + | 
| + public abstract WebGamepadData map(SparseArray<Float> axes, SparseArray<Boolean> buttons); | 
| 
Ted C
2014/03/07 20:18:09
per my comment about object creation in the other
 
kbalazs
2014/03/12 00:17:36
Done, I hold the object in the mapper now.
 | 
| + | 
| + // Factory method. | 
| + public static GamepadDataMapper createDataMapper(String deviceName) { | 
| + if (deviceName.equals(PS3_SIXAXIS_DEVICE_NAME)) | 
| + return new PS3SixAxisGamepadDataMapper(); | 
| + | 
| + return new GenericGamepadDataMapper(deviceName); | 
| + } | 
| + | 
| + protected static float axisValue(Float f) { return f == null ? 0 : f.floatValue(); } | 
| + protected static float buttonValue(Boolean b) { return (b == null || !b) ? 0 : 1; } | 
| + | 
| + protected static WebGamepadData createWebGamepadData() { | 
| + WebGamepadData data = new WebGamepadData(); | 
| + data.axes = new float[CanonicalAxisIndex.NUM_CANONICAL_AXES]; | 
| + data.buttons = new float[CanonicalButtonIndex.NUM_CANONICAL_BUTTONS]; | 
| + return data; | 
| + } | 
| + | 
| + protected static void mapCommonXYAxes(WebGamepadData data, SparseArray<Float> axes) { | 
| + Float x = (Float) axes.get(MotionEvent.AXIS_X); | 
| 
Ted C
2014/03/07 20:18:09
get on a typed SparseArray should mean that you do
 
kbalazs
2014/03/07 22:40:22
Right, but unfortunately there is no such thing as
 
Ted C
2014/03/08 01:06:00
My previous comment was that calling .get() on a S
 | 
| + Float y = (Float) axes.get(MotionEvent.AXIS_Y); | 
| + data.axes[CanonicalAxisIndex.AXIS_LEFT_STICK_X] = axisValue(x); | 
| + data.axes[CanonicalAxisIndex.AXIS_LEFT_STICK_Y] = axisValue(y); | 
| + } | 
| + | 
| + protected static void mapCommonTriggerAxes(WebGamepadData data, SparseArray<Float> axes) { | 
| + Float lTrigger = (Float) axes.get(MotionEvent.AXIS_LTRIGGER); | 
| + Float rTrigger = (Float) axes.get(MotionEvent.AXIS_RTRIGGER); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_LEFT_SHOULDER] = axisValue(lTrigger); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_RIGHT_SHOULDER] = axisValue(rTrigger); | 
| + } | 
| + | 
| + protected static void mapCommonTriggerButtons( | 
| + WebGamepadData data, SparseArray<Boolean> buttons) { | 
| + Boolean l1 = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_L1); | 
| + Boolean r1 = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_R1); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_LEFT_TRIGGER] = buttonValue(l1); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_RIGHT_TRIGGER] = buttonValue(r1); | 
| + } | 
| + | 
| + protected static void mapCommonThumbstickButtons( | 
| + WebGamepadData data, SparseArray<Boolean> buttons) { | 
| + Boolean thumbL = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_THUMBL); | 
| + Boolean thumbR = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_THUMBR); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_LEFT_THUMBSTICK] = buttonValue(thumbL); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_RIGHT_THUMBSTICK] = buttonValue(thumbR); | 
| + } | 
| + | 
| + protected static void mapCommonStartSelectButtons( | 
| + WebGamepadData data, SparseArray<Boolean> buttons) { | 
| + Boolean select = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_SELECT); | 
| + Boolean start = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_START); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_BACK_SELECT] = buttonValue(select); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_START] = buttonValue(start); | 
| + } | 
| + | 
| + protected static void mapCommonDpadButtons( | 
| + WebGamepadData data, SparseArray<Boolean> buttons) { | 
| + Boolean dpadDown = (Boolean) buttons.get(KeyEvent.KEYCODE_DPAD_DOWN); | 
| + Boolean dpadUp = (Boolean) buttons.get(KeyEvent.KEYCODE_DPAD_UP); | 
| + Boolean dpadLeft = (Boolean) buttons.get(KeyEvent.KEYCODE_DPAD_LEFT); | 
| + Boolean dpadRight = (Boolean) buttons.get(KeyEvent.KEYCODE_DPAD_RIGHT); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_DPAD_DOWN] = buttonValue(dpadDown); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_DPAD_UP] = buttonValue(dpadUp); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_DPAD_LEFT] = buttonValue(dpadLeft); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_DPAD_RIGHT] = buttonValue(dpadRight); | 
| + } | 
| + | 
| + protected static void mapCommonXYABButtons( | 
| + WebGamepadData data, SparseArray<Boolean> buttons) { | 
| + Boolean bA = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_A); | 
| + Boolean bB = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_B); | 
| + Boolean bX = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_X); | 
| + Boolean bY = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_Y); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_PRIMARY] = buttonValue(bA); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_SECONDARY] = buttonValue(bB); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_TERTIARY] = buttonValue(bX); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_QUATERNARY] = buttonValue(bY); | 
| + } | 
| +} | 
| + | 
| +// This is a last resort if we find a device that we don't know about. | 
| +// The Android API is general enough that this can be better than nothing | 
| +// but we should not really rely on this. | 
| +class GenericGamepadDataMapper extends GamepadDataMapper { | 
| + private final String mDeviceName; | 
| + | 
| + GenericGamepadDataMapper(String deviceName) { | 
| + mDeviceName = deviceName; | 
| + } | 
| + | 
| + // Find something for right stick x and y. If there is even more axes, map them to | 
| + // non-canonical positions. | 
| + private static void mapRightAndExtraSticks(WebGamepadData data, SparseArray<Float> axes) { | 
| + int position = CanonicalAxisIndex.AXIS_RIGHT_STICK_X; | 
| + // position + 1 is AXIS_RIGHT_STICK_Y. | 
| + Float x = (Float) axes.get(MotionEvent.AXIS_RX); | 
| + Float y = (Float) axes.get(MotionEvent.AXIS_RY); | 
| + if (x != null || y != null) { | 
| + data.axes[position++] = axisValue(x); | 
| + data.axes[position++] = axisValue(y); | 
| + } | 
| + x = (Float) axes.get(MotionEvent.AXIS_Z); | 
| + y = (Float) axes.get(MotionEvent.AXIS_RZ); | 
| + if (x != null || y != null) { | 
| + data.axes[position++] = axisValue(x); | 
| + data.axes[position++] = axisValue(y); | 
| + } | 
| + x = (Float) axes.get(MotionEvent.AXIS_HAT_X); | 
| + y = (Float) axes.get(MotionEvent.AXIS_HAT_Y); | 
| + if (x != null || y != null) { | 
| + data.axes[position++] = axisValue(x); | 
| + data.axes[position++] = axisValue(y); | 
| + } | 
| 
Ted C
2014/03/07 20:18:09
This doesn't verify axes size is large enough.  Mi
 
kbalazs
2014/03/07 22:40:22
Right, I overlooked it.
 
kbalazs
2014/03/12 00:17:36
Done.
 | 
| + } | 
| + | 
| + public WebGamepadData map(SparseArray<Float> axes, SparseArray<Boolean> buttons) { | 
| + WebGamepadData data = createWebGamepadData(); | 
| + data.id = mDeviceName + " (STANDARD_GAMEPAD)"; | 
| + data.mapping = "standard"; | 
| + mapCommonXYAxes(data, axes); | 
| + mapRightAndExtraSticks(data, axes); | 
| + mapCommonTriggerAxes(data, axes); | 
| + mapCommonXYABButtons(data, buttons); | 
| + mapCommonTriggerButtons(data, buttons); | 
| + mapCommonThumbstickButtons(data, buttons); | 
| + mapCommonStartSelectButtons(data, buttons); | 
| + mapCommonDpadButtons(data, buttons); | 
| + | 
| + // TODO(b.kelemen): meta is missing. | 
| + | 
| + return data; | 
| + } | 
| +} | 
| + | 
| +class PS3SixAxisGamepadDataMapper extends GamepadDataMapper { | 
| + public WebGamepadData map(SparseArray<Float> axes, SparseArray<Boolean> buttons) { | 
| + WebGamepadData data = createWebGamepadData(); | 
| + data.id = PS3_SIXAXIS_DEVICE_NAME + " (STANDARD_GAMEPAD)"; | 
| 
Ted C
2014/03/07 20:18:09
I would make " (STANDARD_GAMEPAD)" and "standard"
 
kbalazs
2014/03/12 00:17:36
Done.
 | 
| + data.mapping = "standard"; | 
| + | 
| + mapCommonXYAxes(data, axes); | 
| + | 
| + Float z = (Float) axes.get(MotionEvent.AXIS_Z); | 
| + Float rz = (Float) axes.get(MotionEvent.AXIS_RZ); | 
| + data.axes[CanonicalAxisIndex.AXIS_RIGHT_STICK_X] = axisValue(z); | 
| + data.axes[CanonicalAxisIndex.AXIS_RIGHT_STICK_Y] = axisValue(rz); | 
| + | 
| + mapCommonTriggerAxes(data, axes); | 
| + | 
| + Boolean bA = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_A); | 
| + Boolean bB = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_B); | 
| + Boolean bX = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_X); | 
| + Boolean bY = (Boolean) buttons.get(KeyEvent.KEYCODE_BUTTON_Y); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_PRIMARY] = buttonValue(bX); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_SECONDARY] = buttonValue(bY); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_TERTIARY] = buttonValue(bA); | 
| + data.buttons[CanonicalButtonIndex.BUTTON_QUATERNARY] = buttonValue(bB); | 
| + | 
| + mapCommonTriggerButtons(data, buttons); | 
| + mapCommonThumbstickButtons(data, buttons); | 
| + mapCommonStartSelectButtons(data, buttons); | 
| + mapCommonDpadButtons(data, buttons); | 
| + | 
| + // TODO(b.kelemen): PS button is missing. Looks like it is swallowed by Android | 
| + // but probably there is a way to configure otherwise and in this case we should | 
| + // handle it. | 
| + | 
| + return data; | 
| + } | 
| +} | 
| + | 
| +// TODO(b.kelemen): add more implementations. It would be nice to support at least those that are | 
| +// supported on Linux. |