| Index: content/public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java
|
| diff --git a/content/public/android/java/src/org/chromium/content/browser/DeviceOrientation.java b/content/public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java
|
| similarity index 35%
|
| rename from content/public/android/java/src/org/chromium/content/browser/DeviceOrientation.java
|
| rename to content/public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java
|
| index a77cd28b27ab6901f6a12f7a2e4e012b3e7c8f71..866884882fa6e197b0ec75c8a91735ae36f1dbc5 100644
|
| --- a/content/public/android/java/src/org/chromium/content/browser/DeviceOrientation.java
|
| +++ b/content/public/android/java/src/org/chromium/content/browser/DeviceMotionAndOrientation.java
|
| @@ -1,4 +1,4 @@
|
| -// Copyright (c) 2012 The Chromium Authors. All rights reserved.
|
| +// Copyright (c) 2013 The Chromium Authors. All rights reserved.
|
| // Use of this source code is governed by a BSD-style license that can be
|
| // found in the LICENSE file.
|
|
|
| @@ -11,18 +11,26 @@ import android.hardware.SensorEventListener;
|
| import android.hardware.SensorManager;
|
| import android.os.Handler;
|
| import android.os.Looper;
|
| +import android.util.Log;
|
| +
|
| +import com.google.common.annotations.VisibleForTesting;
|
| +import com.google.common.collect.ImmutableSet;
|
| +import com.google.common.collect.Sets;
|
|
|
| import org.chromium.base.CalledByNative;
|
| import org.chromium.base.JNINamespace;
|
| import org.chromium.base.WeakContext;
|
|
|
| import java.util.List;
|
| +import java.util.Set;
|
|
|
| /**
|
| - * Android implementation of the DeviceOrientation API.
|
| + * Android implementation of the device motion and orientation APIs.
|
| */
|
| @JNINamespace("content")
|
| -class DeviceOrientation implements SensorEventListener {
|
| +class DeviceMotionAndOrientation implements SensorEventListener {
|
| +
|
| + private static final String TAG = "DeviceMotionAndOrientation";
|
|
|
| // These fields are lazily initialized by getHandler().
|
| private Thread mThread;
|
| @@ -38,20 +46,41 @@ class DeviceOrientation implements SensorEventListener {
|
| // The lock to access the mNativePtr.
|
| private Object mNativePtrLock = new Object();
|
|
|
| - // The gravity vector expressed in the body frame.
|
| - private float[] mGravityVector;
|
| + // The acceleration vector including gravity expressed in the body frame.
|
| + private float[] mAccelerationVector;
|
|
|
| // The geomagnetic vector expressed in the body frame.
|
| private float[] mMagneticFieldVector;
|
|
|
| // Lazily initialized when registering for notifications.
|
| - private SensorManager mSensorManager;
|
| + private SensorManagerProxy mSensorManagerProxy;
|
|
|
| // The only instance of that class and its associated lock.
|
| - private static DeviceOrientation sSingleton;
|
| + private static DeviceMotionAndOrientation sSingleton;
|
| private static Object sSingletonLock = new Object();
|
|
|
| - private DeviceOrientation() {
|
| + /**
|
| + * constants for using in JNI calls, also see
|
| + * content/browser/device_orientation/data_fetcher_impl_android.cc
|
| + */
|
| + static final int DEVICE_ORIENTATION = 0;
|
| + static final int DEVICE_MOTION = 1;
|
| +
|
| + static final ImmutableSet<Integer> DEVICE_ORIENTATION_SENSORS = ImmutableSet.of(
|
| + Sensor.TYPE_ACCELEROMETER,
|
| + Sensor.TYPE_MAGNETIC_FIELD);
|
| +
|
| + static final ImmutableSet<Integer> DEVICE_MOTION_SENSORS = ImmutableSet.of(
|
| + Sensor.TYPE_ACCELEROMETER,
|
| + Sensor.TYPE_LINEAR_ACCELERATION,
|
| + Sensor.TYPE_GYROSCOPE);
|
| +
|
| + @VisibleForTesting
|
| + final Set<Integer> mActiveSensors = Sets.newHashSet();
|
| + boolean mDeviceMotionIsActive = false;
|
| + boolean mDeviceOrientationIsActive = false;
|
| +
|
| + protected DeviceMotionAndOrientation() {
|
| }
|
|
|
| /**
|
| @@ -61,32 +90,70 @@ class DeviceOrientation implements SensorEventListener {
|
| * @param nativePtr Value to pass to nativeGotOrientation() for each event.
|
| * @param rateInMilliseconds Requested callback rate in milliseconds. The
|
| * actual rate may be higher. Unwanted events should be ignored.
|
| + * @param eventType Type of event to listen to, can be either DEVICE_ORIENTATION or
|
| + * DEVICE_MOTION.
|
| * @return True on success.
|
| */
|
| @CalledByNative
|
| - public boolean start(int nativePtr, int rateInMilliseconds) {
|
| + public boolean start(int nativePtr, int eventType, int rateInMilliseconds) {
|
| + boolean success = false;
|
| synchronized (mNativePtrLock) {
|
| - stop();
|
| - if (registerForSensors(rateInMilliseconds)) {
|
| + switch (eventType) {
|
| + case DEVICE_ORIENTATION:
|
| + success = registerSensors(DEVICE_ORIENTATION_SENSORS, rateInMilliseconds,
|
| + true);
|
| + break;
|
| + case DEVICE_MOTION:
|
| + // note: device motion spec does not require all sensors to be available
|
| + success = registerSensors(DEVICE_MOTION_SENSORS, rateInMilliseconds, false);
|
| + break;
|
| + default:
|
| + Log.e(TAG, "Unknown event type: " + eventType);
|
| + return false;
|
| + }
|
| + if (success) {
|
| mNativePtr = nativePtr;
|
| - return true;
|
| + setEventTypeActive(eventType, true);
|
| }
|
| - return false;
|
| + return success;
|
| }
|
| }
|
|
|
| /**
|
| - * Stop listening for sensor events. Always succeeds.
|
| + * Stop listening to sensors for a given event type. Ensures that sensors are not disabled
|
| + * if they are still in use by a different event type.
|
| *
|
| - * We strictly guarantee that nativeGotOrientation() will not be called
|
| + * @param eventType Type of event to listen to, can be either DEVICE_ORIENTATION or
|
| + * DEVICE_MOTION.
|
| + * We strictly guarantee that the corresponding native*() methods will not be called
|
| * after this method returns.
|
| */
|
| @CalledByNative
|
| - public void stop() {
|
| + public void stop(int eventType) {
|
| + Set<Integer> sensorsToRemainActive = Sets.newHashSet();
|
| synchronized (mNativePtrLock) {
|
| - if (mNativePtr != 0) {
|
| + switch (eventType) {
|
| + case DEVICE_ORIENTATION:
|
| + if (mDeviceMotionIsActive) {
|
| + sensorsToRemainActive.addAll(DEVICE_MOTION_SENSORS);
|
| + }
|
| + break;
|
| + case DEVICE_MOTION:
|
| + if (mDeviceOrientationIsActive) {
|
| + sensorsToRemainActive.addAll(DEVICE_ORIENTATION_SENSORS);
|
| + }
|
| + break;
|
| + default:
|
| + Log.e(TAG, "Unknown event type: " + eventType);
|
| + return;
|
| + }
|
| +
|
| + Set<Integer> sensorsToDeactivate = Sets.newHashSet(mActiveSensors);
|
| + sensorsToDeactivate.removeAll(sensorsToRemainActive);
|
| + unregisterSensors(sensorsToDeactivate);
|
| + setEventTypeActive(eventType, false);
|
| + if (mActiveSensors.isEmpty()) {
|
| mNativePtr = 0;
|
| - unregisterForSensors();
|
| }
|
| }
|
| }
|
| @@ -98,32 +165,53 @@ class DeviceOrientation implements SensorEventListener {
|
|
|
| @Override
|
| public void onSensorChanged(SensorEvent event) {
|
| - switch (event.sensor.getType()) {
|
| + sensorChanged(event.sensor.getType(), event.values);
|
| + }
|
| +
|
| + @VisibleForTesting
|
| + void sensorChanged(int type, float[] values) {
|
| +
|
| + switch (type) {
|
| case Sensor.TYPE_ACCELEROMETER:
|
| - if (mGravityVector == null) {
|
| - mGravityVector = new float[3];
|
| + if (mAccelerationVector == null) {
|
| + mAccelerationVector = new float[3];
|
| + }
|
| + System.arraycopy(values, 0, mAccelerationVector, 0,
|
| + mAccelerationVector.length);
|
| + if (mDeviceMotionIsActive) {
|
| + gotAccelerationIncludingGravity(mAccelerationVector[0], mAccelerationVector[1],
|
| + mAccelerationVector[2]);
|
| + }
|
| + break;
|
| + case Sensor.TYPE_LINEAR_ACCELERATION:
|
| + if (mDeviceMotionIsActive) {
|
| + gotAcceleration(values[0], values[1], values[2]);
|
| + }
|
| + break;
|
| + case Sensor.TYPE_GYROSCOPE:
|
| + if (mDeviceMotionIsActive) {
|
| + gotRotationRate(values[0], values[1], values[2]);
|
| }
|
| - System.arraycopy(event.values, 0, mGravityVector, 0, mGravityVector.length);
|
| break;
|
| -
|
| case Sensor.TYPE_MAGNETIC_FIELD:
|
| if (mMagneticFieldVector == null) {
|
| mMagneticFieldVector = new float[3];
|
| }
|
| - System.arraycopy(event.values, 0, mMagneticFieldVector, 0,
|
| - mMagneticFieldVector.length);
|
| + System.arraycopy(values, 0, mMagneticFieldVector, 0,
|
| + mMagneticFieldVector.length);
|
| break;
|
| -
|
| default:
|
| // Unexpected
|
| return;
|
| }
|
|
|
| - getOrientationUsingGetRotationMatrix();
|
| + if (mDeviceOrientationIsActive) {
|
| + getOrientationUsingGetRotationMatrix();
|
| + }
|
| }
|
|
|
| - void getOrientationUsingGetRotationMatrix() {
|
| - if (mGravityVector == null || mMagneticFieldVector == null) {
|
| + private void getOrientationUsingGetRotationMatrix() {
|
| + if (mAccelerationVector == null || mMagneticFieldVector == null) {
|
| return;
|
| }
|
|
|
| @@ -131,7 +219,7 @@ class DeviceOrientation implements SensorEventListener {
|
| // The rotation matrix that transforms from the body frame to the earth
|
| // frame.
|
| float[] deviceRotationMatrix = new float[9];
|
| - if (!SensorManager.getRotationMatrix(deviceRotationMatrix, null, mGravityVector,
|
| + if (!SensorManager.getRotationMatrix(deviceRotationMatrix, null, mAccelerationVector,
|
| mMagneticFieldVector)) {
|
| return;
|
| }
|
| @@ -163,53 +251,108 @@ class DeviceOrientation implements SensorEventListener {
|
| gotOrientation(alpha, beta, gamma);
|
| }
|
|
|
| - boolean registerForSensors(int rateInMilliseconds) {
|
| - if (registerForSensorType(Sensor.TYPE_ACCELEROMETER, rateInMilliseconds)
|
| - && registerForSensorType(Sensor.TYPE_MAGNETIC_FIELD, rateInMilliseconds)) {
|
| - return true;
|
| + private SensorManagerProxy getSensorManagerProxy() {
|
| + if (mSensorManagerProxy != null) {
|
| + return mSensorManagerProxy;
|
| }
|
| - unregisterForSensors();
|
| - return false;
|
| + SensorManager sensorManager = (SensorManager)WeakContext.getSystemService(
|
| + Context.SENSOR_SERVICE);
|
| + if (sensorManager != null) {
|
| + mSensorManagerProxy = new SensorManagerProxyImpl(sensorManager);
|
| + }
|
| + return mSensorManagerProxy;
|
| + }
|
| +
|
| + @VisibleForTesting
|
| + void setSensorManagerProxy(SensorManagerProxy sensorManagerProxy) {
|
| + mSensorManagerProxy = sensorManagerProxy;
|
| }
|
|
|
| - private SensorManager getSensorManager() {
|
| - if (mSensorManager != null) {
|
| - return mSensorManager;
|
| + private void setEventTypeActive(int eventType, boolean value) {
|
| + switch (eventType) {
|
| + case DEVICE_ORIENTATION:
|
| + mDeviceOrientationIsActive = value;
|
| + return;
|
| + case DEVICE_MOTION:
|
| + mDeviceMotionIsActive = value;
|
| + return;
|
| }
|
| - mSensorManager = (SensorManager)WeakContext.getSystemService(Context.SENSOR_SERVICE);
|
| - return mSensorManager;
|
| }
|
|
|
| - void unregisterForSensors() {
|
| - SensorManager sensorManager = getSensorManager();
|
| - if (sensorManager == null) {
|
| - return;
|
| + /**
|
| + * @param sensorTypes List of sensors to activate.
|
| + * @param rateInMilliseconds Intended delay (in milliseconds) between sensor readings.
|
| + * @param failOnMissingSensor If true the method returns true only if all sensors could be
|
| + * activated. When false the method return true if at least one
|
| + * sensor in sensorTypes could be activated.
|
| + */
|
| + private boolean registerSensors(Iterable<Integer> sensorTypes, int rateInMilliseconds,
|
| + boolean failOnMissingSensor) {
|
| + Set<Integer> sensorsToActivate = Sets.newHashSet(sensorTypes);
|
| + sensorsToActivate.removeAll(mActiveSensors);
|
| + boolean success = false;
|
| +
|
| + for (Integer sensorType : sensorsToActivate) {
|
| + boolean result = registerForSensorType(sensorType, rateInMilliseconds);
|
| + if (!result && failOnMissingSensor) {
|
| + // restore the previous state upon failure
|
| + unregisterSensors(sensorsToActivate);
|
| + return false;
|
| + }
|
| + if (result) {
|
| + mActiveSensors.add(sensorType);
|
| + success = true;
|
| + }
|
| + }
|
| + return success;
|
| + }
|
| +
|
| + private void unregisterSensors(Iterable<Integer> sensorTypes) {
|
| + for (Integer sensorType : sensorTypes) {
|
| + if (mActiveSensors.contains(sensorType)) {
|
| + getSensorManagerProxy().unregisterListener(this, sensorType);
|
| + mActiveSensors.remove(sensorType);
|
| + }
|
| }
|
| - sensorManager.unregisterListener(this);
|
| }
|
|
|
| - boolean registerForSensorType(int type, int rateInMilliseconds) {
|
| - SensorManager sensorManager = getSensorManager();
|
| + private boolean registerForSensorType(int type, int rateInMilliseconds) {
|
| + SensorManagerProxy sensorManager = getSensorManagerProxy();
|
| if (sensorManager == null) {
|
| return false;
|
| }
|
| - List<Sensor> sensors = sensorManager.getSensorList(type);
|
| - if (sensors.isEmpty()) {
|
| - return false;
|
| + final int rateInMicroseconds = 1000 * rateInMilliseconds;
|
| + return sensorManager.registerListener(this, type, rateInMicroseconds, getHandler());
|
| + }
|
| +
|
| + protected void gotOrientation(double alpha, double beta, double gamma) {
|
| + synchronized (mNativePtrLock) {
|
| + if (mNativePtr != 0) {
|
| + nativeGotOrientation(mNativePtr, alpha, beta, gamma);
|
| + }
|
| }
|
| + }
|
|
|
| - final int rateInMicroseconds = 1000 * rateInMilliseconds;
|
| - // We want to err on the side of getting more events than we need.
|
| - final int requestedRate = rateInMicroseconds / 2;
|
| + protected void gotAcceleration(double x, double y, double z) {
|
| + synchronized (mNativePtrLock) {
|
| + if (mNativePtr != 0) {
|
| + nativeGotAcceleration(mNativePtr, x, y, z);
|
| + }
|
| + }
|
| + }
|
|
|
| - // TODO(steveblock): Consider handling multiple sensors.
|
| - return sensorManager.registerListener(this, sensors.get(0), requestedRate, getHandler());
|
| + protected void gotAccelerationIncludingGravity(double x, double y, double z) {
|
| + synchronized (mNativePtrLock) {
|
| + if (mNativePtr != 0) {
|
| + nativeGotAccelerationIncludingGravity(mNativePtr, x, y, z);
|
| + }
|
| + }
|
| }
|
|
|
| - void gotOrientation(double alpha, double beta, double gamma) {
|
| + protected void gotRotationRate(double alpha, double beta, double gamma) {
|
| synchronized (mNativePtrLock) {
|
| if (mNativePtr != 0) {
|
| - nativeGotOrientation(mNativePtr, alpha, beta, gamma);
|
| + nativeGotRotationRate(mNativePtr, alpha, beta, gamma);
|
| }
|
| }
|
| }
|
| @@ -251,19 +394,79 @@ class DeviceOrientation implements SensorEventListener {
|
| }
|
|
|
| @CalledByNative
|
| - private static DeviceOrientation getInstance() {
|
| + static DeviceMotionAndOrientation getInstance() {
|
| synchronized (sSingletonLock) {
|
| if (sSingleton == null) {
|
| - sSingleton = new DeviceOrientation();
|
| + sSingleton = new DeviceMotionAndOrientation();
|
| }
|
| return sSingleton;
|
| }
|
| }
|
|
|
| /**
|
| - * See chrome/browser/device_orientation/data_fetcher_impl_android.cc
|
| + * Native JNI calls,
|
| + * see content/browser/device_orientation/data_fetcher_impl_android.cc
|
| + */
|
| +
|
| + /**
|
| + * Orientation of the device with respect to its reference frame.
|
| */
|
| private native void nativeGotOrientation(
|
| int nativeDataFetcherImplAndroid,
|
| double alpha, double beta, double gamma);
|
| -}
|
| +
|
| + /**
|
| + * Linear acceleration without gravity of the device with respect to its body frame.
|
| + */
|
| + private native void nativeGotAcceleration(
|
| + int nativeDataFetcherImplAndroid,
|
| + double x, double y, double z);
|
| +
|
| + /**
|
| + * Acceleration including gravity of the device with respect to its body frame.
|
| + */
|
| + private native void nativeGotAccelerationIncludingGravity(
|
| + int nativeDataFetcherImplAndroid,
|
| + double x, double y, double z);
|
| +
|
| + /**
|
| + * Rotation rate of the device with respect to its body frame.
|
| + */
|
| + private native void nativeGotRotationRate(
|
| + int nativeDataFetcherImplAndroid,
|
| + double alpha, double beta, double gamma);
|
| +
|
| + /**
|
| + * Need the an interface for SensorManager for testing.
|
| + */
|
| + interface SensorManagerProxy {
|
| + public boolean registerListener(SensorEventListener listener, int sensorType, int rate,
|
| + Handler handler);
|
| + public void unregisterListener(SensorEventListener listener, int sensorType);
|
| + }
|
| +
|
| + static class SensorManagerProxyImpl implements SensorManagerProxy {
|
| + private final SensorManager mSensorManager;
|
| +
|
| + SensorManagerProxyImpl(SensorManager sensorManager) {
|
| + mSensorManager = sensorManager;
|
| + }
|
| +
|
| + public boolean registerListener(SensorEventListener listener, int sensorType, int rate,
|
| + Handler handler) {
|
| + List<Sensor> sensors = mSensorManager.getSensorList(sensorType);
|
| + if (sensors.isEmpty()) {
|
| + return false;
|
| + }
|
| + return mSensorManager.registerListener(listener, sensors.get(0), rate, handler);
|
| + }
|
| +
|
| + public void unregisterListener(SensorEventListener listener, int sensorType) {
|
| + List<Sensor> sensors = mSensorManager.getSensorList(sensorType);
|
| + if (!sensors.isEmpty()) {
|
| + mSensorManager.unregisterListener(listener, sensors.get(0));
|
| + }
|
| + }
|
| + }
|
| +
|
| +}
|
|
|