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 String id; |
| 24 public String mapping; |
| 25 public float[] axes; |
| 26 public float[] buttons; |
| 27 } |
| 28 |
| 29 /** |
| 30 * Java counterpart of GamepadPlatformDataFetcherAndroid. |
| 31 * Manages game input devices and feed Gamepad API with input data. |
| 32 * GamepadPlatformDataFetcherAndroid is merely a wrepper around this. |
| 33 * Native callable methods called by GamepadPlatformDataFetcherAndroid on the po
ller thread |
| 34 * which is a native thread without a java looper. Events are processed on the U
I thread. |
| 35 */ |
| 36 @JNINamespace("content") |
| 37 public class GamepadAdapter implements InputManager.InputDeviceListener { |
| 38 |
| 39 private static final int NUM_WEB_GAMEPADS = 4; |
| 40 |
| 41 private static GamepadAdapter instance; |
| 42 |
| 43 private final InputManager mInputManager; |
| 44 private InputDeviceHandler[] mDeviceHandlers; |
| 45 private final Object mDeviceHandlersLock = new Object(); |
| 46 private boolean mIsFetched; |
| 47 private boolean mIsPaused; |
| 48 private long mNativeDataFetcher; |
| 49 |
| 50 public static void initialize(Context context) { |
| 51 if (instance == null) |
| 52 instance = new GamepadAdapter(context); |
| 53 } |
| 54 |
| 55 public static GamepadAdapter getInstance() { |
| 56 assert instance != null; |
| 57 return instance; |
| 58 } |
| 59 |
| 60 private GamepadAdapter(Context context) { |
| 61 assert ThreadUtils.runningOnUiThread(); |
| 62 mInputManager = (InputManager) context.getSystemService(Context.INPUT_SE
RVICE); |
| 63 mIsFetched = false; |
| 64 mIsPaused = false; |
| 65 enumerateDevices(); |
| 66 mInputManager.registerInputDeviceListener(this, null); |
| 67 } |
| 68 |
| 69 public void pause() { |
| 70 if (mIsPaused) |
| 71 return; |
| 72 mIsPaused = true; |
| 73 synchronized (mDeviceHandlersLock) { |
| 74 for (int i = 0; i < NUM_WEB_GAMEPADS; ++i) { |
| 75 mDeviceHandlers[i] = null; |
| 76 } |
| 77 } |
| 78 mInputManager.unregisterInputDeviceListener(this); |
| 79 } |
| 80 |
| 81 public void resume() { |
| 82 if (!mIsPaused) |
| 83 return; |
| 84 mIsPaused = false; |
| 85 enumerateDevices(); |
| 86 mInputManager.registerInputDeviceListener(this, null); |
| 87 } |
| 88 |
| 89 private void enumerateDevices() { |
| 90 InputDeviceHandler[] handlers = new InputDeviceHandler[NUM_WEB_GAMEPADS]
; |
| 91 int[] ids = mInputManager.getInputDeviceIds(); |
| 92 int activeDevices = 0; |
| 93 for (int i = 0; i < ids.length && activeDevices < NUM_WEB_GAMEPADS; ++i)
{ |
| 94 InputDevice device = mInputManager.getInputDevice(ids[i]); |
| 95 if (isGameDevice(device)) { |
| 96 handlers[activeDevices++] = new InputDeviceHandler(device); |
| 97 } |
| 98 } |
| 99 synchronized (mDeviceHandlersLock) { |
| 100 mDeviceHandlers = handlers; |
| 101 } |
| 102 } |
| 103 |
| 104 @Override |
| 105 public void onInputDeviceAdded(int deviceID) { |
| 106 ThreadUtils.assertOnUiThread(); |
| 107 InputDevice device = mInputManager.getInputDevice(deviceID); |
| 108 if (!isGameDevice(device)) |
| 109 return; |
| 110 int index = nextAvailableIndex(); |
| 111 if (index == -1) |
| 112 return; |
| 113 synchronized (mDeviceHandlersLock) { |
| 114 mDeviceHandlers[index] = new InputDeviceHandler(device); |
| 115 } |
| 116 } |
| 117 |
| 118 @Override |
| 119 public void onInputDeviceRemoved(int deviceID) { |
| 120 ThreadUtils.assertOnUiThread(); |
| 121 int index = indexForDeviceID(deviceID); |
| 122 if (index == -1) |
| 123 return; |
| 124 synchronized (mDeviceHandlersLock) { |
| 125 mDeviceHandlers[index] = null; |
| 126 } |
| 127 } |
| 128 |
| 129 @Override |
| 130 public void onInputDeviceChanged(int deviceID) { |
| 131 ThreadUtils.assertOnUiThread(); |
| 132 int index = indexForDeviceID(deviceID); |
| 133 if (index == -1) { |
| 134 index = nextAvailableIndex(); |
| 135 if (index == -1) return; |
| 136 } |
| 137 InputDevice device = mInputManager.getInputDevice(deviceID); |
| 138 synchronized (mDeviceHandlersLock) { |
| 139 mDeviceHandlers[index] = null; |
| 140 if (isGameDevice(device)) { |
| 141 mDeviceHandlers[index] = new InputDeviceHandler(device); |
| 142 } |
| 143 } |
| 144 } |
| 145 |
| 146 public boolean handleMotionEvent(MotionEvent event) { |
| 147 if (!mIsFetched) |
| 148 return false; |
| 149 InputDeviceHandler handler = handlerForDeviceId(event.getDeviceId()); |
| 150 if (handler == null) |
| 151 return false; |
| 152 if (!isGameEvent(event)) |
| 153 return false; |
| 154 handler.handleMotionEvent(event); |
| 155 return true; |
| 156 } |
| 157 |
| 158 public boolean handleKeyEvent(KeyEvent event) { |
| 159 if (!mIsFetched) |
| 160 return false; |
| 161 if (event.getAction() != KeyEvent.ACTION_DOWN && |
| 162 event.getAction() != KeyEvent.ACTION_UP) { |
| 163 return false; |
| 164 } |
| 165 InputDeviceHandler handler = handlerForDeviceId(event.getDeviceId()); |
| 166 if (handler == null) |
| 167 return false; |
| 168 int keyCode = event.getKeyCode(); |
| 169 if (!isGameKey(keyCode)) |
| 170 return false; |
| 171 boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN; |
| 172 handler.handleKeyEvent(keyCode, isDown, event.getEventTime()); |
| 173 return true; |
| 174 } |
| 175 |
| 176 @CalledByNative |
| 177 public static GamepadAdapter attach(long nativeDataFetcher) { |
| 178 assert instance != null; |
| 179 instance.mNativeDataFetcher = nativeDataFetcher; |
| 180 instance.mIsFetched = true; |
| 181 return instance; |
| 182 } |
| 183 |
| 184 // Called on polling thread. |
| 185 @CalledByNative |
| 186 private void getGamepadData() { |
| 187 synchronized (mDeviceHandlersLock) { |
| 188 for (int i = 0; i < NUM_WEB_GAMEPADS; ++i) { |
| 189 if (mDeviceHandlers[i] == null) { |
| 190 nativeRefreshDevice(mNativeDataFetcher, i, false, null, null
, 0, null, null); |
| 191 } else { |
| 192 InputDeviceHandler handler = mDeviceHandlers[i]; |
| 193 WebGamepadData data = handler.produceWebData(); |
| 194 nativeRefreshDevice(mNativeDataFetcher, i, true, |
| 195 data.id, data.mapping, handler.getTimestamp(), |
| 196 data.axes, data.buttons); |
| 197 } |
| 198 } |
| 199 } |
| 200 } |
| 201 |
| 202 @CalledByNative |
| 203 public void setFetched(boolean fetched) { |
| 204 mIsFetched = fetched; |
| 205 } |
| 206 |
| 207 int nextAvailableIndex() { |
| 208 for (int i = 0; i < mDeviceHandlers.length; ++i) { |
| 209 if (mDeviceHandlers[i] == null) |
| 210 return i; |
| 211 } |
| 212 return -1; |
| 213 } |
| 214 |
| 215 int indexForDeviceID(int deviceID) { |
| 216 for (int i = 0; i < mDeviceHandlers.length; ++i) { |
| 217 if (mDeviceHandlers[i] != null && |
| 218 mDeviceHandlers[i].getInputDevice().getId() == deviceID) |
| 219 return i; |
| 220 } |
| 221 return -1; |
| 222 } |
| 223 |
| 224 InputDeviceHandler handlerForDeviceId(int deviceID) { |
| 225 int index = indexForDeviceID(deviceID); |
| 226 return index == -1 ? null : mDeviceHandlers[index]; |
| 227 } |
| 228 |
| 229 private static boolean isGameDevice(InputDevice device) { |
| 230 return (device.getSources() & InputDevice.SOURCE_JOYSTICK) != 0; |
| 231 } |
| 232 |
| 233 private static boolean isGameEvent(MotionEvent event) { |
| 234 return (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0 && |
| 235 event.getAction() == MotionEvent.ACTION_MOVE; |
| 236 } |
| 237 |
| 238 private static boolean isGameKey(int keyCode) { |
| 239 switch (keyCode) { |
| 240 case KeyEvent.KEYCODE_DPAD_UP: |
| 241 case KeyEvent.KEYCODE_DPAD_DOWN: |
| 242 case KeyEvent.KEYCODE_DPAD_LEFT: |
| 243 case KeyEvent.KEYCODE_DPAD_RIGHT: |
| 244 case KeyEvent.KEYCODE_DPAD_CENTER: |
| 245 return true; |
| 246 default: |
| 247 return KeyEvent.isGamepadButton(keyCode); |
| 248 } |
| 249 } |
| 250 |
| 251 private static class InputDeviceHandler { |
| 252 private final InputDevice mDevice; |
| 253 private final SparseArray<Float> mAxes; |
| 254 private final SparseArray<Boolean> mButtons = new SparseArray<Boolean>()
; |
| 255 private final GamepadDataMapper mMapper; |
| 256 private long mTimestamp; |
| 257 private final Object mLock = new Object(); |
| 258 |
| 259 InputDevice getInputDevice() { return mDevice; } |
| 260 long getTimestamp() { return mTimestamp; } |
| 261 |
| 262 public InputDeviceHandler(InputDevice device) { |
| 263 assert isGameDevice(device); |
| 264 mDevice = device; |
| 265 mMapper = GamepadDataMapper.createDataMapper(device.getName()); |
| 266 List<MotionRange> ranges = device.getMotionRanges(); |
| 267 mAxes = new SparseArray<Float>(ranges.size()); |
| 268 for (MotionRange range : ranges) { |
| 269 if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0
) { |
| 270 mAxes.put(range.getAxis(), new Float(0)); |
| 271 } |
| 272 } |
| 273 } |
| 274 |
| 275 // Called on UI thread. |
| 276 void handleMotionEvent(MotionEvent event) { |
| 277 synchronized (mLock) { |
| 278 mTimestamp = event.getEventTime(); |
| 279 for (int i = 0; i < mAxes.size(); ++i) { |
| 280 final float value = event.getAxisValue(mAxes.keyAt(i)); |
| 281 mAxes.setValueAt(i, value); |
| 282 } |
| 283 } |
| 284 } |
| 285 |
| 286 // Called on UI thread. |
| 287 void handleKeyEvent(int keyCode, boolean isDown, long timestamp) { |
| 288 synchronized (mLock) { |
| 289 mTimestamp = timestamp; |
| 290 mButtons.put(keyCode, isDown); |
| 291 } |
| 292 } |
| 293 |
| 294 // Called on polling thread. |
| 295 WebGamepadData produceWebData() { |
| 296 synchronized (mLock) { |
| 297 return mMapper.map(mAxes, mButtons); |
| 298 } |
| 299 } |
| 300 } |
| 301 |
| 302 private native void nativeRefreshDevice(long nativeGamepadPlatformDataFetche
rAndroid, |
| 303 int index, boolean connected, String id, String mapping, long timest
amp, |
| 304 float[] axes, float[] buttons); |
| 305 } |
OLD | NEW |