This source file includes following definitions.
- JNINamespace
- start
- getNumberActiveDeviceMotionSensors
- stop
- onAccuracyChanged
- onSensorChanged
- sensorChanged
- computeDeviceOrientationFromRotationMatrix
- getOrientationFromRotationVector
- getSensorManagerProxy
- setSensorManagerProxy
- setEventTypeActive
- registerSensors
- unregisterSensors
- registerForSensorType
- gotOrientation
- gotAcceleration
- gotAccelerationIncludingGravity
- gotRotationRate
- getHandler
- getInstance
- nativeGotOrientation
- nativeGotAcceleration
- nativeGotAccelerationIncludingGravity
- nativeGotRotationRate
- registerListener
- unregisterListener
- registerListener
- unregisterListener
package org.chromium.content.browser;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Handler;
import android.os.HandlerThread;
import android.util.Log;
import com.google.common.annotations.VisibleForTesting;
import org.chromium.base.CalledByNative;
import org.chromium.base.CollectionUtil;
import org.chromium.base.JNINamespace;
import org.chromium.base.ThreadUtils;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
@JNINamespace("content")
class DeviceMotionAndOrientation implements SensorEventListener {
private static final String TAG = "DeviceMotionAndOrientation";
private Thread mThread;
private Handler mHandler;
private final Context mAppContext;
private final Object mHandlerLock = new Object();
private long mNativePtr;
private final Object mNativePtrLock = new Object();
private float[] mTruncatedRotationVector;
private SensorManagerProxy mSensorManagerProxy;
private static DeviceMotionAndOrientation sSingleton;
private static Object sSingletonLock = new Object();
static final int DEVICE_ORIENTATION = 0;
static final int DEVICE_MOTION = 1;
static final Set<Integer> DEVICE_ORIENTATION_SENSORS = CollectionUtil.newHashSet(
Sensor.TYPE_ROTATION_VECTOR);
static final Set<Integer> DEVICE_MOTION_SENSORS = CollectionUtil.newHashSet(
Sensor.TYPE_ACCELEROMETER,
Sensor.TYPE_LINEAR_ACCELERATION,
Sensor.TYPE_GYROSCOPE);
@VisibleForTesting
final Set<Integer> mActiveSensors = new HashSet<Integer>();
boolean mDeviceMotionIsActive = false;
boolean mDeviceOrientationIsActive = false;
protected DeviceMotionAndOrientation(Context context) {
mAppContext = context.getApplicationContext();
}
@CalledByNative
public boolean start(long nativePtr, int eventType, int rateInMilliseconds) {
boolean success = false;
synchronized (mNativePtrLock) {
switch (eventType) {
case DEVICE_ORIENTATION:
success = registerSensors(DEVICE_ORIENTATION_SENSORS, rateInMilliseconds,
true);
break;
case DEVICE_MOTION:
success = registerSensors(DEVICE_MOTION_SENSORS, rateInMilliseconds, false);
break;
default:
Log.e(TAG, "Unknown event type: " + eventType);
return false;
}
if (success) {
mNativePtr = nativePtr;
setEventTypeActive(eventType, true);
}
return success;
}
}
@CalledByNative
public int getNumberActiveDeviceMotionSensors() {
Set<Integer> deviceMotionSensors = new HashSet<Integer>(DEVICE_MOTION_SENSORS);
deviceMotionSensors.removeAll(mActiveSensors);
return DEVICE_MOTION_SENSORS.size() - deviceMotionSensors.size();
}
@CalledByNative
public void stop(int eventType) {
Set<Integer> sensorsToRemainActive = new HashSet<Integer>();
synchronized (mNativePtrLock) {
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 = new HashSet<Integer>(mActiveSensors);
sensorsToDeactivate.removeAll(sensorsToRemainActive);
unregisterSensors(sensorsToDeactivate);
setEventTypeActive(eventType, false);
if (mActiveSensors.isEmpty()) {
mNativePtr = 0;
}
}
}
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
@Override
public void onSensorChanged(SensorEvent event) {
sensorChanged(event.sensor.getType(), event.values);
}
@VisibleForTesting
void sensorChanged(int type, float[] values) {
switch (type) {
case Sensor.TYPE_ACCELEROMETER:
if (mDeviceMotionIsActive) {
gotAccelerationIncludingGravity(values[0], values[1], values[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]);
}
break;
case Sensor.TYPE_ROTATION_VECTOR:
if (mDeviceOrientationIsActive) {
if (values.length > 4) {
if (mTruncatedRotationVector == null) {
mTruncatedRotationVector = new float[4];
}
System.arraycopy(values, 0, mTruncatedRotationVector, 0, 4);
getOrientationFromRotationVector(mTruncatedRotationVector);
} else {
getOrientationFromRotationVector(values);
}
}
break;
default:
return;
}
}
@VisibleForTesting
public static double[] computeDeviceOrientationFromRotationMatrix(float[] R, double[] values) {
if (R.length != 9)
return values;
if (R[8] > 0) {
values[0] = Math.atan2(-R[1], R[4]);
values[1] = Math.asin(R[7]);
values[2] = Math.atan2(-R[6], R[8]);
} else if (R[8] < 0) {
values[0] = Math.atan2(R[1], -R[4]);
values[1] = -Math.asin(R[7]);
values[1] += (values[1] >= 0) ? -Math.PI : Math.PI;
values[2] = Math.atan2(R[6], -R[8]);
} else {
if (R[6] > 0) {
values[0] = Math.atan2(-R[1], R[4]);
values[1] = Math.asin(R[7]);
values[2] = -Math.PI / 2;
} else if (R[6] < 0) {
values[0] = Math.atan2(R[1], -R[4]);
values[1] = -Math.asin(R[7]);
values[1] += (values[1] >= 0) ? -Math.PI : Math.PI;
values[2] = -Math.PI / 2;
} else {
values[0] = Math.atan2(R[3], R[0]);
values[1] = (R[7] > 0) ? Math.PI / 2 : -Math.PI / 2;
values[2] = 0;
}
}
if (values[0] < 0)
values[0] += 2 * Math.PI;
return values;
}
private void getOrientationFromRotationVector(float[] rotationVector) {
float[] deviceRotationMatrix = new float[9];
SensorManager.getRotationMatrixFromVector(deviceRotationMatrix, rotationVector);
double[] rotationAngles = new double[3];
computeDeviceOrientationFromRotationMatrix(deviceRotationMatrix, rotationAngles);
gotOrientation(Math.toDegrees(rotationAngles[0]),
Math.toDegrees(rotationAngles[1]),
Math.toDegrees(rotationAngles[2]));
}
private SensorManagerProxy getSensorManagerProxy() {
if (mSensorManagerProxy != null) {
return mSensorManagerProxy;
}
SensorManager sensorManager = ThreadUtils.runOnUiThreadBlockingNoException(
new Callable<SensorManager>() {
@Override
public SensorManager call() {
return (SensorManager) mAppContext.getSystemService(Context.SENSOR_SERVICE);
}
});
if (sensorManager != null) {
mSensorManagerProxy = new SensorManagerProxyImpl(sensorManager);
}
return mSensorManagerProxy;
}
@VisibleForTesting
void setSensorManagerProxy(SensorManagerProxy sensorManagerProxy) {
mSensorManagerProxy = sensorManagerProxy;
}
private void setEventTypeActive(int eventType, boolean value) {
switch (eventType) {
case DEVICE_ORIENTATION:
mDeviceOrientationIsActive = value;
return;
case DEVICE_MOTION:
mDeviceMotionIsActive = value;
return;
}
}
private boolean registerSensors(Set<Integer> sensorTypes, int rateInMilliseconds,
boolean failOnMissingSensor) {
Set<Integer> sensorsToActivate = new HashSet<Integer>(sensorTypes);
sensorsToActivate.removeAll(mActiveSensors);
boolean success = false;
for (Integer sensorType : sensorsToActivate) {
boolean result = registerForSensorType(sensorType, rateInMilliseconds);
if (!result && failOnMissingSensor) {
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);
}
}
}
private boolean registerForSensorType(int type, int rateInMilliseconds) {
SensorManagerProxy sensorManager = getSensorManagerProxy();
if (sensorManager == null) {
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);
}
}
}
protected void gotAcceleration(double x, double y, double z) {
synchronized (mNativePtrLock) {
if (mNativePtr != 0) {
nativeGotAcceleration(mNativePtr, x, y, z);
}
}
}
protected void gotAccelerationIncludingGravity(double x, double y, double z) {
synchronized (mNativePtrLock) {
if (mNativePtr != 0) {
nativeGotAccelerationIncludingGravity(mNativePtr, x, y, z);
}
}
}
protected void gotRotationRate(double alpha, double beta, double gamma) {
synchronized (mNativePtrLock) {
if (mNativePtr != 0) {
nativeGotRotationRate(mNativePtr, alpha, beta, gamma);
}
}
}
private Handler getHandler() {
synchronized (mHandlerLock) {
if (mHandler == null) {
HandlerThread thread = new HandlerThread("DeviceMotionAndOrientation");
thread.start();
mHandler = new Handler(thread.getLooper());
}
return mHandler;
}
}
@CalledByNative
static DeviceMotionAndOrientation getInstance(Context appContext) {
synchronized (sSingletonLock) {
if (sSingleton == null) {
sSingleton = new DeviceMotionAndOrientation(appContext);
}
return sSingleton;
}
}
private native void nativeGotOrientation(
long nativeSensorManagerAndroid,
double alpha, double beta, double gamma);
private native void nativeGotAcceleration(
long nativeSensorManagerAndroid,
double x, double y, double z);
private native void nativeGotAccelerationIncludingGravity(
long nativeSensorManagerAndroid,
double x, double y, double z);
private native void nativeGotRotationRate(
long nativeSensorManagerAndroid,
double alpha, double beta, double gamma);
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;
}
@Override
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);
}
@Override
public void unregisterListener(SensorEventListener listener, int sensorType) {
List<Sensor> sensors = mSensorManager.getSensorList(sensorType);
if (!sensors.isEmpty()) {
mSensorManager.unregisterListener(listener, sensors.get(0));
}
}
}
}