OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
Peter Beverloo
2013/03/11 17:57:25
It's impossible to review changes in this file giv
timvolodine1
2013/03/12 11:18:06
Done.
| |
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; | |
6 | |
7 import android.content.Context; | |
8 import android.hardware.Sensor; | |
9 import android.hardware.SensorEvent; | |
10 import android.hardware.SensorEventListener; | |
11 import android.hardware.SensorManager; | |
12 import android.os.Handler; | |
13 import android.os.Looper; | |
14 | |
15 import com.google.common.collect.ImmutableMap; | |
16 import com.google.common.collect.ImmutableSet; | |
17 import com.google.common.collect.Sets; | |
18 | |
19 import org.chromium.base.CalledByNative; | |
20 import org.chromium.base.JNINamespace; | |
21 import org.chromium.base.WeakContext; | |
22 | |
23 import java.util.List; | |
24 import java.util.Map; | |
25 import java.util.Set; | |
26 | |
27 /** | |
28 * Android implementation of the DeviceOrientation API. | |
29 */ | |
30 @JNINamespace("content") | |
31 class DeviceMotionAndOrientation implements SensorEventListener { | |
32 | |
33 // These fields are lazily initialized by getHandler(). | |
34 private Thread mThread; | |
35 private Handler mHandler; | |
36 | |
37 // The lock to access the mHandler. | |
38 private Object mHandlerLock = new Object(); | |
39 | |
40 // Non-zero if and only if we're listening for events. | |
41 // To avoid race conditions on the C++ side, access must be synchronized. | |
42 private int mNativePtr; | |
43 | |
44 // The lock to access the mNativePtr. | |
45 private Object mNativePtrLock = new Object(); | |
46 | |
47 // The gravity vector expressed in the body frame. | |
48 private float[] mGravityVector; | |
49 | |
50 // The geomagnetic vector expressed in the body frame. | |
51 private float[] mMagneticFieldVector; | |
52 | |
53 // The linear acceleration vector expressed in the body frame. | |
54 private float[] mAccelerationVector; | |
55 | |
56 // Gyroscope | |
57 private float[] mGyroVector; | |
58 | |
59 // Lazily initialized when registering for notifications. | |
60 private SensorManager mSensorManager; | |
61 | |
62 // The only instance of that class and its associated lock. | |
63 private static DeviceMotionAndOrientation sSingleton; | |
64 private static Object sSingletonLock = new Object(); | |
65 | |
66 /** | |
67 * constants for using in JNI calls, also see | |
68 * content/browser/device_orientation/data_fetcher_impl_android.cc | |
69 * @see #start | |
70 * @see #stop | |
71 */ | |
72 private static enum SpecEventType { | |
73 DEVICE_ORIENTATION(0), | |
74 DEVICE_MOTION(1); | |
75 | |
76 public int value; | |
77 private SpecEventType(int value) { | |
78 this.value = value; | |
79 } | |
80 } | |
81 | |
82 private static ImmutableSet<Integer> DEVICE_ORIENTATION_SENSORS = | |
83 ImmutableSet.of(Sensor.TYPE_ACCELEROMETER, | |
84 Sensor.TYPE_MAGNETIC_FIELD); | |
85 | |
86 private static ImmutableSet<Integer> DEVICE_MOTION_SENSORS = | |
87 ImmutableSet.of(Sensor.TYPE_ACCELEROMETER, | |
88 Sensor.TYPE_LINEAR_ACCELERATION, | |
89 Sensor.TYPE_GYROSCOPE); | |
90 | |
91 private Set<Integer> mActiveSensors = Sets.newHashSet(); | |
92 | |
93 private static Map<Integer, ImmutableSet<Integer>> SENSORS_FOR_EVENT = Immut ableMap.of( | |
94 SpecEventType.DEVICE_ORIENTATION.value, DEVICE_ORIENTATION_SENSORS, | |
95 SpecEventType.DEVICE_MOTION.value, DEVICE_MOTION_SENSORS); | |
96 | |
97 // keep an array of active event types for speed because it is used in @see # | |
98 private boolean[] mIsSpecEventActive = new boolean[SpecEventType.values().le ngth]; | |
99 | |
100 private DeviceMotionAndOrientation() { | |
101 mIsSpecEventActive[SpecEventType.DEVICE_ORIENTATION.value] = false; | |
102 mIsSpecEventActive[SpecEventType.DEVICE_MOTION.value] = false; | |
103 } | |
104 | |
105 /** | |
106 * Start listening for sensor events. If this object is already listening | |
107 * for events, the old callback is unregistered first. | |
108 * | |
109 * @param nativePtr Value to pass to nativeGotOrientation() for each event. | |
110 * @param rateInMilliseconds Requested callback rate in milliseconds. The | |
111 * actual rate may be higher. Unwanted events should be ignored. | |
112 * @param specEventType type of event to listen to, can be 0 for device | |
113 * orientation, and 1 for device motion. | |
114 * @return True on success. | |
115 */ | |
116 @CalledByNative | |
117 public boolean start(int nativePtr, int specEventType, int rateInMillisecond s) { | |
118 Set<Integer> sensorsForEvent = SENSORS_FOR_EVENT.get(specEventType); | |
119 if (sensorsForEvent == null) | |
120 return false; | |
121 | |
122 synchronized (mNativePtrLock) { | |
123 Set<Integer> sensorsToActivate = Sets.newHashSet(sensorsForEvent); | |
124 sensorsToActivate.removeAll(mActiveSensors); | |
125 boolean success = registerForSensors(sensorsToActivate, rateInMillis econds); | |
126 if (success) { | |
127 mNativePtr = nativePtr; | |
128 mIsSpecEventActive[specEventType] = true; | |
129 } | |
130 return success; | |
131 } | |
132 } | |
133 | |
134 /** | |
135 * Stop listening for sensor events. Always succeeds. | |
136 * | |
137 * We strictly guarantee that nativeGotOrientation() will not be called | |
138 * after this method returns. | |
139 */ | |
140 @CalledByNative | |
141 public void stop(int specEventType) { | |
142 Set<Integer> sensorsForEvent = SENSORS_FOR_EVENT.get(specEventType); | |
143 if (sensorsForEvent == null) | |
144 return; | |
145 Set<Integer> sensorsToRemainActive = Sets.newHashSet(); | |
146 | |
147 synchronized (mNativePtrLock) { | |
148 for (SpecEventType specEvent : SpecEventType.values()) { | |
149 if (specEvent.value != specEventType && mIsSpecEventActive[specE vent.value]) { | |
150 sensorsToRemainActive.addAll(SENSORS_FOR_EVENT.get(specEvent .value)); | |
151 } | |
152 } | |
153 Set<Integer> sensorsToDeactivate = Sets.newHashSet(mActiveSensors); | |
154 sensorsToDeactivate.removeAll(sensorsToRemainActive); | |
155 unregisterForSensors(sensorsToDeactivate); | |
156 mNativePtr = 0; | |
157 mIsSpecEventActive[specEventType] = false; | |
158 } | |
159 } | |
160 | |
161 @Override | |
162 public void onAccuracyChanged(Sensor sensor, int accuracy) { | |
163 // Nothing | |
164 } | |
165 | |
166 @Override | |
167 public void onSensorChanged(SensorEvent event) { | |
168 boolean dispatchDeviceOrientation = | |
169 mIsSpecEventActive[SpecEventType.DEVICE_ORIENTATION.value]; | |
170 boolean dispatchDeviceMotion = | |
171 mIsSpecEventActive[SpecEventType.DEVICE_MOTION.value]; | |
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 } | |
OLD | NEW |