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.util.SparseArray; | |
10 import android.view.InputDevice; | |
11 import android.view.InputDevice.MotionRange; | |
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 import java.util.List; | |
20 | |
21 @JNINamespace("content") | |
22 class WebGamepadData { | |
23 public float[] axes; | |
24 public float[] buttons; | |
25 } | |
26 | |
27 /** | |
28 * Java counterpart of GamepadPlatformDataFetcherAndroid. | |
29 * Manages game input devices and feed Gamepad API with input data. | |
30 * GamepadPlatformDataFetcherAndroid is merely a wrepper around this. | |
bajones
2014/03/01 00:04:03
Nit: wrepper->wrapper
| |
31 * Native callable methods called by GamepadPlatformDataFetcherAndroid on the po ller thread | |
32 * which is a native thread without a java looper. Events are processed on the U I thread. | |
33 */ | |
34 @JNINamespace("content") | |
35 public class GamepadAdapter implements InputManager.InputDeviceListener { | |
36 private static final int NUM_WEB_GAMEPADS = 4; | |
37 | |
38 private static GamepadAdapter instance; | |
39 | |
40 private final InputManager mInputManager; | |
41 private final InputDeviceHandler[] mDeviceHandlers; | |
42 private boolean mIsActive; | |
43 private long mNativeDataFetcher; | |
44 private final Object mDeviceHandlersLock = new Object(); | |
45 | |
46 public static void initialize(Context context) { | |
47 if (instance == null) | |
48 instance = new GamepadAdapter(context); | |
49 } | |
50 | |
51 public static GamepadAdapter getInstance() { | |
52 assert instance != null; | |
53 return instance; | |
54 } | |
55 | |
56 private GamepadAdapter(Context context) { | |
57 assert ThreadUtils.runningOnUiThread(); | |
58 mIsActive = false; | |
59 mDeviceHandlers = new InputDeviceHandler[NUM_WEB_GAMEPADS]; | |
60 mInputManager = (InputManager) context.getSystemService(Context.INPUT_SE RVICE); | |
61 mInputManager.registerInputDeviceListener(this, null); | |
62 | |
63 // Enumerate devices. | |
64 int[] ids = mInputManager.getInputDeviceIds(); | |
65 int activeDevices = 0; | |
66 for (int i = 0; i < ids.length && activeDevices < NUM_WEB_GAMEPADS; ++i) { | |
67 InputDevice device = mInputManager.getInputDevice(ids[i]); | |
68 if (isGameDevice(device)) { | |
69 mDeviceHandlers[activeDevices++] = new InputDeviceHandler(device ); | |
70 } | |
71 } | |
72 } | |
73 | |
74 @Override | |
75 public void onInputDeviceAdded(int deviceID) { | |
76 ThreadUtils.assertOnUiThread(); | |
77 InputDevice device = mInputManager.getInputDevice(deviceID); | |
78 if (!isGameDevice(device)) | |
79 return; | |
80 int index = nextAvailableIndex(); | |
81 if (index == -1) | |
82 return; | |
83 synchronized (mDeviceHandlersLock) { | |
84 mDeviceHandlers[index] = new InputDeviceHandler(device); | |
85 } | |
86 } | |
87 | |
88 @Override | |
89 public void onInputDeviceRemoved(int deviceID) { | |
90 ThreadUtils.assertOnUiThread(); | |
91 int index = indexForDeviceID(deviceID); | |
92 if (index == -1) | |
93 return; | |
94 synchronized (mDeviceHandlersLock) { | |
95 mDeviceHandlers[index] = null; | |
96 } | |
97 } | |
98 | |
99 @Override | |
100 public void onInputDeviceChanged(int deviceID) { | |
101 ThreadUtils.assertOnUiThread(); | |
102 int index = indexForDeviceID(deviceID); | |
103 if (index == -1) { | |
104 index = nextAvailableIndex(); | |
105 if (index == -1) return; | |
106 } | |
107 InputDevice device = mInputManager.getInputDevice(deviceID); | |
108 synchronized (mDeviceHandlersLock) { | |
109 mDeviceHandlers[index] = null; | |
110 if (isGameDevice(device)) { | |
111 mDeviceHandlers[index] = new InputDeviceHandler(device); | |
112 } | |
113 } | |
114 } | |
115 | |
116 public boolean handleMotionEvent(MotionEvent event) { | |
117 if (!mIsActive) | |
118 return false; | |
119 InputDeviceHandler handler = handlerForDeviceId(event.getDeviceId()); | |
120 if (handler == null) | |
121 return false; | |
122 if (!isGameEvent(event)) | |
123 return false; | |
124 handler.handleMotionEvent(event); | |
125 return true; | |
126 } | |
127 | |
128 public boolean handleKeyEvent(KeyEvent event) { | |
129 if (!mIsActive) | |
130 return false; | |
131 if (event.getAction() != KeyEvent.ACTION_DOWN && | |
132 event.getAction() != KeyEvent.ACTION_UP) { | |
133 return false; | |
134 } | |
135 InputDeviceHandler handler = handlerForDeviceId(event.getDeviceId()); | |
136 if (handler == null) | |
137 return false; | |
138 int keyCode = event.getKeyCode(); | |
139 if (!isGameKey(keyCode)) | |
140 return false; | |
141 boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN; | |
142 handler.handleKeyEvent(keyCode, isDown, event.getEventTime()); | |
143 return true; | |
144 } | |
145 | |
146 @CalledByNative | |
147 public static GamepadAdapter attach(long nativeDataFetcher) { | |
148 assert instance != null; | |
149 instance.mNativeDataFetcher = nativeDataFetcher; | |
150 instance.mIsActive = true; | |
151 return instance; | |
152 } | |
153 | |
154 // Called on polling thread. | |
155 @CalledByNative | |
156 private void getGamepadData() { | |
157 synchronized (mDeviceHandlersLock) { | |
158 for (int i = 0; i < NUM_WEB_GAMEPADS; ++i) { | |
159 if (mDeviceHandlers[i] == null) { | |
160 nativeRefreshDevice(mNativeDataFetcher, i, false, null, 0, n ull, null); | |
161 } else { | |
162 InputDeviceHandler handler = mDeviceHandlers[i]; | |
163 WebGamepadData data = handler.produceWebData(); | |
164 nativeRefreshDevice(mNativeDataFetcher, i, true, | |
165 handler.getInputDevice().getName(), handler.getTimes tamp(), | |
166 data.axes, data.buttons); | |
167 } | |
168 } | |
169 } | |
170 } | |
171 | |
172 @CalledByNative | |
173 public void setActive(boolean active) { mIsActive = active; } | |
174 | |
175 int nextAvailableIndex() { | |
176 for (int i = 0; i < mDeviceHandlers.length; ++i) { | |
177 if (mDeviceHandlers[i] == null) | |
178 return i; | |
179 } | |
180 return -1; | |
181 } | |
182 | |
183 int indexForDeviceID(int deviceID) { | |
184 for (int i = 0; i < mDeviceHandlers.length; ++i) { | |
185 if (mDeviceHandlers[i] != null && | |
186 mDeviceHandlers[i].getInputDevice().getId() == deviceID) | |
187 return i; | |
188 } | |
189 return -1; | |
190 } | |
191 | |
192 InputDeviceHandler handlerForDeviceId(int deviceID) { | |
193 int index = indexForDeviceID(deviceID); | |
194 return index == -1 ? null : mDeviceHandlers[index]; | |
195 } | |
196 | |
197 private static boolean isGameDevice(InputDevice device) { | |
198 return (device.getSources() & InputDevice.SOURCE_JOYSTICK) != 0; | |
199 } | |
200 | |
201 private static boolean isGameEvent(MotionEvent event) { | |
202 return (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0 && | |
203 event.getAction() == MotionEvent.ACTION_MOVE; | |
204 } | |
205 | |
206 private static boolean isGameKey(int keyCode) { | |
207 switch (keyCode) { | |
208 case KeyEvent.KEYCODE_DPAD_UP: | |
209 case KeyEvent.KEYCODE_DPAD_DOWN: | |
210 case KeyEvent.KEYCODE_DPAD_LEFT: | |
211 case KeyEvent.KEYCODE_DPAD_RIGHT: | |
212 case KeyEvent.KEYCODE_DPAD_CENTER: | |
213 return true; | |
214 default: | |
215 return KeyEvent.isGamepadButton(keyCode); | |
216 } | |
217 } | |
218 | |
219 private static class InputDeviceHandler { | |
220 private final InputDevice mDevice; | |
221 private final SparseArray<Float> mAxes; | |
222 private final SparseArray<Boolean> mButtons = new SparseArray<Boolean>() ; | |
223 private final GamepadDataMapper mMapper; | |
224 private long mTimestamp; | |
225 private final Object mLock = new Object(); | |
226 | |
227 InputDevice getInputDevice() { return mDevice; } | |
228 long getTimestamp() { return mTimestamp; } | |
229 | |
230 public InputDeviceHandler(InputDevice device) { | |
231 assert isGameDevice(device); | |
232 mDevice = device; | |
233 mMapper = GamepadDataMapper.createDataMapper(device.getName()); | |
234 List<MotionRange> ranges = device.getMotionRanges(); | |
235 mAxes = new SparseArray<Float>(ranges.size()); | |
236 for (MotionRange range : ranges) { | |
237 if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ) { | |
238 mAxes.put(range.getAxis(), new Float(0)); | |
239 } | |
240 } | |
241 } | |
242 | |
243 // Called on UI thread. | |
244 void handleMotionEvent(MotionEvent event) { | |
245 synchronized (mLock) { | |
246 mTimestamp = event.getEventTime(); | |
247 for (int i = 0; i < mAxes.size(); ++i) { | |
248 final float value = event.getAxisValue(mAxes.keyAt(i)); | |
249 mAxes.setValueAt(i, value); | |
250 } | |
251 } | |
252 } | |
253 | |
254 // Called on UI thread. | |
255 void handleKeyEvent(int keyCode, boolean isDown, long timestamp) { | |
256 synchronized (mLock) { | |
257 mTimestamp = timestamp; | |
258 mButtons.put(keyCode, isDown); | |
259 } | |
260 } | |
261 | |
262 // Called on polling thread. | |
263 WebGamepadData produceWebData() { | |
264 synchronized (mLock) { | |
265 return mMapper.map(mAxes, mButtons); | |
266 } | |
267 } | |
268 } | |
269 | |
270 private native void nativeRefreshDevice(long nativeGamepadPlatformDataFetche rAndroid, | |
271 int index, boolean connected, String id, long timestamp, | |
272 float[] axes, float[] buttons); | |
273 } | |
OLD | NEW |