Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 package org.chromium.content.browser.input; | |
| 6 | |
| 7 import android.content.Context; | |
| 8 import android.hardware.input.InputManager; | |
| 9 import android.util.SparseArray; | |
| 10 import android.view.InputDevice; | |
| 11 import android.view.InputDevice.MotionRange; | |
| 12 import android.view.KeyEvent; | |
| 13 import android.view.MotionEvent; | |
| 14 | |
| 15 import org.chromium.base.CalledByNative; | |
| 16 import org.chromium.base.JNINamespace; | |
| 17 import org.chromium.base.ThreadUtils; | |
| 18 | |
| 19 import java.util.List; | |
| 20 | |
| 21 @JNINamespace("content") | |
| 22 class WebGamepadData { | |
| 23 public float[] axes; | |
| 24 public float[] buttons; | |
| 25 } | |
| 26 | |
| 27 /** | |
| 28 * Java counterpart of GamepadPlatformDataFetcherAndroid. | |
| 29 * Manages game input devices and feed Gamepad API with input data. | |
| 30 * GamepadPlatformDataFetcherAndroid is merely a wrepper around this. | |
|
bajones
2014/03/01 00:04:03
Nit: wrepper->wrapper
| |
| 31 * Native callable methods called by GamepadPlatformDataFetcherAndroid on the po ller thread | |
| 32 * which is a native thread without a java looper. Events are processed on the U I thread. | |
| 33 */ | |
| 34 @JNINamespace("content") | |
| 35 public class GamepadAdapter implements InputManager.InputDeviceListener { | |
| 36 private static final int NUM_WEB_GAMEPADS = 4; | |
| 37 | |
| 38 private static GamepadAdapter instance; | |
| 39 | |
| 40 private final InputManager mInputManager; | |
| 41 private final InputDeviceHandler[] mDeviceHandlers; | |
| 42 private boolean mIsActive; | |
| 43 private long mNativeDataFetcher; | |
| 44 private final Object mDeviceHandlersLock = new Object(); | |
| 45 | |
| 46 public static void initialize(Context context) { | |
| 47 if (instance == null) | |
| 48 instance = new GamepadAdapter(context); | |
| 49 } | |
| 50 | |
| 51 public static GamepadAdapter getInstance() { | |
| 52 assert instance != null; | |
| 53 return instance; | |
| 54 } | |
| 55 | |
| 56 private GamepadAdapter(Context context) { | |
| 57 assert ThreadUtils.runningOnUiThread(); | |
| 58 mIsActive = false; | |
| 59 mDeviceHandlers = new InputDeviceHandler[NUM_WEB_GAMEPADS]; | |
| 60 mInputManager = (InputManager) context.getSystemService(Context.INPUT_SE RVICE); | |
| 61 mInputManager.registerInputDeviceListener(this, null); | |
| 62 | |
| 63 // Enumerate devices. | |
| 64 int[] ids = mInputManager.getInputDeviceIds(); | |
| 65 int activeDevices = 0; | |
| 66 for (int i = 0; i < ids.length && activeDevices < NUM_WEB_GAMEPADS; ++i) { | |
| 67 InputDevice device = mInputManager.getInputDevice(ids[i]); | |
| 68 if (isGameDevice(device)) { | |
| 69 mDeviceHandlers[activeDevices++] = new InputDeviceHandler(device ); | |
| 70 } | |
| 71 } | |
| 72 } | |
| 73 | |
| 74 @Override | |
| 75 public void onInputDeviceAdded(int deviceID) { | |
| 76 ThreadUtils.assertOnUiThread(); | |
| 77 InputDevice device = mInputManager.getInputDevice(deviceID); | |
| 78 if (!isGameDevice(device)) | |
| 79 return; | |
| 80 int index = nextAvailableIndex(); | |
| 81 if (index == -1) | |
| 82 return; | |
| 83 synchronized (mDeviceHandlersLock) { | |
| 84 mDeviceHandlers[index] = new InputDeviceHandler(device); | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 @Override | |
| 89 public void onInputDeviceRemoved(int deviceID) { | |
| 90 ThreadUtils.assertOnUiThread(); | |
| 91 int index = indexForDeviceID(deviceID); | |
| 92 if (index == -1) | |
| 93 return; | |
| 94 synchronized (mDeviceHandlersLock) { | |
| 95 mDeviceHandlers[index] = null; | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 @Override | |
| 100 public void onInputDeviceChanged(int deviceID) { | |
| 101 ThreadUtils.assertOnUiThread(); | |
| 102 int index = indexForDeviceID(deviceID); | |
| 103 if (index == -1) { | |
| 104 index = nextAvailableIndex(); | |
| 105 if (index == -1) return; | |
| 106 } | |
| 107 InputDevice device = mInputManager.getInputDevice(deviceID); | |
| 108 synchronized (mDeviceHandlersLock) { | |
| 109 mDeviceHandlers[index] = null; | |
| 110 if (isGameDevice(device)) { | |
| 111 mDeviceHandlers[index] = new InputDeviceHandler(device); | |
| 112 } | |
| 113 } | |
| 114 } | |
| 115 | |
| 116 public boolean handleMotionEvent(MotionEvent event) { | |
| 117 if (!mIsActive) | |
| 118 return false; | |
| 119 InputDeviceHandler handler = handlerForDeviceId(event.getDeviceId()); | |
| 120 if (handler == null) | |
| 121 return false; | |
| 122 if (!isGameEvent(event)) | |
| 123 return false; | |
| 124 handler.handleMotionEvent(event); | |
| 125 return true; | |
| 126 } | |
| 127 | |
| 128 public boolean handleKeyEvent(KeyEvent event) { | |
| 129 if (!mIsActive) | |
| 130 return false; | |
| 131 if (event.getAction() != KeyEvent.ACTION_DOWN && | |
| 132 event.getAction() != KeyEvent.ACTION_UP) { | |
| 133 return false; | |
| 134 } | |
| 135 InputDeviceHandler handler = handlerForDeviceId(event.getDeviceId()); | |
| 136 if (handler == null) | |
| 137 return false; | |
| 138 int keyCode = event.getKeyCode(); | |
| 139 if (!isGameKey(keyCode)) | |
| 140 return false; | |
| 141 boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN; | |
| 142 handler.handleKeyEvent(keyCode, isDown, event.getEventTime()); | |
| 143 return true; | |
| 144 } | |
| 145 | |
| 146 @CalledByNative | |
| 147 public static GamepadAdapter attach(long nativeDataFetcher) { | |
| 148 assert instance != null; | |
| 149 instance.mNativeDataFetcher = nativeDataFetcher; | |
| 150 instance.mIsActive = true; | |
| 151 return instance; | |
| 152 } | |
| 153 | |
| 154 // Called on polling thread. | |
| 155 @CalledByNative | |
| 156 private void getGamepadData() { | |
| 157 synchronized (mDeviceHandlersLock) { | |
| 158 for (int i = 0; i < NUM_WEB_GAMEPADS; ++i) { | |
| 159 if (mDeviceHandlers[i] == null) { | |
| 160 nativeRefreshDevice(mNativeDataFetcher, i, false, null, 0, n ull, null); | |
| 161 } else { | |
| 162 InputDeviceHandler handler = mDeviceHandlers[i]; | |
| 163 WebGamepadData data = handler.produceWebData(); | |
| 164 nativeRefreshDevice(mNativeDataFetcher, i, true, | |
| 165 handler.getInputDevice().getName(), handler.getTimes tamp(), | |
| 166 data.axes, data.buttons); | |
| 167 } | |
| 168 } | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 @CalledByNative | |
| 173 public void setActive(boolean active) { mIsActive = active; } | |
| 174 | |
| 175 int nextAvailableIndex() { | |
| 176 for (int i = 0; i < mDeviceHandlers.length; ++i) { | |
| 177 if (mDeviceHandlers[i] == null) | |
| 178 return i; | |
| 179 } | |
| 180 return -1; | |
| 181 } | |
| 182 | |
| 183 int indexForDeviceID(int deviceID) { | |
| 184 for (int i = 0; i < mDeviceHandlers.length; ++i) { | |
| 185 if (mDeviceHandlers[i] != null && | |
| 186 mDeviceHandlers[i].getInputDevice().getId() == deviceID) | |
| 187 return i; | |
| 188 } | |
| 189 return -1; | |
| 190 } | |
| 191 | |
| 192 InputDeviceHandler handlerForDeviceId(int deviceID) { | |
| 193 int index = indexForDeviceID(deviceID); | |
| 194 return index == -1 ? null : mDeviceHandlers[index]; | |
| 195 } | |
| 196 | |
| 197 private static boolean isGameDevice(InputDevice device) { | |
| 198 return (device.getSources() & InputDevice.SOURCE_JOYSTICK) != 0; | |
| 199 } | |
| 200 | |
| 201 private static boolean isGameEvent(MotionEvent event) { | |
| 202 return (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0 && | |
| 203 event.getAction() == MotionEvent.ACTION_MOVE; | |
| 204 } | |
| 205 | |
| 206 private static boolean isGameKey(int keyCode) { | |
| 207 switch (keyCode) { | |
| 208 case KeyEvent.KEYCODE_DPAD_UP: | |
| 209 case KeyEvent.KEYCODE_DPAD_DOWN: | |
| 210 case KeyEvent.KEYCODE_DPAD_LEFT: | |
| 211 case KeyEvent.KEYCODE_DPAD_RIGHT: | |
| 212 case KeyEvent.KEYCODE_DPAD_CENTER: | |
| 213 return true; | |
| 214 default: | |
| 215 return KeyEvent.isGamepadButton(keyCode); | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 private static class InputDeviceHandler { | |
| 220 private final InputDevice mDevice; | |
| 221 private final SparseArray<Float> mAxes; | |
| 222 private final SparseArray<Boolean> mButtons = new SparseArray<Boolean>() ; | |
| 223 private final GamepadDataMapper mMapper; | |
| 224 private long mTimestamp; | |
| 225 private final Object mLock = new Object(); | |
| 226 | |
| 227 InputDevice getInputDevice() { return mDevice; } | |
| 228 long getTimestamp() { return mTimestamp; } | |
| 229 | |
| 230 public InputDeviceHandler(InputDevice device) { | |
| 231 assert isGameDevice(device); | |
| 232 mDevice = device; | |
| 233 mMapper = GamepadDataMapper.createDataMapper(device.getName()); | |
| 234 List<MotionRange> ranges = device.getMotionRanges(); | |
| 235 mAxes = new SparseArray<Float>(ranges.size()); | |
| 236 for (MotionRange range : ranges) { | |
| 237 if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ) { | |
| 238 mAxes.put(range.getAxis(), new Float(0)); | |
| 239 } | |
| 240 } | |
| 241 } | |
| 242 | |
| 243 // Called on UI thread. | |
| 244 void handleMotionEvent(MotionEvent event) { | |
| 245 synchronized (mLock) { | |
| 246 mTimestamp = event.getEventTime(); | |
| 247 for (int i = 0; i < mAxes.size(); ++i) { | |
| 248 final float value = event.getAxisValue(mAxes.keyAt(i)); | |
| 249 mAxes.setValueAt(i, value); | |
| 250 } | |
| 251 } | |
| 252 } | |
| 253 | |
| 254 // Called on UI thread. | |
| 255 void handleKeyEvent(int keyCode, boolean isDown, long timestamp) { | |
| 256 synchronized (mLock) { | |
| 257 mTimestamp = timestamp; | |
| 258 mButtons.put(keyCode, isDown); | |
| 259 } | |
| 260 } | |
| 261 | |
| 262 // Called on polling thread. | |
| 263 WebGamepadData produceWebData() { | |
| 264 synchronized (mLock) { | |
| 265 return mMapper.map(mAxes, mButtons); | |
| 266 } | |
| 267 } | |
| 268 } | |
| 269 | |
| 270 private native void nativeRefreshDevice(long nativeGamepadPlatformDataFetche rAndroid, | |
| 271 int index, boolean connected, String id, long timestamp, | |
| 272 float[] axes, float[] buttons); | |
| 273 } | |
| OLD | NEW |