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; | |
6 | |
7 import android.content.Context; | |
8 import android.hardware.input.InputManager; | |
9 import android.hardware.input.InputManager.InputDeviceListener; | |
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 @JNINamespace("content") | |
23 class GamepadList implements InputDeviceListener { | |
24 private static final int MAX_GAMEPADS = 4; | |
25 private GamepadDevice[] mGamepadDevices = new GamepadDevice[MAX_GAMEPADS]; | |
26 private boolean mIsGamepadAccessed = false; | |
27 private final Object mLock = new Object(); | |
28 private InputManager mInputManager; | |
29 private int mAttachedToWindowCounter; | |
30 | |
31 private GamepadList() {} | |
32 | |
33 private void initializeDevices() { | |
34 // Get list of all the attached input devices. | |
35 int[] deviceIds = mInputManager.getInputDeviceIds(); | |
36 for (int i = 0; i < deviceIds.length; i++) { | |
37 InputDevice inputDevice = InputDevice.getDevice(deviceIds[i]); | |
38 // Check for gamepad device | |
39 if (isGamepadDevice(inputDevice)) { | |
40 // Register a new gamepad device. | |
41 registerGamepad(inputDevice); | |
42 } | |
43 } | |
44 } | |
45 | |
46 private static GamepadList sGamepadList = null; | |
47 | |
48 /** | |
49 * Notifies the GamepadList that a {@link ContentView} is attached to a windo w and it should | |
50 * prepare itself for gamepad input. It must be called before {@link onGeneri cMotionEvent} and | |
51 * {@link dispatchKeyEvent}. | |
52 */ | |
53 public static void onAttachedToWindow(Context context) { | |
54 assert ThreadUtils.runningOnUiThread(); | |
55 createInstance(); | |
56 sGamepadList.attachedToWindow(context); | |
57 } | |
58 | |
59 private void attachedToWindow(Context context) { | |
60 if (mAttachedToWindowCounter++ == 0) { | |
61 mInputManager = (InputManager) context.getSystemService(Context.INP UT_SERVICE); | |
62 synchronized (mLock) { | |
63 initializeDevices(); | |
64 } | |
65 // Register an input device listener. | |
66 mInputManager.registerInputDeviceListener(this, null); | |
67 } | |
68 } | |
69 | |
70 /** | |
71 * Notifies the GamepadList that a {@link ContentView} is detached from it's window. | |
72 */ | |
73 public static void onDetachedFromWindow() { | |
74 assert ThreadUtils.runningOnUiThread(); | |
75 sGamepadList.detachedFromWindow(); | |
76 } | |
77 | |
78 private void detachedFromWindow() { | |
79 if (--mAttachedToWindowCounter == 0) { | |
80 synchronized (mLock) { | |
81 for (int i = 0; i < MAX_GAMEPADS; ++i) { | |
82 mGamepadDevices[i] = null; | |
83 } | |
84 } | |
85 mInputManager.unregisterInputDeviceListener(this); | |
86 mInputManager = null; | |
87 } | |
88 } | |
89 | |
90 // ------------------------------------------------------------ | |
91 | |
92 // Override InputDeviceListener methods | |
93 @Override | |
94 public void onInputDeviceChanged(int deviceId) { | |
95 } | |
96 | |
97 @Override | |
98 public void onInputDeviceRemoved(int deviceId) { | |
99 synchronized (mLock) { | |
100 unregisterGamepad(deviceId); | |
101 } | |
102 } | |
103 | |
104 @Override | |
105 public void onInputDeviceAdded(int deviceId) { | |
106 InputDevice inputDevice = InputDevice.getDevice(deviceId); | |
107 if (!isGamepadDevice(inputDevice)) return; | |
108 synchronized (mLock) { | |
109 registerGamepad(inputDevice); | |
110 } | |
111 } | |
112 | |
113 // ------------------------------------------------------------ | |
114 | |
115 private static void createInstance() { | |
116 if (sGamepadList == null) { | |
117 sGamepadList = new GamepadList(); | |
118 } | |
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 sGamepadList.updateForEvent(event); | |
161 } | |
162 | |
163 private static boolean updateForEvent(KeyEvent event) { | |
164 synchronized (sGamepadList.mLock) { | |
165 if (!sGamepadList.mIsGamepadAccessed) return false; | |
166 GamepadDevice gamepad = sGamepadList.getGamepadForEvent(event); | |
167 if (gamepad == null) return false; | |
168 return gamepad.updateForEvent(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 sGamepadList.updateForEvent(event); | |
179 } | |
180 | |
181 private static boolean updateForEvent(MotionEvent event) { | |
182 synchronized (sGamepadList.mLock) { | |
183 if (!sGamepadList.mIsGamepadAccessed) return false; | |
184 GamepadDevice gamepad = sGamepadList.getGamepadForEvent(event); | |
185 if (gamepad == null) return false; | |
186 return gamepad.updateForEvent(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) { | |
jdduke (slow)
2014/04/04 15:10:17
|return false;| on same line as conditional, no br
SaurabhK
2014/04/11 14:41:42
On 2014/04/04 15:10:17, jdduke wrote:
Done.
| |
209 return false; // invalid index | |
210 } | |
211 | |
212 GamepadDevice gamepad = new GamepadDevice(index, inputDevice); | |
213 mGamepadDevices[index] = gamepad; | |
214 return true; | |
215 } | |
216 | |
217 private void unregisterGamepad(int deviceId) { | |
218 GamepadDevice gamepadDevice = getDeviceById(deviceId); | |
219 if (gamepadDevice == null) return; // Not a registered device. | |
220 int index = gamepadDevice.getIndex(); | |
221 mGamepadDevices[index] = null; | |
222 } | |
223 | |
224 private static boolean isGamepadDevice(InputDevice inputDevice) { | |
225 return ((inputDevice.getSources() & InputDevice.SOURCE_JOYSTICK) == | |
226 InputDevice.SOURCE_JOYSTICK); | |
227 } | |
228 | |
229 private GamepadDevice getGamepadForEvent(InputEvent event) { | |
230 int deviceId = event.getDeviceId(); | |
231 GamepadDevice gamepad = getDeviceById(deviceId); | |
232 return gamepad; | |
233 } | |
234 | |
235 private static boolean isGamepadEvent(MotionEvent event) { | |
236 return ((event.getSource() & InputDevice.SOURCE_JOYSTICK) == InputDevice .SOURCE_JOYSTICK); | |
237 } | |
238 | |
239 // Returns true if event's keycode corresponds to a gamepad key. | |
240 // Specific handling for dpad keys is required because | |
241 // KeyEvent.isGamepadButton doesn't consider dpad keys. | |
242 private static boolean isGamepadEvent(KeyEvent event) { | |
243 int keyCode = event.getKeyCode(); | |
244 switch (keyCode) { | |
245 case KeyEvent.KEYCODE_DPAD_UP: | |
246 case KeyEvent.KEYCODE_DPAD_DOWN: | |
247 case KeyEvent.KEYCODE_DPAD_LEFT: | |
248 case KeyEvent.KEYCODE_DPAD_RIGHT: | |
249 return true; | |
250 default: | |
251 return KeyEvent.isGamepadButton(keyCode); | |
252 } | |
253 } | |
254 | |
255 @CalledByNative | |
256 static void getGamepadData(long gamepads) { | |
257 assert sGamepadList != null; | |
258 sGamepadList.grabGamepadData(gamepads); | |
259 } | |
260 | |
261 private void grabGamepadData(long gamepads) { | |
262 synchronized (mLock) { | |
263 for (int i = 0; i < MAX_GAMEPADS; i++) { | |
264 final GamepadDevice device = getDevice(i); | |
265 if (device != null) { | |
266 device.mapButtonsAndAxes(); | |
267 nativeSetGamepadData(gamepads, i, device.getMapping(), true, | |
268 device.getName(), device.getTimestamp() , device.getAxes(), | |
269 device.getButtons()); | |
270 } | |
271 else | |
272 nativeSetGamepadData(gamepads, i, null, false, null, 0, null , null); | |
273 } | |
274 } | |
275 } | |
276 | |
277 @CalledByNative | |
278 static void notifyForGamepadsAccess(boolean isAccessPaused) { | |
279 assert sGamepadList != null; | |
280 sGamepadList.isGamepadAccessed(!isAccessPaused); | |
281 } | |
282 | |
283 private void isGamepadAccessed(boolean isGamepadAccessed) { | |
jdduke (slow)
2014/04/04 15:10:17
|setIsGamepadAccessed|.
SaurabhK
2014/04/11 14:41:42
On 2014/04/04 15:10:17, jdduke wrote:
Done.
| |
284 synchronized (mLock) { | |
285 mIsGamepadAccessed = isGamepadAccessed; | |
286 if (isGamepadAccessed) { | |
287 for (int i = 0; i < MAX_GAMEPADS; i++) { | |
288 GamepadDevice gamepadDevice = getDevice(i); | |
289 if (gamepadDevice == null) continue; | |
290 gamepadDevice.clearData(); | |
291 } | |
292 } | |
293 } | |
294 } | |
295 | |
296 private native void nativeSetGamepadData(long gamepads, | |
297 int index, | |
298 String mapping, | |
299 boolean connected, | |
300 String devicename, | |
301 long timestamp, | |
302 float[] axes, | |
303 float[] buttons); | |
304 | |
305 } | |
OLD | NEW |