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(); | |
jdduke (slow)
2014/05/29 17:46:36
if (!isGamepadSupported()) return;
SaurabhK
2014/05/30 09:13:45
On 2014/05/29 17:46:36, jdduke wrote:
Done.
| |
78 getInstance().attachedToWindow(context); | |
79 } | |
80 | |
81 private void attachedToWindow(Context context) { | |
82 if (mAttachedToWindowCounter++ == 0) { | |
83 mInputManager = (InputManager) context.getSystemService(Context.INPU T_SERVICE); | |
84 synchronized (mLock) { | |
85 initializeDevices(); | |
86 } | |
87 // Register an input device listener. | |
88 mInputManager.registerInputDeviceListener(mInputDeviceListener, null ); | |
89 } | |
90 } | |
91 | |
92 /** | |
93 * Notifies the GamepadList that a {@link ContentView} is detached from it's window. | |
94 */ | |
95 public static void onDetachedFromWindow() { | |
96 assert ThreadUtils.runningOnUiThread(); | |
jdduke (slow)
2014/05/29 17:46:36
if (!isGamepadSupported()) return;
SaurabhK
2014/05/30 09:13:45
On 2014/05/29 17:46:36, jdduke wrote:
Done.
| |
97 getInstance().detachedFromWindow(); | |
98 } | |
99 | |
100 private void detachedFromWindow() { | |
101 if (--mAttachedToWindowCounter == 0) { | |
102 synchronized (mLock) { | |
103 for (int i = 0; i < MAX_GAMEPADS; ++i) { | |
104 mGamepadDevices[i] = null; | |
105 } | |
106 } | |
107 mInputManager.unregisterInputDeviceListener(mInputDeviceListener); | |
108 mInputManager = null; | |
109 } | |
110 } | |
111 | |
112 // ------------------------------------------------------------ | |
113 | |
114 private void onInputDeviceChangedImpl(int deviceId) {} | |
115 | |
116 private void onInputDeviceRemovedImpl(int deviceId) { | |
117 synchronized (mLock) { | |
118 unregisterGamepad(deviceId); | |
119 } | |
120 } | |
121 | |
122 private void onInputDeviceAddedImpl(int deviceId) { | |
123 InputDevice inputDevice = InputDevice.getDevice(deviceId); | |
124 if (!isGamepadDevice(inputDevice)) return; | |
125 synchronized (mLock) { | |
126 registerGamepad(inputDevice); | |
127 } | |
128 } | |
129 | |
130 // ------------------------------------------------------------ | |
131 | |
132 private static GamepadList getInstance() { | |
133 assert isGamepadSupported(); | |
134 return LazyHolder.INSTANCE; | |
135 } | |
136 | |
137 private int getDeviceCount() { | |
138 int count = 0; | |
139 for (int i = 0; i < MAX_GAMEPADS; i++) { | |
140 if (getDevice(i) != null) { | |
141 count++; | |
142 } | |
143 } | |
144 return count; | |
145 } | |
146 | |
147 private boolean isDeviceConnected(int index) { | |
148 if (index < MAX_GAMEPADS && getDevice(index) != null) { | |
149 return true; | |
150 } | |
151 return false; | |
152 } | |
153 | |
154 private GamepadDevice getDeviceById(int deviceId) { | |
155 for (int i = 0; i < MAX_GAMEPADS; i++) { | |
156 GamepadDevice gamepad = mGamepadDevices[i]; | |
157 if (gamepad != null && gamepad.getId() == deviceId) { | |
158 return gamepad; | |
159 } | |
160 } | |
161 return null; | |
162 } | |
163 | |
164 private GamepadDevice getDevice(int index) { | |
165 // Maximum 4 Gamepads can be connected at a time starting at index zero. | |
166 assert index >= 0 && index < MAX_GAMEPADS; | |
167 return mGamepadDevices[index]; | |
168 } | |
169 | |
170 /** | |
171 * Handles key events from the gamepad devices. | |
172 * @return True if the event has been consumed. | |
173 */ | |
174 public static boolean dispatchKeyEvent(KeyEvent event) { | |
175 if (!isGamepadSupported()) return false; | |
176 if (!isGamepadEvent(event)) return false; | |
177 return getInstance().handleKeyEvent(event); | |
178 } | |
179 | |
180 private boolean handleKeyEvent(KeyEvent event) { | |
181 synchronized (mLock) { | |
182 if (!mIsGamepadAccessed) 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 (!isGamepadSupported()) return false; | |
195 if (!isGamepadEvent(event)) return false; | |
196 return getInstance().handleMotionEvent(event); | |
197 } | |
198 | |
199 private boolean handleMotionEvent(MotionEvent event) { | |
200 synchronized (mLock) { | |
201 if (!mIsGamepadAccessed) return false; | |
202 GamepadDevice gamepad = getGamepadForEvent(event); | |
203 if (gamepad == null) return false; | |
204 return gamepad.handleMotionEvent(event); | |
205 } | |
206 } | |
207 | |
208 private int getNextAvailableIndex() { | |
209 // When multiple gamepads are connected to a user agent, indices must be assigned on a | |
210 // first-come first-serve basis, starting at zero. If a gamepad is disco nnected, previously | |
211 // assigned indices must not be reassigned to gamepads that continue to be connected. | |
212 // However, if a gamepad is disconnected, and subsequently the same or a different | |
213 // gamepad is then connected, index entries must be reused. | |
214 | |
215 for (int i = 0; i < MAX_GAMEPADS; ++i) { | |
216 if (getDevice(i) == null) { | |
217 return i; | |
218 } | |
219 } | |
220 // Reached maximum gamepads limit. | |
221 return -1; | |
222 } | |
223 | |
224 private boolean registerGamepad(InputDevice inputDevice) { | |
225 int index = getNextAvailableIndex(); | |
226 if (index == -1) return false; // invalid index | |
227 | |
228 GamepadDevice gamepad = new GamepadDevice(index, inputDevice); | |
229 mGamepadDevices[index] = gamepad; | |
230 return true; | |
231 } | |
232 | |
233 private void unregisterGamepad(int deviceId) { | |
234 GamepadDevice gamepadDevice = getDeviceById(deviceId); | |
235 if (gamepadDevice == null) return; // Not a registered device. | |
236 int index = gamepadDevice.getIndex(); | |
237 mGamepadDevices[index] = null; | |
238 } | |
239 | |
240 private static boolean isGamepadDevice(InputDevice inputDevice) { | |
241 return ((inputDevice.getSources() & InputDevice.SOURCE_JOYSTICK) == | |
242 InputDevice.SOURCE_JOYSTICK); | |
243 } | |
244 | |
245 private GamepadDevice getGamepadForEvent(InputEvent event) { | |
246 return getDeviceById(event.getDeviceId()); | |
247 } | |
248 | |
249 /** | |
250 * @return True if the motion event corresponds to a gamepad event. | |
251 */ | |
252 public static boolean isGamepadEvent(MotionEvent event) { | |
253 return ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice .SOURCE_JOYSTICK); | |
254 } | |
255 | |
256 /** | |
257 * @return True if event's keycode corresponds to a gamepad key. | |
258 */ | |
259 public static boolean isGamepadEvent(KeyEvent event) { | |
260 int keyCode = event.getKeyCode(); | |
261 switch (keyCode) { | |
262 // Specific handling for dpad keys is required because | |
263 // KeyEvent.isGamepadButton doesn't consider dpad keys. | |
264 case KeyEvent.KEYCODE_DPAD_UP: | |
265 case KeyEvent.KEYCODE_DPAD_DOWN: | |
266 case KeyEvent.KEYCODE_DPAD_LEFT: | |
267 case KeyEvent.KEYCODE_DPAD_RIGHT: | |
268 return true; | |
269 default: | |
270 return KeyEvent.isGamepadButton(keyCode); | |
271 } | |
272 } | |
273 | |
274 private static boolean isGamepadSupported() { | |
275 return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN; | |
276 } | |
277 | |
278 @CalledByNative | |
279 static void updateGamepadData(long webGamepadsPtr) { | |
280 if (!isGamepadSupported()) return; | |
281 getInstance().grabGamepadData(webGamepadsPtr); | |
282 } | |
283 | |
284 private void grabGamepadData(long webGamepadsPtr) { | |
285 synchronized (mLock) { | |
286 for (int i = 0; i < MAX_GAMEPADS; i++) { | |
287 final GamepadDevice device = getDevice(i); | |
288 if (device != null) { | |
289 device.updateButtonsAndAxesMapping(); | |
290 nativeSetGamepadData(webGamepadsPtr, i, device.isStandardGam epad(), true, | |
291 device.getName(), device.getTimestamp(), device.getA xes(), | |
292 device.getButtons()); | |
293 } else { | |
294 nativeSetGamepadData(webGamepadsPtr, i, false, false, null, 0, null, null); | |
295 } | |
296 } | |
297 } | |
298 } | |
299 | |
300 @CalledByNative | |
301 static void notifyForGamepadsAccess(boolean isAccessPaused) { | |
302 if (!isGamepadSupported()) return; | |
303 getInstance().setIsGamepadAccessed(!isAccessPaused); | |
304 } | |
305 | |
306 private void setIsGamepadAccessed(boolean isGamepadAccessed) { | |
307 synchronized (mLock) { | |
308 mIsGamepadAccessed = isGamepadAccessed; | |
309 if (isGamepadAccessed) { | |
310 for (int i = 0; i < MAX_GAMEPADS; i++) { | |
311 GamepadDevice gamepadDevice = getDevice(i); | |
312 if (gamepadDevice == null) continue; | |
313 gamepadDevice.clearData(); | |
314 } | |
315 } | |
316 } | |
317 } | |
318 | |
319 private native void nativeSetGamepadData(long webGamepadsPtr, int index, boo lean mapping, | |
320 boolean connected, String devicename, long timestamp, float[] axes, float[] buttons); | |
321 | |
322 private static class LazyHolder { | |
323 private static final GamepadList INSTANCE = new GamepadList(); | |
324 } | |
325 | |
326 } | |
OLD | NEW |