Chromium Code Reviews| 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 { | |
|
Ted C
2014/03/07 20:18:09
I'd prefer this to be a separate class as it is re
kbalazs
2014/03/12 00:17:36
You meant a separate file, right? :) Done.
| |
| 23 public String id; | |
| 24 public String mapping; | |
| 25 public float[] axes; | |
| 26 public float[] buttons; | |
| 27 } | |
| 28 | |
| 29 /** | |
| 30 * Java counterpart of GamepadPlatformDataFetcherAndroid. | |
| 31 * Manages game input devices and feed Gamepad API with input data. | |
| 32 * GamepadPlatformDataFetcherAndroid is merely a wrepper around this. | |
| 33 * Native callable methods called by GamepadPlatformDataFetcherAndroid on the po ller thread | |
| 34 * which is a native thread without a java looper. Events are processed on the U I thread. | |
| 35 */ | |
| 36 @JNINamespace("content") | |
| 37 public class GamepadAdapter implements InputManager.InputDeviceListener { | |
| 38 | |
| 39 private static final int NUM_WEB_GAMEPADS = 4; | |
|
Ted C
2014/03/07 20:18:09
where did this number come from?
kbalazs
2014/03/07 22:40:22
From the spec :) Also from blink::WebGamepads.
| |
| 40 | |
| 41 private static GamepadAdapter instance; | |
| 42 | |
| 43 private final InputManager mInputManager; | |
| 44 private InputDeviceHandler[] mDeviceHandlers; | |
| 45 private final Object mDeviceHandlersLock = new Object(); | |
| 46 private boolean mIsFetched; | |
| 47 private boolean mIsPaused; | |
| 48 private long mNativeDataFetcher; | |
| 49 | |
| 50 public static void initialize(Context context) { | |
| 51 if (instance == null) | |
| 52 instance = new GamepadAdapter(context); | |
| 53 } | |
| 54 | |
| 55 public static GamepadAdapter getInstance() { | |
|
Ted C
2014/03/07 20:18:09
Any reason this has to be an instance?
We are try
kbalazs
2014/03/07 22:40:22
I'm not sure a 1-1 relation is a really good idea.
Ted C
2014/03/08 01:06:00
You are definitely correct that only a single view
| |
| 56 assert instance != null; | |
| 57 return instance; | |
| 58 } | |
| 59 | |
| 60 private GamepadAdapter(Context context) { | |
| 61 assert ThreadUtils.runningOnUiThread(); | |
| 62 mInputManager = (InputManager) context.getSystemService(Context.INPUT_SE RVICE); | |
|
boliu
2014/03/10 18:27:04
Can this ever be null (say if webview is in a serv
kbalazs
2014/03/12 00:17:36
Done, added checks.
| |
| 63 mIsFetched = false; | |
| 64 mIsPaused = false; | |
|
Ted C
2014/03/08 01:06:00
I just noticed that this is the same calls as resu
| |
| 65 enumerateDevices(); | |
|
Ted C
2014/03/07 20:18:09
I would probably call this initializeDevices
kbalazs
2014/03/12 00:17:36
Done.
| |
| 66 mInputManager.registerInputDeviceListener(this, null); | |
| 67 } | |
| 68 | |
| 69 public void pause() { | |
|
Ted C
2014/03/07 20:18:09
For all non-private methods, you should have javad
kbalazs
2014/03/07 22:40:22
You are right in that if there is more than one ac
Ted C
2014/03/08 01:06:00
Relying on pause and resume is somewhat dangerous
boliu
2014/03/10 18:27:04
What Ted said.
Webview is a view, so Activity lif
kbalazs
2014/03/12 00:17:36
javadoc added.
kbalazs
2014/03/12 00:17:36
Relying on onAttachedToWindow and onDetachedFromWi
| |
| 70 if (mIsPaused) | |
|
Ted C
2014/03/07 20:18:09
in java, braces are always required if the stateme
kbalazs
2014/03/12 00:17:36
Done.
| |
| 71 return; | |
| 72 mIsPaused = true; | |
| 73 synchronized (mDeviceHandlersLock) { | |
| 74 for (int i = 0; i < NUM_WEB_GAMEPADS; ++i) { | |
|
Ted C
2014/03/07 20:18:09
in java land, we use i++ (applies many places)
kbalazs
2014/03/12 00:17:36
Done.
| |
| 75 mDeviceHandlers[i] = null; | |
| 76 } | |
| 77 } | |
| 78 mInputManager.unregisterInputDeviceListener(this); | |
| 79 } | |
| 80 | |
| 81 public void resume() { | |
| 82 if (!mIsPaused) | |
| 83 return; | |
| 84 mIsPaused = false; | |
| 85 enumerateDevices(); | |
| 86 mInputManager.registerInputDeviceListener(this, null); | |
| 87 } | |
| 88 | |
| 89 private void enumerateDevices() { | |
| 90 InputDeviceHandler[] handlers = new InputDeviceHandler[NUM_WEB_GAMEPADS] ; | |
|
Ted C
2014/03/07 20:18:09
I would add the assert runningOnUiThread to here t
kbalazs
2014/03/12 00:17:36
Done.
| |
| 91 int[] ids = mInputManager.getInputDeviceIds(); | |
|
Ted C
2014/03/07 20:18:09
the api doesn't clarify if null is a valid return
kbalazs
2014/03/12 00:17:36
Done.
| |
| 92 int activeDevices = 0; | |
| 93 for (int i = 0; i < ids.length && activeDevices < NUM_WEB_GAMEPADS; ++i) { | |
| 94 InputDevice device = mInputManager.getInputDevice(ids[i]); | |
| 95 if (isGameDevice(device)) { | |
| 96 handlers[activeDevices++] = new InputDeviceHandler(device); | |
| 97 } | |
| 98 } | |
| 99 synchronized (mDeviceHandlersLock) { | |
| 100 mDeviceHandlers = handlers; | |
| 101 } | |
| 102 } | |
| 103 | |
| 104 @Override | |
| 105 public void onInputDeviceAdded(int deviceID) { | |
|
Ted C
2014/03/07 20:18:09
I think you're supposed to use a lower d in ID.
h
kbalazs
2014/03/12 00:17:36
Done.
| |
| 106 ThreadUtils.assertOnUiThread(); | |
| 107 InputDevice device = mInputManager.getInputDevice(deviceID); | |
| 108 if (!isGameDevice(device)) | |
| 109 return; | |
| 110 int index = nextAvailableIndex(); | |
| 111 if (index == -1) | |
| 112 return; | |
| 113 synchronized (mDeviceHandlersLock) { | |
| 114 mDeviceHandlers[index] = new InputDeviceHandler(device); | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 @Override | |
| 119 public void onInputDeviceRemoved(int deviceID) { | |
| 120 ThreadUtils.assertOnUiThread(); | |
| 121 int index = indexForDeviceID(deviceID); | |
| 122 if (index == -1) | |
| 123 return; | |
| 124 synchronized (mDeviceHandlersLock) { | |
| 125 mDeviceHandlers[index] = null; | |
| 126 } | |
| 127 } | |
| 128 | |
| 129 @Override | |
| 130 public void onInputDeviceChanged(int deviceID) { | |
| 131 ThreadUtils.assertOnUiThread(); | |
| 132 int index = indexForDeviceID(deviceID); | |
| 133 if (index == -1) { | |
| 134 index = nextAvailableIndex(); | |
| 135 if (index == -1) return; | |
| 136 } | |
| 137 InputDevice device = mInputManager.getInputDevice(deviceID); | |
| 138 synchronized (mDeviceHandlersLock) { | |
| 139 mDeviceHandlers[index] = null; | |
| 140 if (isGameDevice(device)) { | |
| 141 mDeviceHandlers[index] = new InputDeviceHandler(device); | |
| 142 } | |
| 143 } | |
| 144 } | |
| 145 | |
| 146 public boolean handleMotionEvent(MotionEvent event) { | |
| 147 if (!mIsFetched) | |
| 148 return false; | |
| 149 InputDeviceHandler handler = handlerForDeviceId(event.getDeviceId()); | |
| 150 if (handler == null) | |
| 151 return false; | |
| 152 if (!isGameEvent(event)) | |
| 153 return false; | |
| 154 handler.handleMotionEvent(event); | |
| 155 return true; | |
| 156 } | |
| 157 | |
| 158 public boolean handleKeyEvent(KeyEvent event) { | |
| 159 if (!mIsFetched) | |
| 160 return false; | |
| 161 if (event.getAction() != KeyEvent.ACTION_DOWN && | |
| 162 event.getAction() != KeyEvent.ACTION_UP) { | |
|
Ted C
2014/03/07 20:18:09
+4 indent
kbalazs
2014/03/07 22:40:22
This is apparently 4, could you elaborate on how t
Ted C
2014/03/08 01:06:00
+4 indent was meant to say it needs to be indented
kbalazs
2014/03/11 20:38:34
I see. For the record I don't find this rule neith
Ted C
2014/03/11 20:42:47
It's in the referenced android style guide:
http:/
kbalazs
2014/03/12 00:17:36
Ah, ok, I missed it. Done.
| |
| 163 return false; | |
| 164 } | |
| 165 InputDeviceHandler handler = handlerForDeviceId(event.getDeviceId()); | |
| 166 if (handler == null) | |
| 167 return false; | |
| 168 int keyCode = event.getKeyCode(); | |
| 169 if (!isGameKey(keyCode)) | |
| 170 return false; | |
| 171 boolean isDown = event.getAction() == KeyEvent.ACTION_DOWN; | |
| 172 handler.handleKeyEvent(keyCode, isDown, event.getEventTime()); | |
| 173 return true; | |
| 174 } | |
| 175 | |
| 176 @CalledByNative | |
| 177 public static GamepadAdapter attach(long nativeDataFetcher) { | |
|
Ted C
2014/03/07 20:18:09
why public?
kbalazs
2014/03/07 22:40:22
If I remember correctly the jni generator only all
Ted C
2014/03/08 01:06:00
Ah, yeah the jni generator definitely allows you t
kbalazs
2014/03/12 00:17:36
Now I remember, the presubmit complained about pri
| |
| 178 assert instance != null; | |
| 179 instance.mNativeDataFetcher = nativeDataFetcher; | |
| 180 instance.mIsFetched = true; | |
| 181 return instance; | |
|
jdduke (slow)
2014/03/07 23:33:27
Could we rename mIsFetched to something like |mHas
kbalazs
2014/03/12 00:17:36
Done.
| |
| 182 } | |
| 183 | |
| 184 // Called on polling thread. | |
| 185 @CalledByNative | |
| 186 private void getGamepadData() { | |
| 187 synchronized (mDeviceHandlersLock) { | |
| 188 for (int i = 0; i < NUM_WEB_GAMEPADS; ++i) { | |
| 189 if (mDeviceHandlers[i] == null) { | |
| 190 nativeRefreshDevice(mNativeDataFetcher, i, false, null, null , 0, null, null); | |
| 191 } else { | |
| 192 InputDeviceHandler handler = mDeviceHandlers[i]; | |
| 193 WebGamepadData data = handler.produceWebData(); | |
| 194 nativeRefreshDevice(mNativeDataFetcher, i, true, | |
| 195 data.id, data.mapping, handler.getTimestamp(), | |
| 196 data.axes, data.buttons); | |
| 197 } | |
| 198 } | |
| 199 } | |
| 200 } | |
| 201 | |
| 202 @CalledByNative | |
| 203 public void setFetched(boolean fetched) { | |
| 204 mIsFetched = fetched; | |
| 205 } | |
| 206 | |
| 207 int nextAvailableIndex() { | |
| 208 for (int i = 0; i < mDeviceHandlers.length; ++i) { | |
| 209 if (mDeviceHandlers[i] == null) | |
| 210 return i; | |
| 211 } | |
| 212 return -1; | |
| 213 } | |
| 214 | |
| 215 int indexForDeviceID(int deviceID) { | |
|
Ted C
2014/03/07 20:18:09
indexForDeviceId
| |
| 216 for (int i = 0; i < mDeviceHandlers.length; ++i) { | |
| 217 if (mDeviceHandlers[i] != null && | |
| 218 mDeviceHandlers[i].getInputDevice().getId() == deviceID) | |
|
Ted C
2014/03/07 20:18:09
+4 indent and needs braces
| |
| 219 return i; | |
| 220 } | |
| 221 return -1; | |
| 222 } | |
| 223 | |
| 224 InputDeviceHandler handlerForDeviceId(int deviceID) { | |
| 225 int index = indexForDeviceID(deviceID); | |
| 226 return index == -1 ? null : mDeviceHandlers[index]; | |
| 227 } | |
| 228 | |
| 229 private static boolean isGameDevice(InputDevice device) { | |
| 230 return (device.getSources() & InputDevice.SOURCE_JOYSTICK) != 0; | |
| 231 } | |
| 232 | |
| 233 private static boolean isGameEvent(MotionEvent event) { | |
| 234 return (event.getSource() & InputDevice.SOURCE_JOYSTICK) != 0 && | |
| 235 event.getAction() == MotionEvent.ACTION_MOVE; | |
| 236 } | |
| 237 | |
| 238 private static boolean isGameKey(int keyCode) { | |
| 239 switch (keyCode) { | |
| 240 case KeyEvent.KEYCODE_DPAD_UP: | |
| 241 case KeyEvent.KEYCODE_DPAD_DOWN: | |
| 242 case KeyEvent.KEYCODE_DPAD_LEFT: | |
| 243 case KeyEvent.KEYCODE_DPAD_RIGHT: | |
| 244 case KeyEvent.KEYCODE_DPAD_CENTER: | |
| 245 return true; | |
| 246 default: | |
| 247 return KeyEvent.isGamepadButton(keyCode); | |
| 248 } | |
| 249 } | |
| 250 | |
| 251 private static class InputDeviceHandler { | |
| 252 private final InputDevice mDevice; | |
| 253 private final SparseArray<Float> mAxes; | |
| 254 private final SparseArray<Boolean> mButtons = new SparseArray<Boolean>() ; | |
|
Ted C
2014/03/07 20:18:09
Should be able to use SparseBooleanArray here.
I
kbalazs
2014/03/12 00:17:36
I still wanted something like an associative array
| |
| 255 private final GamepadDataMapper mMapper; | |
| 256 private long mTimestamp; | |
| 257 private final Object mLock = new Object(); | |
| 258 | |
| 259 InputDevice getInputDevice() { return mDevice; } | |
| 260 long getTimestamp() { return mTimestamp; } | |
| 261 | |
| 262 public InputDeviceHandler(InputDevice device) { | |
| 263 assert isGameDevice(device); | |
| 264 mDevice = device; | |
| 265 mMapper = GamepadDataMapper.createDataMapper(device.getName()); | |
| 266 List<MotionRange> ranges = device.getMotionRanges(); | |
| 267 mAxes = new SparseArray<Float>(ranges.size()); | |
| 268 for (MotionRange range : ranges) { | |
| 269 if ((range.getSource() & InputDevice.SOURCE_CLASS_JOYSTICK) != 0 ) { | |
| 270 mAxes.put(range.getAxis(), new Float(0)); | |
| 271 } | |
| 272 } | |
| 273 } | |
| 274 | |
| 275 // Called on UI thread. | |
| 276 void handleMotionEvent(MotionEvent event) { | |
| 277 synchronized (mLock) { | |
| 278 mTimestamp = event.getEventTime(); | |
| 279 for (int i = 0; i < mAxes.size(); ++i) { | |
| 280 final float value = event.getAxisValue(mAxes.keyAt(i)); | |
| 281 mAxes.setValueAt(i, value); | |
|
Ted C
2014/03/07 20:18:09
Hmm...this will result in a new capital Float obje
kbalazs
2014/03/07 22:40:22
IMHO an associative array is quite convenient here
jdduke (slow)
2014/03/07 23:33:27
Any amount of garbage created per frame is undesir
| |
| 282 } | |
| 283 } | |
| 284 } | |
| 285 | |
| 286 // Called on UI thread. | |
| 287 void handleKeyEvent(int keyCode, boolean isDown, long timestamp) { | |
| 288 synchronized (mLock) { | |
| 289 mTimestamp = timestamp; | |
| 290 mButtons.put(keyCode, isDown); | |
| 291 } | |
| 292 } | |
| 293 | |
| 294 // Called on polling thread. | |
| 295 WebGamepadData produceWebData() { | |
| 296 synchronized (mLock) { | |
| 297 return mMapper.map(mAxes, mButtons); | |
|
Ted C
2014/03/07 20:18:09
Is this also called every often? It looks like th
| |
| 298 } | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 private native void nativeRefreshDevice(long nativeGamepadPlatformDataFetche rAndroid, | |
| 303 int index, boolean connected, String id, String mapping, long timest amp, | |
| 304 float[] axes, float[] buttons); | |
| 305 } | |
| OLD | NEW |