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

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

Issue 12771008: Implement java-side browser sensor polling for device motion and device orientation DOM events. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: taking into account the comments Created 7 years, 9 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 (c) 2012 The Chromium Authors. All rights reserved.
timvolodine1 2013/03/12 11:22:40 I did a mv old new, but still no proper diff with
Peter Beverloo 2013/03/12 11:26:39 Ok. This still is unreviewable unfortunately.. Y
Miguel Garcia 2013/03/12 11:40:51 I agree, I am adding some comments but please send
Miguel Garcia 2013/03/12 11:40:51 copyright 2013
timvolodine 2013/03/12 16:12:46 Done.
timvolodine 2013/03/12 16:12:46 Done.
timvolodine 2013/03/12 16:12:46 Done.
timvolodine 2013/03/12 16:12:46 Done.
2
3 package org.chromium.content.browser;
4
5 import android.content.Context;
6 import android.hardware.Sensor;
7 import android.hardware.SensorEvent;
8 import android.hardware.SensorEventListener;
9 import android.hardware.SensorManager;
10 import android.os.Handler;
11 import android.os.Looper;
12
13 import com.google.common.collect.ImmutableMap;
14 import com.google.common.collect.ImmutableSet;
15 import com.google.common.collect.Sets;
16
17 import org.chromium.base.CalledByNative;
18 import org.chromium.base.JNINamespace;
19 import org.chromium.base.WeakContext;
20
21 import java.util.List;
22 import java.util.Map;
23 import java.util.Set;
24
25 /**
26 * Android implementation of the device motion and orientation API.
27 */
28 @JNINamespace("content")
29 class DeviceMotionAndOrientation implements SensorEventListener {
30
31 // These fields are lazily initialized by getHandler().
32 private Thread mThread;
33 private Handler mHandler;
34
35 // The lock to access the mHandler.
36 private Object mHandlerLock = new Object();
37
38 // Non-zero if and only if we're listening for events.
39 // To avoid race conditions on the C++ side, access must be synchronized.
40 private int mNativePtr;
41
42 // The lock to access the mNativePtr.
43 private Object mNativePtrLock = new Object();
44
45 // The gravity vector expressed in the body frame.
46 private float[] mGravityVector;
47
48 // The geomagnetic vector expressed in the body frame.
49 private float[] mMagneticFieldVector;
50
51 // The linear acceleration vector expressed in the body frame.
52 private float[] mAccelerationVector;
53
54 // Gyroscope
55 private float[] mGyroVector;
56
57 // Lazily initialized when registering for notifications.
58 private SensorManager mSensorManager;
59
60 // The only instance of that class and its associated lock.
61 private static DeviceMotionAndOrientation sSingleton;
62 private static Object sSingletonLock = new Object();
63
64 /**
65 * constants for using in JNI calls, also see
66 * content/browser/device_orientation/data_fetcher_impl_android.cc
67 * @see #start
68 * @see #stop
69 */
70 private static enum EventType {
Miguel Garcia 2013/03/12 11:40:51 no enums in Java please, use int constants instead
71 DEVICE_ORIENTATION(0),
Miguel Garcia 2013/03/12 11:40:51 also the C++ version has a TEST element as well
72 DEVICE_MOTION(1);
73
74 public int value;
75 private EventType(int value) {
76 this.value = value;
77 }
78 }
79
80 private static ImmutableSet<Integer> DEVICE_ORIENTATION_SENSORS =
81 ImmutableSet.of(Sensor.TYPE_ACCELEROMETER,
82 Sensor.TYPE_MAGNETIC_FIELD);
83
84 private static ImmutableSet<Integer> DEVICE_MOTION_SENSORS =
85 ImmutableSet.of(Sensor.TYPE_ACCELEROMETER,
86 Sensor.TYPE_LINEAR_ACCELERATION,
87 Sensor.TYPE_GYROSCOPE);
88
89 private Set<Integer> mActiveSensors = Sets.newHashSet();
90
91 private static Map<Integer, ImmutableSet<Integer>> SENSORS_FOR_EVENT = Immut ableMap.of(
92 EventType.DEVICE_ORIENTATION.value, DEVICE_ORIENTATION_SENSORS,
93 EventType.DEVICE_MOTION.value, DEVICE_MOTION_SENSORS);
94
95 // keep an array of active event types for speed because it is used in @see #
96 private boolean[] mIsSpecEventActive = new boolean[EventType.values().length ];
97
98 private DeviceMotionAndOrientation() {
99 mIsSpecEventActive[EventType.DEVICE_ORIENTATION.value] = false;
100 mIsSpecEventActive[EventType.DEVICE_MOTION.value] = false;
101 }
102
103 /**
104 * Start listening for sensor events. If this object is already listening
105 * for events, the old callback is unregistered first.
106 *
107 * @param nativePtr Value to pass to nativeGotOrientation() for each event.
108 * @param rateInMilliseconds Requested callback rate in milliseconds. The
109 * actual rate may be higher. Unwanted events should be ignored.
110 * @param specEventType type of event to listen to, can either 0 for device
111 * orientation, or 1 for device motion.
112 * @return True on success.
113 */
114 @CalledByNative
115 public boolean start(int nativePtr, int eventType, int rateInMilliseconds) {
116 Set<Integer> sensorsForEvent = SENSORS_FOR_EVENT.get(eventType);
117 if (sensorsForEvent == null)
118 return false;
119
120 synchronized (mNativePtrLock) {
121 Set<Integer> sensorsToActivate = Sets.newHashSet(sensorsForEvent);
122 sensorsToActivate.removeAll(mActiveSensors);
123 boolean success = registerForSensors(sensorsToActivate, rateInMillis econds);
124 if (success) {
125 mNativePtr = nativePtr;
126 mIsSpecEventActive[eventType] = true;
127 }
128 return success;
129 }
130 }
131
132 /**
133 * Stop listening for particular sensor events. Always succeeds.
134 *
135 * @param specEventType type of event to listen to, can either 0 for device
136 * orientation, or 1 for device motion.
137 * We strictly guarantee that nativeGotOrientation() will not be called
138 * after this method returns.
139 */
140 @CalledByNative
141 public void stop(int eventType) {
142 if (SENSORS_FOR_EVENT.get(eventType) == null)
143 return;
144 Set<Integer> sensorsToRemainActive = Sets.newHashSet();
145
146 synchronized (mNativePtrLock) {
147 for (EventType event : EventType.values()) {
148 if (event.value != eventType && mIsSpecEventActive[event.value]) {
149 sensorsToRemainActive.addAll(SENSORS_FOR_EVENT.get(event.val ue));
150 }
151 }
152 Set<Integer> sensorsToDeactivate = Sets.newHashSet(mActiveSensors);
153 sensorsToDeactivate.removeAll(sensorsToRemainActive);
154 unregisterForSensors(sensorsToDeactivate);
155 mNativePtr = 0;
156 mIsSpecEventActive[eventType] = false;
157 }
158 }
159
160 @Override
161 public void onAccuracyChanged(Sensor sensor, int accuracy) {
162 // Nothing
163 }
164
165 @Override
166 public void onSensorChanged(SensorEvent event) {
167 boolean dispatchDeviceOrientation =
168 mIsSpecEventActive[EventType.DEVICE_ORIENTATION.value];
169 boolean dispatchDeviceMotion =
170 mIsSpecEventActive[EventType.DEVICE_MOTION.value];
171
172 switch (event.sensor.getType()) {
173 case Sensor.TYPE_ACCELEROMETER:
174 if (mGravityVector == null) {
175 mGravityVector = new float[3];
176 }
177 System.arraycopy(event.values, 0, mGravityVector, 0, mGravityVec tor.length);
178 if (dispatchDeviceMotion)
179 gotAccelerationIncludingGravity(mGravityVector[0], mGravityV ector[1],
180 mGravityVector[2]);
181 break;
182 case Sensor.TYPE_LINEAR_ACCELERATION:
183 if (mAccelerationVector == null) {
184 mAccelerationVector = new float[3];
185 }
186 System.arraycopy(event.values, 0, mAccelerationVector, 0,
187 mAccelerationVector.length);
188 if (dispatchDeviceMotion)
189 gotAcceleration(mAccelerationVector[0], mAccelerationVector[ 1],
190 mAccelerationVector[2]);
191 break;
192
193 case Sensor.TYPE_GYROSCOPE:
194 if (mGyroVector == null) {
195 mGyroVector = new float[3];
196 }
197 System.arraycopy(event.values, 0, mGyroVector, 0, mGyroVector.le ngth);
198 if (dispatchDeviceMotion)
199 gotRotationRate(mGyroVector[0], mGyroVector[1], mGyroVector[ 2]);
200 break;
201
202 case Sensor.TYPE_MAGNETIC_FIELD:
203 if (mMagneticFieldVector == null) {
204 mMagneticFieldVector = new float[3];
205 }
206 System.arraycopy(event.values, 0, mMagneticFieldVector, 0,
207 mMagneticFieldVector.length);
208 break;
209
210 default:
211 // Unexpected
212 return;
213 }
214
215 if (dispatchDeviceOrientation)
216 getOrientationUsingGetRotationMatrix();
217 }
218
219 void getOrientationUsingGetRotationMatrix() {
220 if (mGravityVector == null || mMagneticFieldVector == null) {
221 return;
222 }
223
224 // Get the rotation matrix.
225 // The rotation matrix that transforms from the body frame to the earth
226 // frame.
227 float[] deviceRotationMatrix = new float[9];
228 if (!SensorManager.getRotationMatrix(deviceRotationMatrix, null, mGravit yVector,
229 mMagneticFieldVector)) {
230 return;
231 }
232
233 // Convert rotation matrix to rotation angles.
234 // Assuming that the rotations are appied in the order listed at
235 // http://developer.android.com/reference/android/hardware/SensorEvent.h tml#values
236 // the rotations are applied about the same axes and in the same order a s required by the
237 // API. The only conversions are sign changes as follows. The angles ar e in radians
238
239 float[] rotationAngles = new float[3];
240 SensorManager.getOrientation(deviceRotationMatrix, rotationAngles);
241
242 double alpha = Math.toDegrees(-rotationAngles[0]);
243 while (alpha < 0.0) {
244 alpha += 360.0; // [0, 360)
245 }
246
247 double beta = Math.toDegrees(-rotationAngles[1]);
248 while (beta < -180.0) {
249 beta += 360.0; // [-180, 180)
250 }
251
252 double gamma = Math.toDegrees(rotationAngles[2]);
253 while (gamma < -90.0) {
254 gamma += 360.0; // [-90, 90)
255 }
256
257 gotOrientation(alpha, beta, gamma);
258 }
259
260 private SensorManager getSensorManager() {
261 if (mSensorManager != null) {
262 return mSensorManager;
263 }
264 mSensorManager = (SensorManager)WeakContext.getSystemService(Context.SEN SOR_SERVICE);
265 return mSensorManager;
266 }
267
268 private boolean registerForSensors(Iterable<Integer> sensorTypes, int rateIn Milliseconds) {
269 for (Integer sensorType : sensorTypes) {
270 if (!mActiveSensors.contains(sensorType)) {
271 if (!registerForSensorType(sensorType, rateInMilliseconds)) {
272 // restore the previous state upon failure
273 unregisterForSensors(sensorTypes);
274 return false;
275 }
276 }
277 mActiveSensors.add(sensorType);
278 }
279 return true;
280 }
281
282 private void unregisterForSensors(Iterable<Integer> sensorTypes) {
283 for (Integer sensorType : sensorTypes) {
284 if (mActiveSensors.contains(sensorType)) {
285 getSensorManager().unregisterListener(this,
286 getSensorManager().getDefaultSensor(sensorType));
287 mActiveSensors.remove(sensorType);
288 }
289 }
290 }
291
292 boolean registerForSensorType(int type, int rateInMilliseconds) {
293 SensorManager sensorManager = getSensorManager();
294 if (sensorManager == null) {
295 return false;
296 }
297 Sensor defaultSensor = sensorManager.getDefaultSensor(type);
298 if (defaultSensor == null) {
299 return false;
300 }
301
302 final int rateInMicroseconds = 1000 * rateInMilliseconds;
303 return sensorManager.registerListener(this, defaultSensor, rateInMicrose conds,
304 getHandler());
305 }
306
307 void gotOrientation(double alpha, double beta, double gamma) {
308 synchronized (mNativePtrLock) {
309 if (mNativePtr != 0) {
310 nativeGotOrientation(mNativePtr, alpha, beta, gamma);
311 }
312 }
313 }
314
315 void gotAcceleration(double x, double y, double z) {
316 synchronized (mNativePtrLock) {
317 if (mNativePtr != 0) {
318 nativeGotAcceleration(mNativePtr, x, y, z);
319 }
320 }
321 }
322
323 void gotAccelerationIncludingGravity(double x, double y, double z) {
324 synchronized (mNativePtrLock) {
325 if (mNativePtr != 0) {
326 nativeGotAccelerationIncludingGravity(mNativePtr, x, y, z);
327 }
328 }
329 }
330
331 void gotRotationRate(double alpha, double beta, double gamma) {
332 synchronized (mNativePtrLock) {
333 if (mNativePtr != 0) {
334 nativeGotRotationRate(mNativePtr, alpha, beta, gamma);
335 }
336 }
337 }
338
339 private Handler getHandler() {
340 synchronized (mHandlerLock) {
341 // If we don't have a background thread, start it now.
342 if (mThread == null) {
343 mThread = new Thread(new Runnable() {
344 @Override
345 public void run() {
346 Looper.prepare();
347 // Our Handler doesn't actually have to do anything, bec ause
348 // SensorManager posts directly to the underlying Looper .
349 setHandler(new Handler());
350 Looper.loop();
351 }
352 });
353 mThread.start();
354 }
355 // Wait for the background thread to spin up.
356 while (mHandler == null) {
357 try {
358 mHandlerLock.wait();
359 } catch (InterruptedException e) {
360 // Somebody doesn't want us to wait! That's okay, SensorMana ger accepts null.
361 return null;
362 }
363 }
364 return mHandler;
365 }
366 }
367
368 private void setHandler(Handler handler) {
369 synchronized (mHandlerLock) {
370 mHandler = handler;
371 mHandlerLock.notify();
372 }
373 }
374
375 @CalledByNative
376 private static DeviceMotionAndOrientation getInstance() {
377 synchronized (sSingletonLock) {
378 if (sSingleton == null) {
379 sSingleton = new DeviceMotionAndOrientation();
380 }
381 return sSingleton;
382 }
383 }
384
385 /**
386 * Native JNI calls,
387 * see content/browser/device_orientation/data_fetcher_impl_android.cc
388 */
389 private native void nativeGotOrientation(
390 int nativeDataFetcherImplAndroid,
391 double alpha, double beta, double gamma);
392
393 /**
394 * linear acceleration w/o gravity
395 */
396 private native void nativeGotAcceleration(
397 int nativeDataFetcherImplAndroid,
398 double x, double y, double z);
399
400 /**
401 * acceleration including gravity
402 */
403 private native void nativeGotAccelerationIncludingGravity(
404 int nativeDataFetcherImplAndroid,
405 double x, double y, double z);
406
407 /**
408 * gyroscope
409 */
410 private native void nativeGotRotationRate(
411 int nativeDataFetcherImplAndroid,
412 double alpha, double beta, double gamma);
413
414 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698