OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 } | |
OLD | NEW |