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

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: fixed 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
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.content.browser; 5 package org.chromium.content.browser;
6 6
7 import android.content.Context; 7 import android.content.Context;
8 import android.hardware.Sensor; 8 import android.hardware.Sensor;
9 import android.hardware.SensorEvent; 9 import android.hardware.SensorEvent;
10 import android.hardware.SensorEventListener; 10 import android.hardware.SensorEventListener;
11 import android.hardware.SensorManager; 11 import android.hardware.SensorManager;
12 import android.os.Handler; 12 import android.os.Handler;
13 import android.os.Looper; 13 import android.os.Looper;
14 import android.util.Log;
15
16 import com.google.common.collect.ImmutableSet;
17 import com.google.common.collect.Sets;
14 18
15 import org.chromium.base.CalledByNative; 19 import org.chromium.base.CalledByNative;
16 import org.chromium.base.JNINamespace; 20 import org.chromium.base.JNINamespace;
17 import org.chromium.base.WeakContext; 21 import org.chromium.base.WeakContext;
18 22
19 import java.util.List; 23 import java.util.List;
24 import java.util.Set;
20 25
21 /** 26 /**
22 * Android implementation of the DeviceOrientation API. 27 * Android implementation of the device motion and orientation APIs.
23 */ 28 */
24 @JNINamespace("content") 29 @JNINamespace("content")
25 class DeviceOrientation implements SensorEventListener { 30 class DeviceMotionAndOrientation implements SensorEventListener {
31
32 private static final String LOGTAG = "DeviceMotionAndOrientation";
26 33
27 // These fields are lazily initialized by getHandler(). 34 // These fields are lazily initialized by getHandler().
28 private Thread mThread; 35 private Thread mThread;
29 private Handler mHandler; 36 private Handler mHandler;
30 37
31 // The lock to access the mHandler. 38 // The lock to access the mHandler.
32 private Object mHandlerLock = new Object(); 39 private Object mHandlerLock = new Object();
33 40
34 // Non-zero if and only if we're listening for events. 41 // Non-zero if and only if we're listening for events.
35 // To avoid race conditions on the C++ side, access must be synchronized. 42 // To avoid race conditions on the C++ side, access must be synchronized.
36 private int mNativePtr; 43 private int mNativePtr;
37 44
38 // The lock to access the mNativePtr. 45 // The lock to access the mNativePtr.
39 private Object mNativePtrLock = new Object(); 46 private Object mNativePtrLock = new Object();
40 47
41 // The gravity vector expressed in the body frame. 48 // The gravity vector expressed in the body frame.
Peter Beverloo 2013/03/13 16:18:57 This should read "The acceleration vector includin
timvolodine 2013/03/13 17:53:02 Done.
42 private float[] mGravityVector; 49 private float[] mAccelerationVector;
43 50
44 // The geomagnetic vector expressed in the body frame. 51 // The geomagnetic vector expressed in the body frame.
45 private float[] mMagneticFieldVector; 52 private float[] mMagneticFieldVector;
46 53
47 // Lazily initialized when registering for notifications. 54 // Lazily initialized when registering for notifications.
48 private SensorManager mSensorManager; 55 private SensorManager mSensorManager;
49 56
50 // The only instance of that class and its associated lock. 57 // The only instance of that class and its associated lock.
51 private static DeviceOrientation sSingleton; 58 private static DeviceMotionAndOrientation sSingleton;
52 private static Object sSingletonLock = new Object(); 59 private static Object sSingletonLock = new Object();
53 60
54 private DeviceOrientation() { 61 /**
62 * constants for using in JNI calls, also see
63 * content/browser/device_orientation/data_fetcher_impl_android.cc
64 */
65 private static final int DEVICE_ORIENTATION = 0;
66 private static final int DEVICE_MOTION = 1;
67
68 private static ImmutableSet<Integer> DEVICE_ORIENTATION_SENSORS = ImmutableS et.of(
69 Sensor.TYPE_ACCELEROMETER,
70 Sensor.TYPE_MAGNETIC_FIELD);
71
72 private static ImmutableSet<Integer> DEVICE_MOTION_SENSORS = ImmutableSet.of (
73 Sensor.TYPE_ACCELEROMETER,
74 Sensor.TYPE_LINEAR_ACCELERATION,
75 Sensor.TYPE_GYROSCOPE);
76
77 private Set<Integer> mActiveSensors = Sets.newHashSet();
78 private boolean mDeviceMotionIsActive = false;
79 private boolean mDeviceOrientationIsActive = false;
80
81 private DeviceMotionAndOrientation() {
55 } 82 }
56 83
57 /** 84 /**
58 * Start listening for sensor events. If this object is already listening 85 * Start listening for sensor events. If this object is already listening
59 * for events, the old callback is unregistered first. 86 * for events, the old callback is unregistered first.
60 * 87 *
61 * @param nativePtr Value to pass to nativeGotOrientation() for each event. 88 * @param nativePtr Value to pass to nativeGotOrientation() for each event.
62 * @param rateInMilliseconds Requested callback rate in milliseconds. The 89 * @param rateInMilliseconds Requested callback rate in milliseconds. The
63 * actual rate may be higher. Unwanted events should be ignored. 90 * actual rate may be higher. Unwanted events should be ignored.
91 * @param eventType Type of event to listen to, can be either DEVICE_ORIENTA TION or
92 * DEVICE_MOTION.
Peter Beverloo 2013/03/13 16:18:57 nit: 11-space ident? What should this be aligned
timvolodine 2013/03/13 17:53:02 aligned with last row for readability. On 2013/03
64 * @return True on success. 93 * @return True on success.
65 */ 94 */
66 @CalledByNative 95 @CalledByNative
67 public boolean start(int nativePtr, int rateInMilliseconds) { 96 public boolean start(int nativePtr, int eventType, int rateInMilliseconds) {
97 boolean success = false;
68 synchronized (mNativePtrLock) { 98 synchronized (mNativePtrLock) {
69 stop(); 99 switch (eventType) {
70 if (registerForSensors(rateInMilliseconds)) { 100 case DEVICE_ORIENTATION:
101 success = registerSensors(DEVICE_ORIENTATION_SENSORS, rateIn Milliseconds,
102 true);
Peter Beverloo 2013/03/13 16:18:57 nit: indent with eight spaces, so equal to the "="
timvolodine 2013/03/13 17:53:02 Done.
103 break;
104 case DEVICE_MOTION:
105 success = registerSensors(DEVICE_MOTION_SENSORS, rateInMilli seconds, false);
106 break;
107 default:
108 Log.e(LOGTAG, "Unknown event type: " + eventType);
109 return false;
110 }
111 if (success) {
71 mNativePtr = nativePtr; 112 mNativePtr = nativePtr;
72 return true; 113 setActiveEventType(eventType, true);
73 } 114 }
74 return false; 115 return success;
75 } 116 }
76 } 117 }
77 118
78 /** 119 /**
79 * Stop listening for sensor events. Always succeeds. 120 * Stop listening for particular sensor events. Always succeeds.
80 * 121 *
81 * We strictly guarantee that nativeGotOrientation() will not be called 122 * @param eventType Type of event to listen to, can be either DEVICE_ORIENTA TION or
123 * DEVICE_MOTION.
Peter Beverloo 2013/03/13 16:18:57 nit: 11 space indent?
timvolodine 2013/03/13 17:53:02 aligned to last line On 2013/03/13 16:18:57, Peter
124 * We strictly guarantee that the corresponding native*() methods will not b e called
82 * after this method returns. 125 * after this method returns.
83 */ 126 */
84 @CalledByNative 127 @CalledByNative
85 public void stop() { 128 public void stop(int eventType) {
129 Set<Integer> sensorsToRemainActive = Sets.newHashSet();
86 synchronized (mNativePtrLock) { 130 synchronized (mNativePtrLock) {
87 if (mNativePtr != 0) { 131 switch (eventType) {
88 mNativePtr = 0; 132 case DEVICE_ORIENTATION:
89 unregisterForSensors(); 133 if (mDeviceMotionIsActive) {
134 sensorsToRemainActive.addAll(DEVICE_MOTION_SENSORS);
135 }
136 break;
137 case DEVICE_MOTION:
138 if (mDeviceOrientationIsActive) {
139 sensorsToRemainActive.addAll(DEVICE_ORIENTATION_SENSORS) ;
140 }
141 break;
142 default:
143 Log.e(LOGTAG, "Unknown event type: " + eventType);
144 return;
90 } 145 }
146
147 Set<Integer> sensorsToDeactivate = Sets.newHashSet(mActiveSensors);
148 sensorsToDeactivate.removeAll(sensorsToRemainActive);
149 unregisterSensors(sensorsToDeactivate);
150 mNativePtr = 0;
151 setActiveEventType(eventType, false);
91 } 152 }
92 } 153 }
93 154
94 @Override 155 @Override
95 public void onAccuracyChanged(Sensor sensor, int accuracy) { 156 public void onAccuracyChanged(Sensor sensor, int accuracy) {
96 // Nothing 157 // Nothing
97 } 158 }
98 159
99 @Override 160 @Override
100 public void onSensorChanged(SensorEvent event) { 161 public void onSensorChanged(SensorEvent event) {
101 switch (event.sensor.getType()) { 162 switch (event.sensor.getType()) {
102 case Sensor.TYPE_ACCELEROMETER: 163 case Sensor.TYPE_ACCELEROMETER:
103 if (mGravityVector == null) { 164 if (mAccelerationVector == null) {
104 mGravityVector = new float[3]; 165 mAccelerationVector = new float[3];
105 } 166 }
106 System.arraycopy(event.values, 0, mGravityVector, 0, mGravityVec tor.length); 167 System.arraycopy(event.values, 0, mAccelerationVector, 0,
168 mAccelerationVector.length);
Peter Beverloo 2013/03/13 16:18:57 nit: 8 space indent, so with the first "r".
timvolodine 2013/03/13 17:53:02 Done.
169 if (mDeviceMotionIsActive) {
170 gotAccelerationIncludingGravity(mAccelerationVector[0], mAcc elerationVector[1],
171 mAccelerationVector[2]);
Peter Beverloo 2013/03/13 16:18:57 nit: 8 space indent, so with the second "e".
timvolodine 2013/03/13 17:53:02 Done.
172 }
107 break; 173 break;
108 174 case Sensor.TYPE_LINEAR_ACCELERATION:
175 gotAcceleration(event.values[0], event.values[1], event.values[2 ]);
Peter Beverloo 2013/03/13 16:18:57 Should we protect this with if(mDeviceMotionIsActi
timvolodine 2013/03/13 17:53:02 Done.
176 break;
177 case Sensor.TYPE_GYROSCOPE:
178 gotRotationRate(event.values[0], event.values[1], event.values[2 ]);
179 break;
109 case Sensor.TYPE_MAGNETIC_FIELD: 180 case Sensor.TYPE_MAGNETIC_FIELD:
110 if (mMagneticFieldVector == null) { 181 if (mMagneticFieldVector == null) {
111 mMagneticFieldVector = new float[3]; 182 mMagneticFieldVector = new float[3];
112 } 183 }
113 System.arraycopy(event.values, 0, mMagneticFieldVector, 0, 184 System.arraycopy(event.values, 0, mMagneticFieldVector, 0,
114 mMagneticFieldVector.length); 185 mMagneticFieldVector.length);
115 break; 186 break;
116
117 default: 187 default:
118 // Unexpected 188 // Unexpected
119 return; 189 return;
120 } 190 }
121 191
122 getOrientationUsingGetRotationMatrix(); 192 if (mDeviceOrientationIsActive) {
193 getOrientationUsingGetRotationMatrix();
194 }
123 } 195 }
124 196
125 void getOrientationUsingGetRotationMatrix() { 197 private void getOrientationUsingGetRotationMatrix() {
126 if (mGravityVector == null || mMagneticFieldVector == null) { 198 if (mAccelerationVector == null || mMagneticFieldVector == null) {
127 return; 199 return;
128 } 200 }
129 201
130 // Get the rotation matrix. 202 // Get the rotation matrix.
131 // The rotation matrix that transforms from the body frame to the earth 203 // The rotation matrix that transforms from the body frame to the earth
132 // frame. 204 // frame.
133 float[] deviceRotationMatrix = new float[9]; 205 float[] deviceRotationMatrix = new float[9];
134 if (!SensorManager.getRotationMatrix(deviceRotationMatrix, null, mGravit yVector, 206 if (!SensorManager.getRotationMatrix(deviceRotationMatrix, null, mAccele rationVector,
135 mMagneticFieldVector)) { 207 mMagneticFieldVector)) {
136 return; 208 return;
137 } 209 }
138 210
139 // Convert rotation matrix to rotation angles. 211 // Convert rotation matrix to rotation angles.
140 // Assuming that the rotations are appied in the order listed at 212 // Assuming that the rotations are appied in the order listed at
141 // http://developer.android.com/reference/android/hardware/SensorEvent.h tml#values 213 // http://developer.android.com/reference/android/hardware/SensorEvent.h tml#values
142 // the rotations are applied about the same axes and in the same order a s required by the 214 // the rotations are applied about the same axes and in the same order a s required by the
143 // API. The only conversions are sign changes as follows. The angles ar e in radians 215 // API. The only conversions are sign changes as follows. The angles ar e in radians
144 216
(...skipping 11 matching lines...) Expand all
156 } 228 }
157 229
158 double gamma = Math.toDegrees(rotationAngles[2]); 230 double gamma = Math.toDegrees(rotationAngles[2]);
159 while (gamma < -90.0) { 231 while (gamma < -90.0) {
160 gamma += 360.0; // [-90, 90) 232 gamma += 360.0; // [-90, 90)
161 } 233 }
162 234
163 gotOrientation(alpha, beta, gamma); 235 gotOrientation(alpha, beta, gamma);
164 } 236 }
165 237
166 boolean registerForSensors(int rateInMilliseconds) {
167 if (registerForSensorType(Sensor.TYPE_ACCELEROMETER, rateInMilliseconds)
168 && registerForSensorType(Sensor.TYPE_MAGNETIC_FIELD, rateInMilli seconds)) {
169 return true;
170 }
171 unregisterForSensors();
172 return false;
173 }
174
175 private SensorManager getSensorManager() { 238 private SensorManager getSensorManager() {
176 if (mSensorManager != null) { 239 if (mSensorManager != null) {
177 return mSensorManager; 240 return mSensorManager;
178 } 241 }
179 mSensorManager = (SensorManager)WeakContext.getSystemService(Context.SEN SOR_SERVICE); 242 mSensorManager = (SensorManager)WeakContext.getSystemService(Context.SEN SOR_SERVICE);
180 return mSensorManager; 243 return mSensorManager;
181 } 244 }
182 245
183 void unregisterForSensors() { 246 private void setActiveEventType(int eventType, boolean value) {
184 SensorManager sensorManager = getSensorManager(); 247 switch (eventType) {
185 if (sensorManager == null) { 248 case DEVICE_ORIENTATION:
186 return; 249 mDeviceOrientationIsActive = value;
250 return;
251 case DEVICE_MOTION:
252 mDeviceMotionIsActive = value;
253 return;
187 } 254 }
188 sensorManager.unregisterListener(this); 255 }
256
257 /**
258 * @param sensorTypes List of sensors to activate.
259 * @param rateInMilliseconds Intented delay (in milliseconds) between sensor readings.
260 * @param failOnMissingSensor If true the method returns true only if all se nsors could be
261 * activated. When false the method return true i f at least one
262 * sensor in sensorTypes could be activated.
263 */
264 private boolean registerSensors(Iterable<Integer> sensorTypes, int rateInMil liseconds,
265 boolean failOnMissingSensor) {
266 Set<Integer> sensorsToActivate = Sets.newHashSet(sensorTypes);
267 sensorsToActivate.removeAll(mActiveSensors);
268 boolean success = false;
269
270 for (Integer sensorType : sensorsToActivate) {
271 boolean result = registerForSensorType(sensorType, rateInMillisecond s);
272 if (!result && failOnMissingSensor) {
273 // restore the previous state upon failure
274 unregisterSensors(sensorsToActivate);
275 return false;
276 }
277 if (result) {
278 mActiveSensors.add(sensorType);
279 success = true;
280 }
281 }
282 return success;
283 }
284
285 private void unregisterSensors(Iterable<Integer> sensorTypes) {
286 for (Integer sensorType : sensorTypes) {
287 if (mActiveSensors.contains(sensorType)) {
288 List<Sensor> sensors = getSensorManager().getSensorList(sensorTy pe);
289 if (!sensors.isEmpty()) {
290 getSensorManager().unregisterListener(this, sensors.get(0));
291 mActiveSensors.remove(sensorType);
292 }
293 }
294 }
189 } 295 }
190 296
191 boolean registerForSensorType(int type, int rateInMilliseconds) { 297 boolean registerForSensorType(int type, int rateInMilliseconds) {
192 SensorManager sensorManager = getSensorManager(); 298 SensorManager sensorManager = getSensorManager();
193 if (sensorManager == null) { 299 if (sensorManager == null) {
194 return false; 300 return false;
195 } 301 }
196 List<Sensor> sensors = sensorManager.getSensorList(type); 302 List<Sensor> sensors = sensorManager.getSensorList(type);
197 if (sensors.isEmpty()) { 303 if (sensors.isEmpty()) {
198 return false; 304 return false;
199 } 305 }
200 306
201 final int rateInMicroseconds = 1000 * rateInMilliseconds; 307 final int rateInMicroseconds = 1000 * rateInMilliseconds;
202 // We want to err on the side of getting more events than we need. 308 return sensorManager.registerListener(this, sensors.get(0), rateInMicros econds,
203 final int requestedRate = rateInMicroseconds / 2; 309 getHandler());
Peter Beverloo 2013/03/13 16:18:57 nit: 8 space indent.
timvolodine 2013/03/13 17:53:02 Done.
204
205 // TODO(steveblock): Consider handling multiple sensors.
206 return sensorManager.registerListener(this, sensors.get(0), requestedRat e, getHandler());
207 } 310 }
208 311
209 void gotOrientation(double alpha, double beta, double gamma) { 312 private void gotOrientation(double alpha, double beta, double gamma) {
210 synchronized (mNativePtrLock) { 313 synchronized (mNativePtrLock) {
211 if (mNativePtr != 0) { 314 if (mNativePtr != 0) {
212 nativeGotOrientation(mNativePtr, alpha, beta, gamma); 315 nativeGotOrientation(mNativePtr, alpha, beta, gamma);
213 } 316 }
214 } 317 }
215 } 318 }
216 319
320 private void gotAcceleration(double x, double y, double z) {
321 synchronized (mNativePtrLock) {
322 if (mNativePtr != 0) {
323 nativeGotAcceleration(mNativePtr, x, y, z);
324 }
325 }
326 }
327
328 private void gotAccelerationIncludingGravity(double x, double y, double z) {
329 synchronized (mNativePtrLock) {
330 if (mNativePtr != 0) {
331 nativeGotAccelerationIncludingGravity(mNativePtr, x, y, z);
332 }
333 }
334 }
335
336 private void gotRotationRate(double alpha, double beta, double gamma) {
337 synchronized (mNativePtrLock) {
338 if (mNativePtr != 0) {
339 nativeGotRotationRate(mNativePtr, alpha, beta, gamma);
340 }
341 }
342 }
343
217 private Handler getHandler() { 344 private Handler getHandler() {
218 synchronized (mHandlerLock) { 345 synchronized (mHandlerLock) {
219 // If we don't have a background thread, start it now. 346 // If we don't have a background thread, start it now.
220 if (mThread == null) { 347 if (mThread == null) {
221 mThread = new Thread(new Runnable() { 348 mThread = new Thread(new Runnable() {
222 @Override 349 @Override
223 public void run() { 350 public void run() {
224 Looper.prepare(); 351 Looper.prepare();
225 // Our Handler doesn't actually have to do anything, bec ause 352 // Our Handler doesn't actually have to do anything, bec ause
226 // SensorManager posts directly to the underlying Looper . 353 // SensorManager posts directly to the underlying Looper .
(...skipping 17 matching lines...) Expand all
244 } 371 }
245 372
246 private void setHandler(Handler handler) { 373 private void setHandler(Handler handler) {
247 synchronized (mHandlerLock) { 374 synchronized (mHandlerLock) {
248 mHandler = handler; 375 mHandler = handler;
249 mHandlerLock.notify(); 376 mHandlerLock.notify();
250 } 377 }
251 } 378 }
252 379
253 @CalledByNative 380 @CalledByNative
254 private static DeviceOrientation getInstance() { 381 private static DeviceMotionAndOrientation getInstance() {
255 synchronized (sSingletonLock) { 382 synchronized (sSingletonLock) {
256 if (sSingleton == null) { 383 if (sSingleton == null) {
257 sSingleton = new DeviceOrientation(); 384 sSingleton = new DeviceMotionAndOrientation();
258 } 385 }
259 return sSingleton; 386 return sSingleton;
260 } 387 }
261 } 388 }
262 389
263 /** 390 /**
264 * See chrome/browser/device_orientation/data_fetcher_impl_android.cc 391 * Native JNI calls,
392 * see content/browser/device_orientation/data_fetcher_impl_android.cc
393 */
394
395 /**
396 * Orientation of the device with respect to its reference frame.
265 */ 397 */
266 private native void nativeGotOrientation( 398 private native void nativeGotOrientation(
267 int nativeDataFetcherImplAndroid, 399 int nativeDataFetcherImplAndroid,
268 double alpha, double beta, double gamma); 400 double alpha, double beta, double gamma);
269 } 401
402 /**
403 * Linear acceleration without gravity of the device with respect to its bod y frame.
404 */
405 private native void nativeGotAcceleration(
406 int nativeDataFetcherImplAndroid,
407 double x, double y, double z);
408
409 /**
410 * Acceleration including gravity of the device with respect to its body fra me.
411 */
412 private native void nativeGotAccelerationIncludingGravity(
413 int nativeDataFetcherImplAndroid,
414 double x, double y, double z);
415
416 /**
417 * Rotation rate of the device with respect to its body frame.
418 */
419 private native void nativeGotRotationRate(
420 int nativeDataFetcherImplAndroid,
421 double alpha, double beta, double gamma);
422
423 }
OLDNEW
« no previous file with comments | « content/content_jni.gypi ('k') | content/public/android/java/src/org/chromium/content/browser/DeviceOrientation.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698