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; | |
10 import android.os.Build; | |
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.CalledByNative; | |
17 import org.chromium.base.JNINamespace; | |
18 import org.chromium.base.ThreadUtils; | |
19 | |
20 /** | |
21 * Class to manage connected gamepad devices list. | |
22 * | |
23 * It is a Java counterpart of GamepadPlatformDataFetcherAndroid and feeds Gamep ad API with input | |
24 * data. | |
25 */ | |
26 @JNINamespace("content") | |
27 public class GamepadList { | |
28 private static final int MAX_GAMEPADS = 4; | |
29 | |
30 private final Object mLock = new Object(); | |
31 | |
32 private final GamepadDevice[] mGamepadDevices = new GamepadDevice[MAX_GAMEPA DS]; | |
33 private InputManager mInputManager; | |
34 private int mAttachedToWindowCounter; | |
35 private boolean mIsGamepadAccessed; | |
36 private InputDeviceListener mInputDeviceListener; | |
37 | |
38 private GamepadList() { | |
39 mInputDeviceListener = new InputDeviceListener() { | |
40 // Override InputDeviceListener methods | |
41 @Override | |
42 public void onInputDeviceChanged(int deviceId) { | |
43 onInputDeviceChangedImpl(deviceId); | |
44 } | |
45 | |
46 @Override | |
47 public void onInputDeviceRemoved(int deviceId) { | |
48 onInputDeviceRemovedImpl(deviceId); | |
49 } | |
50 | |
51 @Override | |
52 public void onInputDeviceAdded(int deviceId) { | |
53 onInputDeviceAddedImpl(deviceId); | |
54 } | |
55 }; | |
56 } | |
57 | |
58 private void initializeDevices() { | |
59 // Get list of all the attached input devices. | |
60 int[] deviceIds = mInputManager.getInputDeviceIds(); | |
61 for (int i = 0; i < deviceIds.length; i++) { | |
62 InputDevice inputDevice = InputDevice.getDevice(deviceIds[i]); | |
63 // Check for gamepad device | |
64 if (isGamepadDevice(inputDevice)) { | |
65 // Register a new gamepad device. | |
66 registerGamepad(inputDevice); | |
67 } | |
68 } | |
69 } | |
70 | |
71 /** | |
72 * Notifies the GamepadList that a {@link ContentView} is attached to a wind ow and it should | |
73 * prepare itself for gamepad input. It must be called before {@link onGener icMotionEvent} and | |
74 * {@link dispatchKeyEvent}. | |
75 */ | |
76 public static void onAttachedToWindow(Context context) { | |
77 assert ThreadUtils.runningOnUiThread(); | |
78 if (!isGamepadSupported()) return; | |
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 public static void onDetachedFromWindow() { | |
97 assert ThreadUtils.runningOnUiThread(); | |
98 if (!isGamepadSupported()) return; | |
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 assert isGamepadSupported(); | |
136 return LazyHolder.INSTANCE; | |
137 } | |
138 | |
139 private int getDeviceCount() { | |
140 int count = 0; | |
141 for (int i = 0; i < MAX_GAMEPADS; i++) { | |
142 if (getDevice(i) != null) { | |
143 count++; | |
144 } | |
145 } | |
146 return count; | |
147 } | |
148 | |
149 private boolean isDeviceConnected(int index) { | |
150 if (index < MAX_GAMEPADS && getDevice(index) != null) { | |
151 return true; | |
152 } | |
153 return false; | |
154 } | |
155 | |
156 private GamepadDevice getDeviceById(int deviceId) { | |
157 for (int i = 0; i < MAX_GAMEPADS; i++) { | |
158 GamepadDevice gamepad = mGamepadDevices[i]; | |
159 if (gamepad != null && gamepad.getId() == deviceId) { | |
160 return gamepad; | |
161 } | |
162 } | |
163 return null; | |
164 } | |
165 | |
166 private GamepadDevice getDevice(int index) { | |
167 // Maximum 4 Gamepads can be connected at a time starting at index zero. | |
168 assert index >= 0 && index < MAX_GAMEPADS; | |
169 return mGamepadDevices[index]; | |
170 } | |
171 | |
172 /** | |
173 * Handles key events from the gamepad devices. | |
174 * @return True if the event has been consumed. | |
175 */ | |
176 public static boolean dispatchKeyEvent(KeyEvent event) { | |
177 if (!isGamepadSupported()) return false; | |
178 if (!isGamepadEvent(event)) return false; | |
179 return getInstance().handleKeyEvent(event); | |
180 } | |
181 | |
182 private boolean handleKeyEvent(KeyEvent event) { | |
183 synchronized (mLock) { | |
184 if (!mIsGamepadAccessed) return false; | |
185 GamepadDevice gamepad = getGamepadForEvent(event); | |
186 if (gamepad == null) return false; | |
187 return gamepad.handleKeyEvent(event); | |
188 } | |
189 } | |
190 | |
191 /** | |
192 * Handles motion events from the gamepad devices. | |
193 * @return True if the event has been consumed. | |
194 */ | |
195 public static boolean onGenericMotionEvent(MotionEvent event) { | |
196 if (!isGamepadSupported()) return false; | |
197 if (!isGamepadEvent(event)) return false; | |
198 return getInstance().handleMotionEvent(event); | |
199 } | |
200 | |
201 private boolean handleMotionEvent(MotionEvent event) { | |
202 synchronized (mLock) { | |
203 if (!mIsGamepadAccessed) return false; | |
204 GamepadDevice gamepad = getGamepadForEvent(event); | |
205 if (gamepad == null) return false; | |
206 return gamepad.handleMotionEvent(event); | |
207 } | |
208 } | |
209 | |
210 private int getNextAvailableIndex() { | |
211 // When multiple gamepads are connected to a user agent, indices must be assigned on a | |
212 // first-come first-serve basis, starting at zero. If a gamepad is disco nnected, previously | |
213 // assigned indices must not be reassigned to gamepads that continue to be connected. | |
214 // However, if a gamepad is disconnected, and subsequently the same or a different | |
215 // gamepad is then connected, index entries must be reused. | |
216 | |
217 for (int i = 0; i < MAX_GAMEPADS; ++i) { | |
218 if (getDevice(i) == null) { | |
219 return i; | |
220 } | |
221 } | |
222 // Reached maximum gamepads limit. | |
223 return -1; | |
224 } | |
225 | |
226 private boolean registerGamepad(InputDevice inputDevice) { | |
227 int index = getNextAvailableIndex(); | |
228 if (index == -1) return false; // invalid index | |
229 | |
230 GamepadDevice gamepad = new GamepadDevice(index, inputDevice); | |
231 mGamepadDevices[index] = gamepad; | |
232 return true; | |
233 } | |
234 | |
235 private void unregisterGamepad(int deviceId) { | |
236 GamepadDevice gamepadDevice = getDeviceById(deviceId); | |
237 if (gamepadDevice == null) return; // Not a registered device. | |
238 int index = gamepadDevice.getIndex(); | |
239 mGamepadDevices[index] = null; | |
240 } | |
241 | |
242 private static boolean isGamepadDevice(InputDevice inputDevice) { | |
243 return ((inputDevice.getSources() & InputDevice.SOURCE_JOYSTICK) == | |
Feng Qian
2014/10/09 21:49:15
inputDevice could be null, see http://developer.an
| |
244 InputDevice.SOURCE_JOYSTICK); | |
245 } | |
246 | |
247 private GamepadDevice getGamepadForEvent(InputEvent event) { | |
248 return getDeviceById(event.getDeviceId()); | |
249 } | |
250 | |
251 /** | |
252 * @return True if the motion event corresponds to a gamepad event. | |
253 */ | |
254 public static boolean isGamepadEvent(MotionEvent event) { | |
255 return ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice .SOURCE_JOYSTICK); | |
256 } | |
257 | |
258 /** | |
259 * @return True if event's keycode corresponds to a gamepad key. | |
260 */ | |
261 public static boolean isGamepadEvent(KeyEvent event) { | |
262 int keyCode = event.getKeyCode(); | |
263 switch (keyCode) { | |
264 // Specific handling for dpad keys is required because | |
265 // KeyEvent.isGamepadButton doesn't consider dpad keys. | |
266 case KeyEvent.KEYCODE_DPAD_UP: | |
267 case KeyEvent.KEYCODE_DPAD_DOWN: | |
268 case KeyEvent.KEYCODE_DPAD_LEFT: | |
269 case KeyEvent.KEYCODE_DPAD_RIGHT: | |
270 return true; | |
271 default: | |
272 return KeyEvent.isGamepadButton(keyCode); | |
273 } | |
274 } | |
275 | |
276 private static boolean isGamepadSupported() { | |
277 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; | |
278 } | |
279 | |
280 @CalledByNative | |
281 static void updateGamepadData(long webGamepadsPtr) { | |
282 if (!isGamepadSupported()) return; | |
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 notifyForGamepadsAccess(boolean isAccessPaused) { | |
304 if (!isGamepadSupported()) return; | |
305 getInstance().setIsGamepadAccessed(!isAccessPaused); | |
306 } | |
307 | |
308 private void setIsGamepadAccessed(boolean isGamepadAccessed) { | |
309 synchronized (mLock) { | |
310 mIsGamepadAccessed = isGamepadAccessed; | |
311 if (isGamepadAccessed) { | |
312 for (int i = 0; i < MAX_GAMEPADS; i++) { | |
313 GamepadDevice gamepadDevice = getDevice(i); | |
314 if (gamepadDevice == null) continue; | |
315 gamepadDevice.clearData(); | |
316 } | |
317 } | |
318 } | |
319 } | |
320 | |
321 private native void nativeSetGamepadData(long webGamepadsPtr, int index, boo lean mapping, | |
322 boolean connected, String devicename, long timestamp, float[] axes, float[] buttons); | |
323 | |
324 private static class LazyHolder { | |
325 private static final GamepadList INSTANCE = new GamepadList(); | |
326 } | |
327 | |
328 } | |
OLD | NEW |