Chromium Code Reviews| 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 |