| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 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 org.chromium.base.CalledByNative; | |
| 16 import org.chromium.base.JNINamespace; | |
| 17 import org.chromium.base.WeakContext; | |
| 18 | |
| 19 import java.util.List; | |
| 20 | |
| 21 /** | |
| 22 * Android implementation of the DeviceOrientation API. | |
| 23 */ | |
| 24 @JNINamespace("content") | |
| 25 class DeviceOrientation implements SensorEventListener { | |
| 26 | |
| 27 // These fields are lazily initialized by getHandler(). | |
| 28 private Thread mThread; | |
| 29 private Handler mHandler; | |
| 30 | |
| 31 // The lock to access the mHandler. | |
| 32 private Object mHandlerLock = new Object(); | |
| 33 | |
| 34 // Non-zero if and only if we're listening for events. | |
| 35 // To avoid race conditions on the C++ side, access must be synchronized. | |
| 36 private int mNativePtr; | |
| 37 | |
| 38 // The lock to access the mNativePtr. | |
| 39 private Object mNativePtrLock = new Object(); | |
| 40 | |
| 41 // The gravity vector expressed in the body frame. | |
| 42 private float[] mGravityVector; | |
| 43 | |
| 44 // The geomagnetic vector expressed in the body frame. | |
| 45 private float[] mMagneticFieldVector; | |
| 46 | |
| 47 // Lazily initialized when registering for notifications. | |
| 48 private SensorManager mSensorManager; | |
| 49 | |
| 50 // The only instance of that class and its associated lock. | |
| 51 private static DeviceOrientation sSingleton; | |
| 52 private static Object sSingletonLock = new Object(); | |
| 53 | |
| 54 private DeviceOrientation() { | |
| 55 } | |
| 56 | |
| 57 /** | |
| 58 * Start listening for sensor events. If this object is already listening | |
| 59 * for events, the old callback is unregistered first. | |
| 60 * | |
| 61 * @param nativePtr Value to pass to nativeGotOrientation() for each event. | |
| 62 * @param rateInMilliseconds Requested callback rate in milliseconds. The | |
| 63 * actual rate may be higher. Unwanted events should be ignored. | |
| 64 * @return True on success. | |
| 65 */ | |
| 66 @CalledByNative | |
| 67 public boolean start(int nativePtr, int rateInMilliseconds) { | |
| 68 synchronized (mNativePtrLock) { | |
| 69 stop(); | |
| 70 if (registerForSensors(rateInMilliseconds)) { | |
| 71 mNativePtr = nativePtr; | |
| 72 return true; | |
| 73 } | |
| 74 return false; | |
| 75 } | |
| 76 } | |
| 77 | |
| 78 /** | |
| 79 * Stop listening for sensor events. Always succeeds. | |
| 80 * | |
| 81 * We strictly guarantee that nativeGotOrientation() will not be called | |
| 82 * after this method returns. | |
| 83 */ | |
| 84 @CalledByNative | |
| 85 public void stop() { | |
| 86 synchronized (mNativePtrLock) { | |
| 87 if (mNativePtr != 0) { | |
| 88 mNativePtr = 0; | |
| 89 unregisterForSensors(); | |
| 90 } | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 @Override | |
| 95 public void onAccuracyChanged(Sensor sensor, int accuracy) { | |
| 96 // Nothing | |
| 97 } | |
| 98 | |
| 99 @Override | |
| 100 public void onSensorChanged(SensorEvent event) { | |
| 101 switch (event.sensor.getType()) { | |
| 102 case Sensor.TYPE_ACCELEROMETER: | |
| 103 if (mGravityVector == null) { | |
| 104 mGravityVector = new float[3]; | |
| 105 } | |
| 106 System.arraycopy(event.values, 0, mGravityVector, 0, mGravityVec
tor.length); | |
| 107 break; | |
| 108 | |
| 109 case Sensor.TYPE_MAGNETIC_FIELD: | |
| 110 if (mMagneticFieldVector == null) { | |
| 111 mMagneticFieldVector = new float[3]; | |
| 112 } | |
| 113 System.arraycopy(event.values, 0, mMagneticFieldVector, 0, | |
| 114 mMagneticFieldVector.length); | |
| 115 break; | |
| 116 | |
| 117 default: | |
| 118 // Unexpected | |
| 119 return; | |
| 120 } | |
| 121 | |
| 122 getOrientationUsingGetRotationMatrix(); | |
| 123 } | |
| 124 | |
| 125 void getOrientationUsingGetRotationMatrix() { | |
| 126 if (mGravityVector == null || mMagneticFieldVector == null) { | |
| 127 return; | |
| 128 } | |
| 129 | |
| 130 // Get the rotation matrix. | |
| 131 // The rotation matrix that transforms from the body frame to the earth | |
| 132 // frame. | |
| 133 float[] deviceRotationMatrix = new float[9]; | |
| 134 if (!SensorManager.getRotationMatrix(deviceRotationMatrix, null, mGravit
yVector, | |
| 135 mMagneticFieldVector)) { | |
| 136 return; | |
| 137 } | |
| 138 | |
| 139 // Convert rotation matrix to rotation angles. | |
| 140 // Assuming that the rotations are appied in the order listed at | |
| 141 // 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 | |
| 143 // API. The only conversions are sign changes as follows. The angles ar
e in radians | |
| 144 | |
| 145 float[] rotationAngles = new float[3]; | |
| 146 SensorManager.getOrientation(deviceRotationMatrix, rotationAngles); | |
| 147 | |
| 148 double alpha = Math.toDegrees(-rotationAngles[0]); | |
| 149 while (alpha < 0.0) { | |
| 150 alpha += 360.0; // [0, 360) | |
| 151 } | |
| 152 | |
| 153 double beta = Math.toDegrees(-rotationAngles[1]); | |
| 154 while (beta < -180.0) { | |
| 155 beta += 360.0; // [-180, 180) | |
| 156 } | |
| 157 | |
| 158 double gamma = Math.toDegrees(rotationAngles[2]); | |
| 159 while (gamma < -90.0) { | |
| 160 gamma += 360.0; // [-90, 90) | |
| 161 } | |
| 162 | |
| 163 gotOrientation(alpha, beta, gamma); | |
| 164 } | |
| 165 | |
| 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() { | |
| 176 if (mSensorManager != null) { | |
| 177 return mSensorManager; | |
| 178 } | |
| 179 mSensorManager = (SensorManager)WeakContext.getSystemService(Context.SEN
SOR_SERVICE); | |
| 180 return mSensorManager; | |
| 181 } | |
| 182 | |
| 183 void unregisterForSensors() { | |
| 184 SensorManager sensorManager = getSensorManager(); | |
| 185 if (sensorManager == null) { | |
| 186 return; | |
| 187 } | |
| 188 sensorManager.unregisterListener(this); | |
| 189 } | |
| 190 | |
| 191 boolean registerForSensorType(int type, int rateInMilliseconds) { | |
| 192 SensorManager sensorManager = getSensorManager(); | |
| 193 if (sensorManager == null) { | |
| 194 return false; | |
| 195 } | |
| 196 List<Sensor> sensors = sensorManager.getSensorList(type); | |
| 197 if (sensors.isEmpty()) { | |
| 198 return false; | |
| 199 } | |
| 200 | |
| 201 final int rateInMicroseconds = 1000 * rateInMilliseconds; | |
| 202 // We want to err on the side of getting more events than we need. | |
| 203 final int requestedRate = rateInMicroseconds / 2; | |
| 204 | |
| 205 // TODO(steveblock): Consider handling multiple sensors. | |
| 206 return sensorManager.registerListener(this, sensors.get(0), requestedRat
e, getHandler()); | |
| 207 } | |
| 208 | |
| 209 void gotOrientation(double alpha, double beta, double gamma) { | |
| 210 synchronized (mNativePtrLock) { | |
| 211 if (mNativePtr != 0) { | |
| 212 nativeGotOrientation(mNativePtr, alpha, beta, gamma); | |
| 213 } | |
| 214 } | |
| 215 } | |
| 216 | |
| 217 private Handler getHandler() { | |
| 218 synchronized (mHandlerLock) { | |
| 219 // If we don't have a background thread, start it now. | |
| 220 if (mThread == null) { | |
| 221 mThread = new Thread(new Runnable() { | |
| 222 @Override | |
| 223 public void run() { | |
| 224 Looper.prepare(); | |
| 225 // Our Handler doesn't actually have to do anything, bec
ause | |
| 226 // SensorManager posts directly to the underlying Looper
. | |
| 227 setHandler(new Handler()); | |
| 228 Looper.loop(); | |
| 229 } | |
| 230 }); | |
| 231 mThread.start(); | |
| 232 } | |
| 233 // Wait for the background thread to spin up. | |
| 234 while (mHandler == null) { | |
| 235 try { | |
| 236 mHandlerLock.wait(); | |
| 237 } catch (InterruptedException e) { | |
| 238 // Somebody doesn't want us to wait! That's okay, SensorMana
ger accepts null. | |
| 239 return null; | |
| 240 } | |
| 241 } | |
| 242 return mHandler; | |
| 243 } | |
| 244 } | |
| 245 | |
| 246 private void setHandler(Handler handler) { | |
| 247 synchronized (mHandlerLock) { | |
| 248 mHandler = handler; | |
| 249 mHandlerLock.notify(); | |
| 250 } | |
| 251 } | |
| 252 | |
| 253 @CalledByNative | |
| 254 private static DeviceOrientation getInstance() { | |
| 255 synchronized (sSingletonLock) { | |
| 256 if (sSingleton == null) { | |
| 257 sSingleton = new DeviceOrientation(); | |
| 258 } | |
| 259 return sSingleton; | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 /** | |
| 264 * See chrome/browser/device_orientation/data_fetcher_impl_android.cc | |
| 265 */ | |
| 266 private native void nativeGotOrientation( | |
| 267 int nativeDataFetcherImplAndroid, | |
| 268 double alpha, double beta, double gamma); | |
| 269 } | |
| OLD | NEW |