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