diff --git a/hal/HalSensor.h b/hal/HalSensor.h index bcee5e1ed210f..71365a9b68c28 100644 --- a/hal/HalSensor.h +++ b/hal/HalSensor.h @@ -15,15 +15,18 @@ namespace hal { /** * Enumeration of sensor types. They are used to specify type while * register or unregister an observer for a sensor of given type. + * If you add or change any here, do the same in GeckoHalDefines.java. */ enum SensorType { SENSOR_UNKNOWN = -1, - SENSOR_ORIENTATION, - SENSOR_ACCELERATION, - SENSOR_PROXIMITY, - SENSOR_LINEAR_ACCELERATION, - SENSOR_GYROSCOPE, - SENSOR_LIGHT, + SENSOR_ORIENTATION = 0, + SENSOR_ACCELERATION = 1, + SENSOR_PROXIMITY = 2, + SENSOR_LINEAR_ACCELERATION = 3, + SENSOR_GYROSCOPE = 4, + SENSOR_LIGHT = 5, + SENSOR_ROTATION_VECTOR = 6, + SENSOR_GAME_ROTATION_VECTOR = 7, NUM_SENSOR_TYPE }; diff --git a/hal/gonk/GonkSensor.cpp b/hal/gonk/GonkSensor.cpp index 7e6b2bade2533..7bffd63fe6348 100644 --- a/hal/gonk/GonkSensor.cpp +++ b/hal/gonk/GonkSensor.cpp @@ -38,6 +38,10 @@ namespace mozilla { // different orientation angles #define ACCELEROMETER_POLL_RATE 66667000 /*66.667ms*/ +// This is present in Android from API level 18 onwards, which is 4.3. We might +// be building on something before 4.3, so use a local define for its value +#define MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR 15 + double radToDeg(double a) { return a * (180.0 / M_PI); } @@ -58,6 +62,10 @@ HardwareSensorToHalSensor(int type) return SENSOR_GYROSCOPE; case SENSOR_TYPE_LINEAR_ACCELERATION: return SENSOR_LINEAR_ACCELERATION; + case SENSOR_TYPE_ROTATION_VECTOR: + return SENSOR_ROTATION_VECTOR; + case MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR: + return SENSOR_GAME_ROTATION_VECTOR; default: return SENSOR_UNKNOWN; } @@ -84,6 +92,10 @@ HalSensorToHardwareSensor(SensorType type) return SENSOR_TYPE_GYROSCOPE; case SENSOR_LINEAR_ACCELERATION: return SENSOR_TYPE_LINEAR_ACCELERATION; + case SENSOR_ROTATION_VECTOR: + return SENSOR_TYPE_ROTATION_VECTOR; + case SENSOR_GAME_ROTATION_VECTOR: + return MOZ_SENSOR_TYPE_GAME_ROTATION_VECTOR; default: return -1; } @@ -131,6 +143,28 @@ class SensorRunnable : public nsRunnable } } else if (mSensorData.sensor() == SENSOR_LIGHT) { mSensorValues.AppendElement(data.data[0]); + } else if (mSensorData.sensor() == SENSOR_ROTATION_VECTOR) { + mSensorValues.AppendElement(data.data[0]); + mSensorValues.AppendElement(data.data[1]); + mSensorValues.AppendElement(data.data[2]); + if (data.data[3] == 0.0) { + // data.data[3] was optional in Android <= API level 18. It can be computed from 012, + // but it's better to take the actual value if one is provided. The computation is + // v = 1 - d[0]*d[0] - d[1]*d[1] - d[2]*d[2] + // d[3] = v > 0 ? sqrt(v) : 0; + // I'm assuming that it will be 0 if it's not passed in. (The values form a unit + // quaternion, so the angle can be computed from the direction vector.) + float sx = data.data[0], sy = data.data[1], sz = data.data[2]; + float v = 1.0f - sx*sx - sy*sy - sz*sz; + mSensorValues.AppendElement(v > 0.0f ? sqrt(v) : 0.0f); + } else { + mSensorValues.AppendElement(data.data[3]); + } + } else if (mSensorData.sensor() == SENSOR_GAME_ROTATION_VECTOR) { + mSensorValues.AppendElement(data.data[0]); + mSensorValues.AppendElement(data.data[1]); + mSensorValues.AppendElement(data.data[2]); + mSensorValues.AppendElement(data.data[3]); } else { mSensorValues.AppendElement(data.data[0]); mSensorValues.AppendElement(data.data[1]); @@ -149,7 +183,7 @@ class SensorRunnable : public nsRunnable private: SensorData mSensorData; - InfallibleTArray mSensorValues; + nsAutoTArray mSensorValues; }; namespace hal_impl { diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 71151f5501da6..bf451b7a198d9 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -226,6 +226,8 @@ public static CrashHandler ensureCrashHandling() { private static Sensor gOrientationSensor; private static Sensor gProximitySensor; private static Sensor gLightSensor; + private static Sensor gRotationVectorSensor; + private static Sensor gGameRotationVectorSensor; private static final String GECKOREQUEST_RESPONSE_KEY = "response"; private static final String GECKOREQUEST_ERROR_KEY = "error"; @@ -648,46 +650,61 @@ public static void enableSensor(int aSensortype) { switch(aSensortype) { case GeckoHalDefines.SENSOR_ORIENTATION: - if(gOrientationSensor == null) + if (gOrientationSensor == null) gOrientationSensor = sm.getDefaultSensor(Sensor.TYPE_ORIENTATION); - if (gOrientationSensor != null) + if (gOrientationSensor != null) sm.registerListener(gi.getSensorEventListener(), gOrientationSensor, SensorManager.SENSOR_DELAY_GAME); break; case GeckoHalDefines.SENSOR_ACCELERATION: - if(gAccelerometerSensor == null) + if (gAccelerometerSensor == null) gAccelerometerSensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); if (gAccelerometerSensor != null) sm.registerListener(gi.getSensorEventListener(), gAccelerometerSensor, SensorManager.SENSOR_DELAY_GAME); break; case GeckoHalDefines.SENSOR_PROXIMITY: - if(gProximitySensor == null ) + if (gProximitySensor == null) gProximitySensor = sm.getDefaultSensor(Sensor.TYPE_PROXIMITY); if (gProximitySensor != null) sm.registerListener(gi.getSensorEventListener(), gProximitySensor, SensorManager.SENSOR_DELAY_NORMAL); break; case GeckoHalDefines.SENSOR_LIGHT: - if(gLightSensor == null) + if (gLightSensor == null) gLightSensor = sm.getDefaultSensor(Sensor.TYPE_LIGHT); if (gLightSensor != null) sm.registerListener(gi.getSensorEventListener(), gLightSensor, SensorManager.SENSOR_DELAY_NORMAL); break; case GeckoHalDefines.SENSOR_LINEAR_ACCELERATION: - if(gLinearAccelerometerSensor == null) - gLinearAccelerometerSensor = sm.getDefaultSensor(10 /* API Level 9 - TYPE_LINEAR_ACCELERATION */); + if (gLinearAccelerometerSensor == null) + gLinearAccelerometerSensor = sm.getDefaultSensor(Sensor.TYPE_LINEAR_ACCELERATION); if (gLinearAccelerometerSensor != null) sm.registerListener(gi.getSensorEventListener(), gLinearAccelerometerSensor, SensorManager.SENSOR_DELAY_GAME); break; case GeckoHalDefines.SENSOR_GYROSCOPE: - if(gGyroscopeSensor == null) + if (gGyroscopeSensor == null) gGyroscopeSensor = sm.getDefaultSensor(Sensor.TYPE_GYROSCOPE); if (gGyroscopeSensor != null) sm.registerListener(gi.getSensorEventListener(), gGyroscopeSensor, SensorManager.SENSOR_DELAY_GAME); break; + + case GeckoHalDefines.SENSOR_ROTATION_VECTOR: + if (gRotationVectorSensor == null) + gRotationVectorSensor = sm.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR); + if (gRotationVectorSensor != null) + sm.registerListener(gi.getSensorEventListener(), gRotationVectorSensor, SensorManager.SENSOR_DELAY_GAME); + break; + + case GeckoHalDefines.SENSOR_GAME_ROTATION_VECTOR: + if (gGameRotationVectorSensor == null) + gGameRotationVectorSensor = sm.getDefaultSensor(15 /* Sensor.TYPE_GAME_ROTATION_VECTOR */); // API >= 18 + if (gGameRotationVectorSensor != null) + sm.registerListener(gi.getSensorEventListener(), gGameRotationVectorSensor, SensorManager.SENSOR_DELAY_GAME); + break; + default: Log.w(LOGTAG, "Error! Can't enable unknown SENSOR type " + aSensortype); } @@ -728,6 +745,16 @@ public static void disableSensor(int aSensortype) { sm.unregisterListener(gi.getSensorEventListener(), gLinearAccelerometerSensor); break; + case GeckoHalDefines.SENSOR_ROTATION_VECTOR: + if (gRotationVectorSensor != null) + sm.unregisterListener(gi.getSensorEventListener(), gRotationVectorSensor); + break; + + case GeckoHalDefines.SENSOR_GAME_ROTATION_VECTOR: + if (gGameRotationVectorSensor != null) + sm.unregisterListener(gi.getSensorEventListener(), gGameRotationVectorSensor); + break; + case GeckoHalDefines.SENSOR_GYROSCOPE: if (gGyroscopeSensor != null) sm.unregisterListener(gi.getSensorEventListener(), gGyroscopeSensor); diff --git a/mobile/android/base/GeckoEvent.java b/mobile/android/base/GeckoEvent.java index 0fa41d6390dd4..300f21ba919a6 100644 --- a/mobile/android/base/GeckoEvent.java +++ b/mobile/android/base/GeckoEvent.java @@ -179,6 +179,7 @@ private ImeAction(int value) { private double mX; private double mY; private double mZ; + private double mW; private int mMetaState; private int mFlags; @@ -509,7 +510,7 @@ public static GeckoEvent createSensorEvent(SensorEvent s) { event.mZ = s.values[2]; break; - case 10 /* Requires API Level 9, so just use the raw value - Sensor.TYPE_LINEAR_ACCELEROMETER*/ : + case Sensor.TYPE_LINEAR_ACCELERATION: event = GeckoEvent.get(NativeGeckoEvent.SENSOR_EVENT); event.mFlags = GeckoHalDefines.SENSOR_LINEAR_ACCELERATION; event.mMetaState = HalSensorAccuracyFor(s.accuracy); @@ -551,6 +552,35 @@ public static GeckoEvent createSensorEvent(SensorEvent s) { event.mMetaState = HalSensorAccuracyFor(s.accuracy); event.mX = s.values[0]; break; + + case Sensor.TYPE_ROTATION_VECTOR: + event = GeckoEvent.get(NativeGeckoEvent.SENSOR_EVENT); + event.mFlags = GeckoHalDefines.SENSOR_ROTATION_VECTOR; + event.mMetaState = HalSensorAccuracyFor(s.accuracy); + event.mX = s.values[0]; + event.mY = s.values[1]; + event.mZ = s.values[2]; + if (s.values.length >= 4) { + event.mW = s.values[3]; + } else { + // s.values[3] was optional in API <= 18, so we need to compute it + // The values form a unit quaternion, so we can compute the angle of + // rotation purely based on the given 3 values. + event.mW = 1 - s.values[0]*s.values[0] - s.values[1]*s.values[1] - s.values[2]*s.values[2]; + event.mW = (event.mW > 0.0) ? Math.sqrt(event.mW) : 0.0; + } + break; + + // case Sensor.TYPE_GAME_ROTATION_VECTOR: // API >= 18 + case 15: + event = GeckoEvent.get(NativeGeckoEvent.SENSOR_EVENT); + event.mFlags = GeckoHalDefines.SENSOR_GAME_ROTATION_VECTOR; + event.mMetaState = HalSensorAccuracyFor(s.accuracy); + event.mX = s.values[0]; + event.mY = s.values[1]; + event.mZ = s.values[2]; + event.mW = s.values[3]; + break; } return event; } diff --git a/mobile/android/base/GeckoHalDefines.java b/mobile/android/base/GeckoHalDefines.java index 3ebe470288503..3d9b974279814 100644 --- a/mobile/android/base/GeckoHalDefines.java +++ b/mobile/android/base/GeckoHalDefines.java @@ -8,14 +8,16 @@ public class GeckoHalDefines { /* - * Keep these values consistent with |SensorType| in Hal.h - */ + * Keep these values consistent with |SensorType| in HalSensor.h + */ public static final int SENSOR_ORIENTATION = 0; public static final int SENSOR_ACCELERATION = 1; public static final int SENSOR_PROXIMITY = 2; public static final int SENSOR_LINEAR_ACCELERATION = 3; public static final int SENSOR_GYROSCOPE = 4; public static final int SENSOR_LIGHT = 5; + public static final int SENSOR_ROTATION_VECTOR = 6; + public static final int SENSOR_GAME_ROTATION_VECTOR = 7; public static final int SENSOR_ACCURACY_UNKNOWN = -1; public static final int SENSOR_ACCURACY_UNRELIABLE = 0; diff --git a/widget/android/AndroidJavaWrappers.cpp b/widget/android/AndroidJavaWrappers.cpp index 27e9ffed4d750..829aa3916c9d7 100644 --- a/widget/android/AndroidJavaWrappers.cpp +++ b/widget/android/AndroidJavaWrappers.cpp @@ -29,6 +29,7 @@ jfieldID AndroidGeckoEvent::jOrientations = 0; jfieldID AndroidGeckoEvent::jXField = 0; jfieldID AndroidGeckoEvent::jYField = 0; jfieldID AndroidGeckoEvent::jZField = 0; +jfieldID AndroidGeckoEvent::jWField = 0; jfieldID AndroidGeckoEvent::jDistanceField = 0; jfieldID AndroidGeckoEvent::jRectField = 0; jfieldID AndroidGeckoEvent::jNativeWindowField = 0; @@ -138,6 +139,7 @@ AndroidGeckoEvent::InitGeckoEventClass(JNIEnv *jEnv) jXField = geckoEvent.getField("mX", "D"); jYField = geckoEvent.getField("mY", "D"); jZField = geckoEvent.getField("mZ", "D"); + jWField = geckoEvent.getField("mW", "D"); jRectField = geckoEvent.getField("mRect", "Landroid/graphics/Rect;"); jCharactersField = geckoEvent.getField("mCharacters", "Ljava/lang/String;"); @@ -466,6 +468,7 @@ AndroidGeckoEvent::Init(JNIEnv *jenv, jobject jobj) mX = jenv->GetDoubleField(jobj, jXField); mY = jenv->GetDoubleField(jobj, jYField); mZ = jenv->GetDoubleField(jobj, jZField); + mW = jenv->GetDoubleField(jobj, jWField); mFlags = jenv->GetIntField(jobj, jFlagsField); mMetaState = jenv->GetIntField(jobj, jMetaStateField); break; diff --git a/widget/android/AndroidJavaWrappers.h b/widget/android/AndroidJavaWrappers.h index 230919fa87001..7ef13ec8db51a 100644 --- a/widget/android/AndroidJavaWrappers.h +++ b/widget/android/AndroidJavaWrappers.h @@ -518,6 +518,7 @@ class AndroidGeckoEvent : public WrappedJavaObject double X() { return mX; } double Y() { return mY; } double Z() { return mZ; } + double W() { return mW; } const nsIntRect& Rect() { return mRect; } nsAString& Characters() { return mCharacters; } nsAString& CharactersExtra() { return mCharactersExtra; } @@ -591,7 +592,7 @@ class AndroidGeckoEvent : public WrappedJavaObject int mRangeType, mRangeStyles, mRangeLineStyle; bool mRangeBoldLine; int mRangeForeColor, mRangeBackColor, mRangeLineColor; - double mX, mY, mZ; + double mX, mY, mZ, mW; int mPointerIndex; nsString mCharacters, mCharactersExtra, mData; nsRefPtr mGeoPosition; @@ -648,6 +649,7 @@ class AndroidGeckoEvent : public WrappedJavaObject static jfieldID jXField; static jfieldID jYField; static jfieldID jZField; + static jfieldID jWField; static jfieldID jDistanceField; static jfieldID jRectField; static jfieldID jNativeWindowField; diff --git a/widget/android/nsAppShell.cpp b/widget/android/nsAppShell.cpp index ee5a4429333b9..c4d59ce46481e 100644 --- a/widget/android/nsAppShell.cpp +++ b/widget/android/nsAppShell.cpp @@ -274,7 +274,7 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) break; case AndroidGeckoEvent::SENSOR_EVENT: { - InfallibleTArray values; + nsAutoTArray values; mozilla::hal::SensorType type = (mozilla::hal::SensorType) curEvent->Flags(); switch (type) { @@ -299,6 +299,14 @@ nsAppShell::ProcessNextNativeEvent(bool mayWait) values.AppendElement(curEvent->X()); break; + case hal::SENSOR_ROTATION_VECTOR: + case hal::SENSOR_GAME_ROTATION_VECTOR: + values.AppendElement(curEvent->X()); + values.AppendElement(curEvent->Y()); + values.AppendElement(curEvent->Z()); + values.AppendElement(curEvent->W()); + break; + default: __android_log_print(ANDROID_LOG_ERROR, "Gecko", "### SENSOR_EVENT fired, but type wasn't known %d",