OLD | NEW |
---|---|
(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 } | |
OLD | NEW |