Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(91)

Side by Side Diff: content/public/android/java/src/org/chromium/content/browser/input/GamepadAdapter.java

Issue 165483003: Gamepad API for Android (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698