| 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.annotation.SuppressLint; | |
| 8 import android.content.Context; | |
| 9 import android.hardware.input.InputManager; | |
| 10 import android.hardware.input.InputManager.InputDeviceListener; | |
| 11 import android.view.InputDevice; | |
| 12 import android.view.InputEvent; | |
| 13 import android.view.KeyEvent; | |
| 14 import android.view.MotionEvent; | |
| 15 | |
| 16 import org.chromium.base.ThreadUtils; | |
| 17 import org.chromium.base.annotations.CalledByNative; | |
| 18 import org.chromium.base.annotations.JNINamespace; | |
| 19 import org.chromium.content.browser.ContentView; | |
| 20 | |
| 21 /** | |
| 22 * Class to manage connected gamepad devices list. | |
| 23 * | |
| 24 * It is a Java counterpart of GamepadPlatformDataFetcherAndroid and feeds Gamep
ad API with input | |
| 25 * data. | |
| 26 */ | |
| 27 @JNINamespace("content") | |
| 28 public class GamepadList { | |
| 29 private static final int MAX_GAMEPADS = 4; | |
| 30 | |
| 31 private final Object mLock = new Object(); | |
| 32 | |
| 33 private final GamepadDevice[] mGamepadDevices = new GamepadDevice[MAX_GAMEPA
DS]; | |
| 34 private InputManager mInputManager; | |
| 35 private int mAttachedToWindowCounter; | |
| 36 private boolean mIsGamepadAPIActive; | |
| 37 private InputDeviceListener mInputDeviceListener; | |
| 38 | |
| 39 private GamepadList() { | |
| 40 mInputDeviceListener = new InputDeviceListener() { | |
| 41 // Override InputDeviceListener methods | |
| 42 @Override | |
| 43 public void onInputDeviceChanged(int deviceId) { | |
| 44 onInputDeviceChangedImpl(deviceId); | |
| 45 } | |
| 46 | |
| 47 @Override | |
| 48 public void onInputDeviceRemoved(int deviceId) { | |
| 49 onInputDeviceRemovedImpl(deviceId); | |
| 50 } | |
| 51 | |
| 52 @Override | |
| 53 public void onInputDeviceAdded(int deviceId) { | |
| 54 onInputDeviceAddedImpl(deviceId); | |
| 55 } | |
| 56 }; | |
| 57 } | |
| 58 | |
| 59 private void initializeDevices() { | |
| 60 // Get list of all the attached input devices. | |
| 61 int[] deviceIds = mInputManager.getInputDeviceIds(); | |
| 62 for (int i = 0; i < deviceIds.length; i++) { | |
| 63 InputDevice inputDevice = InputDevice.getDevice(deviceIds[i]); | |
| 64 // Check for gamepad device | |
| 65 if (isGamepadDevice(inputDevice)) { | |
| 66 // Register a new gamepad device. | |
| 67 registerGamepad(inputDevice); | |
| 68 } | |
| 69 } | |
| 70 } | |
| 71 | |
| 72 /** | |
| 73 * Notifies the GamepadList that a {@link ContentView} is attached to a wind
ow and it should | |
| 74 * prepare itself for gamepad input. It must be called before {@link onGener
icMotionEvent} and | |
| 75 * {@link dispatchKeyEvent}. | |
| 76 */ | |
| 77 public static void onAttachedToWindow(Context context) { | |
| 78 assert ThreadUtils.runningOnUiThread(); | |
| 79 getInstance().attachedToWindow(context); | |
| 80 } | |
| 81 | |
| 82 private void attachedToWindow(Context context) { | |
| 83 if (mAttachedToWindowCounter++ == 0) { | |
| 84 mInputManager = (InputManager) context.getSystemService(Context.INPU
T_SERVICE); | |
| 85 synchronized (mLock) { | |
| 86 initializeDevices(); | |
| 87 } | |
| 88 // Register an input device listener. | |
| 89 mInputManager.registerInputDeviceListener(mInputDeviceListener, null
); | |
| 90 } | |
| 91 } | |
| 92 | |
| 93 /** | |
| 94 * Notifies the GamepadList that a {@link ContentView} is detached from it's
window. | |
| 95 */ | |
| 96 @SuppressLint("MissingSuperCall") | |
| 97 public static void onDetachedFromWindow() { | |
| 98 assert ThreadUtils.runningOnUiThread(); | |
| 99 getInstance().detachedFromWindow(); | |
| 100 } | |
| 101 | |
| 102 private void detachedFromWindow() { | |
| 103 if (--mAttachedToWindowCounter == 0) { | |
| 104 synchronized (mLock) { | |
| 105 for (int i = 0; i < MAX_GAMEPADS; ++i) { | |
| 106 mGamepadDevices[i] = null; | |
| 107 } | |
| 108 } | |
| 109 mInputManager.unregisterInputDeviceListener(mInputDeviceListener); | |
| 110 mInputManager = null; | |
| 111 } | |
| 112 } | |
| 113 | |
| 114 // ------------------------------------------------------------ | |
| 115 | |
| 116 private void onInputDeviceChangedImpl(int deviceId) {} | |
| 117 | |
| 118 private void onInputDeviceRemovedImpl(int deviceId) { | |
| 119 synchronized (mLock) { | |
| 120 unregisterGamepad(deviceId); | |
| 121 } | |
| 122 } | |
| 123 | |
| 124 private void onInputDeviceAddedImpl(int deviceId) { | |
| 125 InputDevice inputDevice = InputDevice.getDevice(deviceId); | |
| 126 if (!isGamepadDevice(inputDevice)) return; | |
| 127 synchronized (mLock) { | |
| 128 registerGamepad(inputDevice); | |
| 129 } | |
| 130 } | |
| 131 | |
| 132 // ------------------------------------------------------------ | |
| 133 | |
| 134 private static GamepadList getInstance() { | |
| 135 return LazyHolder.INSTANCE; | |
| 136 } | |
| 137 | |
| 138 private int getDeviceCount() { | |
| 139 int count = 0; | |
| 140 for (int i = 0; i < MAX_GAMEPADS; i++) { | |
| 141 if (getDevice(i) != null) { | |
| 142 count++; | |
| 143 } | |
| 144 } | |
| 145 return count; | |
| 146 } | |
| 147 | |
| 148 private boolean isDeviceConnected(int index) { | |
| 149 if (index < MAX_GAMEPADS && getDevice(index) != null) { | |
| 150 return true; | |
| 151 } | |
| 152 return false; | |
| 153 } | |
| 154 | |
| 155 private GamepadDevice getDeviceById(int deviceId) { | |
| 156 for (int i = 0; i < MAX_GAMEPADS; i++) { | |
| 157 GamepadDevice gamepad = mGamepadDevices[i]; | |
| 158 if (gamepad != null && gamepad.getId() == deviceId) { | |
| 159 return gamepad; | |
| 160 } | |
| 161 } | |
| 162 return null; | |
| 163 } | |
| 164 | |
| 165 private GamepadDevice getDevice(int index) { | |
| 166 // Maximum 4 Gamepads can be connected at a time starting at index zero. | |
| 167 assert index >= 0 && index < MAX_GAMEPADS; | |
| 168 return mGamepadDevices[index]; | |
| 169 } | |
| 170 | |
| 171 /** | |
| 172 * Handles key events from the gamepad devices. | |
| 173 * @return True if the event has been consumed. | |
| 174 */ | |
| 175 public static boolean dispatchKeyEvent(KeyEvent event) { | |
| 176 if (!isGamepadEvent(event)) return false; | |
| 177 return getInstance().handleKeyEvent(event); | |
| 178 } | |
| 179 | |
| 180 private boolean handleKeyEvent(KeyEvent event) { | |
| 181 synchronized (mLock) { | |
| 182 if (!mIsGamepadAPIActive) return false; | |
| 183 GamepadDevice gamepad = getGamepadForEvent(event); | |
| 184 if (gamepad == null) return false; | |
| 185 return gamepad.handleKeyEvent(event); | |
| 186 } | |
| 187 } | |
| 188 | |
| 189 /** | |
| 190 * Handles motion events from the gamepad devices. | |
| 191 * @return True if the event has been consumed. | |
| 192 */ | |
| 193 public static boolean onGenericMotionEvent(MotionEvent event) { | |
| 194 if (!isGamepadEvent(event)) return false; | |
| 195 return getInstance().handleMotionEvent(event); | |
| 196 } | |
| 197 | |
| 198 private boolean handleMotionEvent(MotionEvent event) { | |
| 199 synchronized (mLock) { | |
| 200 if (!mIsGamepadAPIActive) return false; | |
| 201 GamepadDevice gamepad = getGamepadForEvent(event); | |
| 202 if (gamepad == null) return false; | |
| 203 return gamepad.handleMotionEvent(event); | |
| 204 } | |
| 205 } | |
| 206 | |
| 207 private int getNextAvailableIndex() { | |
| 208 // When multiple gamepads are connected to a user agent, indices must be
assigned on a | |
| 209 // first-come first-serve basis, starting at zero. If a gamepad is disco
nnected, previously | |
| 210 // assigned indices must not be reassigned to gamepads that continue to
be connected. | |
| 211 // However, if a gamepad is disconnected, and subsequently the same or a
different | |
| 212 // gamepad is then connected, index entries must be reused. | |
| 213 | |
| 214 for (int i = 0; i < MAX_GAMEPADS; ++i) { | |
| 215 if (getDevice(i) == null) { | |
| 216 return i; | |
| 217 } | |
| 218 } | |
| 219 // Reached maximum gamepads limit. | |
| 220 return -1; | |
| 221 } | |
| 222 | |
| 223 private boolean registerGamepad(InputDevice inputDevice) { | |
| 224 int index = getNextAvailableIndex(); | |
| 225 if (index == -1) return false; // invalid index | |
| 226 | |
| 227 GamepadDevice gamepad = new GamepadDevice(index, inputDevice); | |
| 228 mGamepadDevices[index] = gamepad; | |
| 229 return true; | |
| 230 } | |
| 231 | |
| 232 private void unregisterGamepad(int deviceId) { | |
| 233 GamepadDevice gamepadDevice = getDeviceById(deviceId); | |
| 234 if (gamepadDevice == null) return; // Not a registered device. | |
| 235 int index = gamepadDevice.getIndex(); | |
| 236 mGamepadDevices[index] = null; | |
| 237 } | |
| 238 | |
| 239 private static boolean isGamepadDevice(InputDevice inputDevice) { | |
| 240 if (inputDevice == null) return false; | |
| 241 return ((inputDevice.getSources() | |
| 242 & InputDevice.SOURCE_JOYSTICK) == InputDevice.SOURCE_JOYSTICK); | |
| 243 } | |
| 244 | |
| 245 private GamepadDevice getGamepadForEvent(InputEvent event) { | |
| 246 return getDeviceById(event.getDeviceId()); | |
| 247 } | |
| 248 | |
| 249 /** | |
| 250 * @return True if HTML5 gamepad API is active. | |
| 251 */ | |
| 252 public static boolean isGamepadAPIActive() { | |
| 253 return getInstance().mIsGamepadAPIActive; | |
| 254 } | |
| 255 | |
| 256 /** | |
| 257 * @return True if the motion event corresponds to a gamepad event. | |
| 258 */ | |
| 259 public static boolean isGamepadEvent(MotionEvent event) { | |
| 260 return ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice
.SOURCE_JOYSTICK); | |
| 261 } | |
| 262 | |
| 263 /** | |
| 264 * @return True if event's keycode corresponds to a gamepad key. | |
| 265 */ | |
| 266 public static boolean isGamepadEvent(KeyEvent event) { | |
| 267 int keyCode = event.getKeyCode(); | |
| 268 switch (keyCode) { | |
| 269 // Specific handling for dpad keys is required because | |
| 270 // KeyEvent.isGamepadButton doesn't consider dpad keys. | |
| 271 case KeyEvent.KEYCODE_DPAD_UP: | |
| 272 case KeyEvent.KEYCODE_DPAD_DOWN: | |
| 273 case KeyEvent.KEYCODE_DPAD_LEFT: | |
| 274 case KeyEvent.KEYCODE_DPAD_RIGHT: | |
| 275 return true; | |
| 276 default: | |
| 277 return KeyEvent.isGamepadButton(keyCode); | |
| 278 } | |
| 279 } | |
| 280 | |
| 281 @CalledByNative | |
| 282 static void updateGamepadData(long webGamepadsPtr) { | |
| 283 getInstance().grabGamepadData(webGamepadsPtr); | |
| 284 } | |
| 285 | |
| 286 private void grabGamepadData(long webGamepadsPtr) { | |
| 287 synchronized (mLock) { | |
| 288 for (int i = 0; i < MAX_GAMEPADS; i++) { | |
| 289 final GamepadDevice device = getDevice(i); | |
| 290 if (device != null) { | |
| 291 device.updateButtonsAndAxesMapping(); | |
| 292 nativeSetGamepadData(webGamepadsPtr, i, device.isStandardGam
epad(), true, | |
| 293 device.getName(), device.getTimestamp(), device.getA
xes(), | |
| 294 device.getButtons()); | |
| 295 } else { | |
| 296 nativeSetGamepadData(webGamepadsPtr, i, false, false, null,
0, null, null); | |
| 297 } | |
| 298 } | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 @CalledByNative | |
| 303 static void setGamepadAPIActive(boolean isActive) { | |
| 304 getInstance().setIsGamepadActive(isActive); | |
| 305 } | |
| 306 | |
| 307 private void setIsGamepadActive(boolean isGamepadActive) { | |
| 308 synchronized (mLock) { | |
| 309 mIsGamepadAPIActive = isGamepadActive; | |
| 310 if (isGamepadActive) { | |
| 311 for (int i = 0; i < MAX_GAMEPADS; i++) { | |
| 312 GamepadDevice gamepadDevice = getDevice(i); | |
| 313 if (gamepadDevice == null) continue; | |
| 314 gamepadDevice.clearData(); | |
| 315 } | |
| 316 } | |
| 317 } | |
| 318 } | |
| 319 | |
| 320 private native void nativeSetGamepadData(long webGamepadsPtr, int index, boo
lean mapping, | |
| 321 boolean connected, String devicename, long timestamp, float[] axes,
float[] buttons); | |
| 322 | |
| 323 private static class LazyHolder { | |
| 324 private static final GamepadList INSTANCE = new GamepadList(); | |
| 325 } | |
| 326 | |
| 327 } | |
| OLD | NEW |