mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 788975 - Orientation Observer uses accelerometer sensor. r=mwu
This commit is contained in:
parent
fa0dddce46
commit
4d6d5b66fd
@ -39,6 +39,9 @@ namespace mozilla {
|
||||
|
||||
// The value from SensorDevice.h (Android)
|
||||
#define DEFAULT_DEVICE_POLL_RATE 200000000 /*200ms*/
|
||||
// ProcessOrientation.cpp needs smaller poll rate to detect delay between
|
||||
// different orientation angles
|
||||
#define ACCELEROMETER_POLL_RATE 66667000 /*66.667ms*/
|
||||
|
||||
double radToDeg(double a) {
|
||||
return a * (180.0 / M_PI);
|
||||
@ -214,8 +217,13 @@ SwitchSensor(bool aActivate, sensor_t aSensor, pthread_t aThreadId)
|
||||
sSensorDevice->activate(sSensorDevice, aSensor.handle, aActivate);
|
||||
|
||||
if (aActivate) {
|
||||
sSensorDevice->setDelay(sSensorDevice, aSensor.handle,
|
||||
if (aSensor.type == SENSOR_TYPE_ACCELEROMETER) {
|
||||
sSensorDevice->setDelay(sSensorDevice, aSensor.handle,
|
||||
ACCELEROMETER_POLL_RATE);
|
||||
} else {
|
||||
sSensorDevice->setDelay(sSensorDevice, aSensor.handle,
|
||||
DEFAULT_DEVICE_POLL_RATE);
|
||||
}
|
||||
}
|
||||
|
||||
if (aActivate) {
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include "nsIScreenManager.h"
|
||||
#include "OrientationObserver.h"
|
||||
#include "mozilla/HalSensor.h"
|
||||
#include "ProcessOrientation.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace dom;
|
||||
@ -166,8 +167,8 @@ OrientationObserver::GetInstance()
|
||||
|
||||
OrientationObserver::OrientationObserver()
|
||||
: mAutoOrientationEnabled(false)
|
||||
, mLastUpdate(0)
|
||||
, mAllowedOrientations(sDefaultOrientations)
|
||||
, mOrientation(new mozilla::ProcessOrientation())
|
||||
{
|
||||
DetectDefaultOrientation();
|
||||
|
||||
@ -198,27 +199,7 @@ OrientationObserver::Notify(const hal::SensorData& aSensorData)
|
||||
{
|
||||
// Sensor will call us on the main thread.
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aSensorData.sensor() == hal::SensorType::SENSOR_ORIENTATION);
|
||||
|
||||
const InfallibleTArray<float>& values = aSensorData.values();
|
||||
// Azimuth (values[0]): the device's horizontal orientation
|
||||
// (0 degree is north). It's unused for screen rotation.
|
||||
float pitch = values[1];
|
||||
float roll = values[2];
|
||||
|
||||
uint32_t rotation;
|
||||
if (roll > 45) {
|
||||
rotation = nsIScreen::ROTATION_90_DEG;
|
||||
} else if (roll < -45) {
|
||||
rotation = nsIScreen::ROTATION_270_DEG;
|
||||
} else if (pitch < -45) {
|
||||
rotation = nsIScreen::ROTATION_0_DEG;
|
||||
} else if (pitch > 45) {
|
||||
rotation = nsIScreen::ROTATION_180_DEG;
|
||||
} else {
|
||||
// Don't rotate if neither pitch nor roll exceeds the 45 degree threshold.
|
||||
return;
|
||||
}
|
||||
MOZ_ASSERT(aSensorData.sensor() == hal::SensorType::SENSOR_ACCELERATION);
|
||||
|
||||
nsCOMPtr<nsIScreen> screen = GetPrimaryScreen();
|
||||
if (!screen) {
|
||||
@ -226,8 +207,12 @@ OrientationObserver::Notify(const hal::SensorData& aSensorData)
|
||||
}
|
||||
|
||||
uint32_t currRotation;
|
||||
if (NS_FAILED(screen->GetRotation(&currRotation)) ||
|
||||
rotation == currRotation) {
|
||||
if(NS_FAILED(screen->GetRotation(&currRotation))) {
|
||||
return;
|
||||
}
|
||||
|
||||
int rotation = mOrientation->OnSensorChanged(aSensorData, static_cast<int>(currRotation));
|
||||
if (rotation < 0 || rotation == currRotation) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -241,14 +226,7 @@ OrientationObserver::Notify(const hal::SensorData& aSensorData)
|
||||
return;
|
||||
}
|
||||
|
||||
PRTime now = PR_Now();
|
||||
MOZ_ASSERT(now > mLastUpdate);
|
||||
if (now - mLastUpdate < sMinUpdateInterval) {
|
||||
return;
|
||||
}
|
||||
mLastUpdate = now;
|
||||
|
||||
if (NS_FAILED(screen->SetRotation(rotation))) {
|
||||
if (NS_FAILED(screen->SetRotation(static_cast<uint32_t>(rotation)))) {
|
||||
// Don't notify dom on rotation failure.
|
||||
return;
|
||||
}
|
||||
@ -262,7 +240,8 @@ OrientationObserver::EnableAutoOrientation()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread() && !mAutoOrientationEnabled);
|
||||
|
||||
hal::RegisterSensorObserver(hal::SENSOR_ORIENTATION, this);
|
||||
mOrientation->Reset();
|
||||
hal::RegisterSensorObserver(hal::SENSOR_ACCELERATION, this);
|
||||
mAutoOrientationEnabled = true;
|
||||
}
|
||||
|
||||
@ -274,7 +253,7 @@ OrientationObserver::DisableAutoOrientation()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread() && mAutoOrientationEnabled);
|
||||
|
||||
hal::UnregisterSensorObserver(hal::SENSOR_ORIENTATION, this);
|
||||
hal::UnregisterSensorObserver(hal::SENSOR_ACCELERATION, this);
|
||||
mAutoOrientationEnabled = false;
|
||||
}
|
||||
|
||||
|
@ -22,6 +22,7 @@
|
||||
#include "mozilla/dom/ScreenOrientation.h"
|
||||
|
||||
namespace mozilla {
|
||||
class ProcessOrientation;
|
||||
namespace hal {
|
||||
class SensorData;
|
||||
typedef mozilla::Observer<SensorData> ISensorObserver;
|
||||
@ -56,11 +57,9 @@ public:
|
||||
|
||||
private:
|
||||
bool mAutoOrientationEnabled;
|
||||
PRTime mLastUpdate;
|
||||
uint32_t mAllowedOrientations;
|
||||
mozilla::ScopedDeletePtr<mozilla::ProcessOrientation> mOrientation;
|
||||
|
||||
// 200 ms, the latency which is barely perceptible by human.
|
||||
static const PRTime sMinUpdateInterval = 200 * PR_USEC_PER_MSEC;
|
||||
static const uint32_t sDefaultOrientations =
|
||||
mozilla::dom::eScreenOrientation_PortraitPrimary |
|
||||
mozilla::dom::eScreenOrientation_PortraitSecondary |
|
||||
|
511
widget/gonk/ProcessOrientation.cpp
Normal file
511
widget/gonk/ProcessOrientation.cpp
Normal file
@ -0,0 +1,511 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Linux Foundation. All rights reserved
|
||||
*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "base/basictypes.h"
|
||||
#include "mozilla/Hal.h"
|
||||
#include "nsIScreen.h"
|
||||
#include "nsIScreenManager.h"
|
||||
#include "OrientationObserver.h"
|
||||
#include "ProcessOrientation.h"
|
||||
#include "mozilla/HalSensor.h"
|
||||
#include "math.h"
|
||||
#include "limits.h"
|
||||
#include "android/log.h"
|
||||
|
||||
#if 0
|
||||
#define LOGD(args...) __android_log_print(ANDROID_LOG_DEBUG, "ProcessOrientation" , ## args)
|
||||
#else
|
||||
#define LOGD(args...)
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// We work with all angles in degrees in this class.
|
||||
#define RADIANS_TO_DEGREES (180/M_PI)
|
||||
|
||||
// Number of nanoseconds per millisecond.
|
||||
#define NANOS_PER_MS 1000000
|
||||
|
||||
// Indices into SensorEvent.values for the accelerometer sensor.
|
||||
#define ACCELEROMETER_DATA_X 0
|
||||
#define ACCELEROMETER_DATA_Y 1
|
||||
#define ACCELEROMETER_DATA_Z 2
|
||||
|
||||
// The minimum amount of time that a predicted rotation must be stable before
|
||||
// it is accepted as a valid rotation proposal. This value can be quite small
|
||||
// because the low-pass filter already suppresses most of the noise so we're
|
||||
// really just looking for quick confirmation that the last few samples are in
|
||||
// agreement as to the desired orientation.
|
||||
#define PROPOSAL_SETTLE_TIME_NANOS (40*NANOS_PER_MS)
|
||||
|
||||
// The minimum amount of time that must have elapsed since the device last
|
||||
// exited the flat state (time since it was picked up) before the proposed
|
||||
// rotation can change.
|
||||
#define PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS (500*NANOS_PER_MS)
|
||||
|
||||
// The minimum amount of time that must have elapsed since the device stopped
|
||||
// swinging (time since device appeared to be in the process of being put down
|
||||
// or put away into a pocket) before the proposed rotation can change.
|
||||
#define PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS (300*NANOS_PER_MS)
|
||||
|
||||
// The minimum amount of time that must have elapsed since the device stopped
|
||||
// undergoing external acceleration before the proposed rotation can change.
|
||||
#define PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS (500*NANOS_PER_MS)
|
||||
|
||||
// If the tilt angle remains greater than the specified angle for a minimum of
|
||||
// the specified time, then the device is deemed to be lying flat
|
||||
// (just chillin' on a table).
|
||||
#define FLAT_ANGLE 75
|
||||
#define FLAT_TIME_NANOS (1000*NANOS_PER_MS)
|
||||
|
||||
// If the tilt angle has increased by at least delta degrees within the
|
||||
// specified amount of time, then the device is deemed to be swinging away
|
||||
// from the user down towards flat (tilt = 90).
|
||||
#define SWING_AWAY_ANGLE_DELTA 20
|
||||
#define SWING_TIME_NANOS (300*NANOS_PER_MS)
|
||||
|
||||
// The maximum sample inter-arrival time in milliseconds. If the acceleration
|
||||
// samples are further apart than this amount in time, we reset the state of
|
||||
// the low-pass filter and orientation properties. This helps to handle
|
||||
// boundary conditions when the device is turned on, wakes from suspend or
|
||||
// there is a significant gap in samples.
|
||||
#define MAX_FILTER_DELTA_TIME_NANOS (1000*NANOS_PER_MS)
|
||||
|
||||
// The acceleration filter time constant.
|
||||
//
|
||||
// This time constant is used to tune the acceleration filter such that
|
||||
// impulses and vibrational noise (think car dock) is suppressed before we try
|
||||
// to calculate the tilt and orientation angles.
|
||||
//
|
||||
// The filter time constant is related to the filter cutoff frequency, which
|
||||
// is the frequency at which signals are attenuated by 3dB (half the passband
|
||||
// power). Each successive octave beyond this frequency is attenuated by an
|
||||
// additional 6dB.
|
||||
//
|
||||
// Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz
|
||||
// is given by Fc = 1 / (2pi * t).
|
||||
//
|
||||
// The higher the time constant, the lower the cutoff frequency, so more noise
|
||||
// will be suppressed.
|
||||
//
|
||||
// Filtering adds latency proportional the time constant (inversely
|
||||
// proportional to the cutoff frequency) so we don't want to make the time
|
||||
// constant too large or we can lose responsiveness. Likewise we don't want
|
||||
// to make it too small or we do a poor job suppressing acceleration spikes.
|
||||
// Empirically, 100ms seems to be too small and 500ms is too large. Android
|
||||
// default is 200.
|
||||
#define FILTER_TIME_CONSTANT_MS 200.0f
|
||||
|
||||
// State for orientation detection. Thresholds for minimum and maximum
|
||||
// allowable deviation from gravity.
|
||||
//
|
||||
// If the device is undergoing external acceleration (being bumped, in a car
|
||||
// that is turning around a corner or a plane taking off) then the magnitude
|
||||
// may be substantially more or less than gravity. This can skew our
|
||||
// orientation detection by making us think that up is pointed in a different
|
||||
// direction.
|
||||
//
|
||||
// Conversely, if the device is in freefall, then there will be no gravity to
|
||||
// measure at all. This is problematic because we cannot detect the orientation
|
||||
// without gravity to tell us which way is up. A magnitude near 0 produces
|
||||
// singularities in the tilt and orientation calculations.
|
||||
//
|
||||
// In both cases, we postpone choosing an orientation.
|
||||
//
|
||||
// However, we need to tolerate some acceleration because the angular momentum
|
||||
// of turning the device can skew the observed acceleration for a short period
|
||||
// of time.
|
||||
#define NEAR_ZERO_MAGNITUDE 1 // m/s^2
|
||||
#define ACCELERATION_TOLERANCE 4 // m/s^2
|
||||
#define STANDARD_GRAVITY 9.80665f
|
||||
#define MIN_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY-ACCELERATION_TOLERANCE)
|
||||
#define MAX_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY+ACCELERATION_TOLERANCE)
|
||||
|
||||
// Maximum absolute tilt angle at which to consider orientation data. Beyond
|
||||
// this (i.e. when screen is facing the sky or ground), we completely ignore
|
||||
// orientation data.
|
||||
#define MAX_TILT 75
|
||||
|
||||
// The gap angle in degrees between adjacent orientation angles for
|
||||
// hysteresis.This creates a "dead zone" between the current orientation and a
|
||||
// proposed adjacent orientation. No orientation proposal is made when the
|
||||
// orientation angle is within the gap between the current orientation and the
|
||||
// adjacent orientation.
|
||||
#define ADJACENT_ORIENTATION_ANGLE_GAP 45
|
||||
|
||||
const int
|
||||
ProcessOrientation::tiltTolerance[][4] = {
|
||||
{-25, 70}, // ROTATION_0
|
||||
{-25, 65}, // ROTATION_90
|
||||
{-25, 60}, // ROTATION_180
|
||||
{-25, 65} // ROTATION_270
|
||||
};
|
||||
|
||||
int
|
||||
ProcessOrientation::GetProposedRotation()
|
||||
{
|
||||
return mProposedRotation;
|
||||
}
|
||||
|
||||
int
|
||||
ProcessOrientation::OnSensorChanged(const SensorData& event,
|
||||
int deviceCurrentRotation)
|
||||
{
|
||||
// The vector given in the SensorEvent points straight up (towards the sky)
|
||||
// under ideal conditions (the phone is not accelerating). I'll call this up
|
||||
// vector elsewhere.
|
||||
const InfallibleTArray<float>& values = event.values();
|
||||
float x = values[ACCELEROMETER_DATA_X];
|
||||
float y = values[ACCELEROMETER_DATA_Y];
|
||||
float z = values[ACCELEROMETER_DATA_Z];
|
||||
|
||||
LOGD
|
||||
("ProcessOrientation: Raw acceleration vector: x = %f, y = %f, z = %f,"
|
||||
"magnitude = %f\n", x, y, z, sqrt(x * x + y * y + z * z));
|
||||
// Apply a low-pass filter to the acceleration up vector in cartesian space.
|
||||
// Reset the orientation listener state if the samples are too far apart in
|
||||
// time or when we see values of (0, 0, 0) which indicates that we polled the
|
||||
// accelerometer too soon after turning it on and we don't have any data yet.
|
||||
const long now = event.timestamp();
|
||||
const long then = mLastFilteredTimestampNanos;
|
||||
const float timeDeltaMS = (now - then) * 0.000001f;
|
||||
bool skipSample = false;
|
||||
if (now < then
|
||||
|| now > then + MAX_FILTER_DELTA_TIME_NANOS
|
||||
|| (x == 0 && y == 0 && z == 0)) {
|
||||
LOGD
|
||||
("ProcessOrientation: Resetting orientation listener.");
|
||||
Reset();
|
||||
skipSample = true;
|
||||
} else {
|
||||
const float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS);
|
||||
x = alpha * (x - mLastFilteredX) + mLastFilteredX;
|
||||
y = alpha * (y - mLastFilteredY) + mLastFilteredY;
|
||||
z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
|
||||
LOGD
|
||||
("ProcessOrientation: Filtered acceleration vector: x=%f, y=%f, z=%f,"
|
||||
"magnitude=%f", z, y, z, sqrt(x * x + y * y + z * z));
|
||||
skipSample = false;
|
||||
}
|
||||
mLastFilteredTimestampNanos = now;
|
||||
mLastFilteredX = x;
|
||||
mLastFilteredY = y;
|
||||
mLastFilteredZ = z;
|
||||
|
||||
bool isAccelerating = false;
|
||||
bool isFlat = false;
|
||||
bool isSwinging = false;
|
||||
if (skipSample) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Calculate the magnitude of the acceleration vector.
|
||||
const float magnitude = sqrt(x * x + y * y + z * z);
|
||||
if (magnitude < NEAR_ZERO_MAGNITUDE) {
|
||||
LOGD
|
||||
("ProcessOrientation: Ignoring sensor data, magnitude too close to"
|
||||
" zero.");
|
||||
ClearPredictedRotation();
|
||||
} else {
|
||||
// Determine whether the device appears to be undergoing external
|
||||
// acceleration.
|
||||
if (this->IsAccelerating(magnitude)) {
|
||||
isAccelerating = true;
|
||||
mAccelerationTimestampNanos = now;
|
||||
}
|
||||
// Calculate the tilt angle. This is the angle between the up vector and
|
||||
// the x-y plane (the plane of the screen) in a range of [-90, 90]
|
||||
// degrees.
|
||||
// -90 degrees: screen horizontal and facing the ground (overhead)
|
||||
// 0 degrees: screen vertical
|
||||
// 90 degrees: screen horizontal and facing the sky (on table)
|
||||
const int tiltAngle =
|
||||
static_cast<int>(roundf(asin(z / magnitude) * RADIANS_TO_DEGREES));
|
||||
AddTiltHistoryEntry(now, tiltAngle);
|
||||
|
||||
// Determine whether the device appears to be flat or swinging.
|
||||
if (this->IsFlat(now)) {
|
||||
isFlat = true;
|
||||
mFlatTimestampNanos = now;
|
||||
}
|
||||
if (this->IsSwinging(now, tiltAngle)) {
|
||||
isSwinging = true;
|
||||
mSwingTimestampNanos = now;
|
||||
}
|
||||
// If the tilt angle is too close to horizontal then we cannot determine
|
||||
// the orientation angle of the screen.
|
||||
if (abs(tiltAngle) > MAX_TILT) {
|
||||
LOGD
|
||||
("ProcessOrientation: Ignoring sensor data, tilt angle too high:"
|
||||
" tiltAngle=%d", tiltAngle);
|
||||
ClearPredictedRotation();
|
||||
} else {
|
||||
// Calculate the orientation angle.
|
||||
// This is the angle between the x-y projection of the up vector onto
|
||||
// the +y-axis, increasing clockwise in a range of [0, 360] degrees.
|
||||
int orientationAngle =
|
||||
static_cast<int>(roundf(-atan2f(-x, y) * RADIANS_TO_DEGREES));
|
||||
if (orientationAngle < 0) {
|
||||
// atan2 returns [-180, 180]; normalize to [0, 360]
|
||||
orientationAngle += 360;
|
||||
}
|
||||
// Find the nearest rotation.
|
||||
int nearestRotation = (orientationAngle + 45) / 90;
|
||||
if (nearestRotation == 4) {
|
||||
nearestRotation = 0;
|
||||
}
|
||||
// Determine the predicted orientation.
|
||||
if (IsTiltAngleAcceptable(nearestRotation, tiltAngle)
|
||||
&&
|
||||
IsOrientationAngleAcceptable
|
||||
(nearestRotation, orientationAngle, deviceCurrentRotation)) {
|
||||
UpdatePredictedRotation(now, nearestRotation);
|
||||
LOGD
|
||||
("ProcessOrientation: Predicted: tiltAngle=%d, orientationAngle=%d,"
|
||||
" predictedRotation=%d, predictedRotationAgeMS=%f",
|
||||
tiltAngle,
|
||||
orientationAngle,
|
||||
mPredictedRotation,
|
||||
((now - mPredictedRotationTimestampNanos) * 0.000001f));
|
||||
} else {
|
||||
LOGD
|
||||
("ProcessOrientation: Ignoring sensor data, no predicted rotation:"
|
||||
" tiltAngle=%d, orientationAngle=%d",
|
||||
tiltAngle,
|
||||
orientationAngle);
|
||||
ClearPredictedRotation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Determine new proposed rotation.
|
||||
const int oldProposedRotation = mProposedRotation;
|
||||
if (mPredictedRotation < 0 || IsPredictedRotationAcceptable(now)) {
|
||||
mProposedRotation = mPredictedRotation;
|
||||
}
|
||||
// Write final statistics about where we are in the orientation detection
|
||||
// process.
|
||||
LOGD
|
||||
("ProcessOrientation: Result: oldProposedRotation=%d,currentRotation=%d, "
|
||||
"proposedRotation=%d, predictedRotation=%d, timeDeltaMS=%f, "
|
||||
"isAccelerating=%d, isFlat=%d, isSwinging=%d, timeUntilSettledMS=%f, "
|
||||
"timeUntilAccelerationDelayExpiredMS=%f, timeUntilFlatDelayExpiredMS=%f, "
|
||||
"timeUntilSwingDelayExpiredMS=%f",
|
||||
oldProposedRotation,
|
||||
deviceCurrentRotation, mProposedRotation,
|
||||
mPredictedRotation, timeDeltaMS, isAccelerating, isFlat,
|
||||
isSwinging, RemainingMS(now,
|
||||
mPredictedRotationTimestampNanos +
|
||||
PROPOSAL_SETTLE_TIME_NANOS),
|
||||
RemainingMS(now,
|
||||
mAccelerationTimestampNanos +
|
||||
PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS),
|
||||
RemainingMS(now,
|
||||
mFlatTimestampNanos +
|
||||
PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS),
|
||||
RemainingMS(now,
|
||||
mSwingTimestampNanos +
|
||||
PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS));
|
||||
// Tell the listener.
|
||||
if (mProposedRotation != oldProposedRotation && mProposedRotation >= 0) {
|
||||
LOGD
|
||||
("ProcessOrientation: Proposed rotation changed! proposedRotation=%d, "
|
||||
"oldProposedRotation=%d",
|
||||
mProposedRotation,
|
||||
oldProposedRotation);
|
||||
return mProposedRotation;
|
||||
}
|
||||
// Don't rotate screen
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessOrientation::IsTiltAngleAcceptable(int rotation, int tiltAngle)
|
||||
{
|
||||
return (tiltAngle >= tiltTolerance[rotation][0]
|
||||
&& tiltAngle <= tiltTolerance[rotation][1]);
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessOrientation::IsOrientationAngleAcceptable(int rotation,
|
||||
int orientationAngle,
|
||||
int currentRotation)
|
||||
{
|
||||
// If there is no current rotation, then there is no gap.
|
||||
// The gap is used only to introduce hysteresis among advertised orientation
|
||||
// changes to avoid flapping.
|
||||
if (currentRotation < 0) {
|
||||
return true;
|
||||
}
|
||||
// If the specified rotation is the same or is counter-clockwise adjacent
|
||||
// to the current rotation, then we set a lower bound on the orientation
|
||||
// angle. For example, if currentRotation is ROTATION_0 and proposed is
|
||||
// ROTATION_90, then we want to check orientationAngle > 45 + GAP / 2.
|
||||
if (rotation == currentRotation || rotation == (currentRotation + 1) % 4) {
|
||||
int lowerBound = rotation * 90 - 45 + ADJACENT_ORIENTATION_ANGLE_GAP / 2;
|
||||
if (rotation == 0) {
|
||||
if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (orientationAngle < lowerBound) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
// If the specified rotation is the same or is clockwise adjacent, then we
|
||||
// set an upper bound on the orientation angle. For example, if
|
||||
// currentRotation is ROTATION_0 and rotation is ROTATION_270, then we want
|
||||
// to check orientationAngle < 315 - GAP / 2.
|
||||
if (rotation == currentRotation || rotation == (currentRotation + 3) % 4) {
|
||||
int upperBound = rotation * 90 + 45 - ADJACENT_ORIENTATION_ANGLE_GAP / 2;
|
||||
if (rotation == 0) {
|
||||
if (orientationAngle <= 45 && orientationAngle > upperBound) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (orientationAngle > upperBound) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessOrientation::IsPredictedRotationAcceptable(long now)
|
||||
{
|
||||
// The predicted rotation must have settled long enough.
|
||||
if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) {
|
||||
return false;
|
||||
}
|
||||
// The last flat state (time since picked up) must have been sufficiently long
|
||||
// ago.
|
||||
if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) {
|
||||
return false;
|
||||
}
|
||||
// The last swing state (time since last movement to put down) must have been
|
||||
// sufficiently long ago.
|
||||
if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) {
|
||||
return false;
|
||||
}
|
||||
// The last acceleration state must have been sufficiently long ago.
|
||||
if (now < mAccelerationTimestampNanos
|
||||
+ PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) {
|
||||
return false;
|
||||
}
|
||||
// Looks good!
|
||||
return true;
|
||||
}
|
||||
|
||||
int
|
||||
ProcessOrientation::Reset()
|
||||
{
|
||||
mLastFilteredTimestampNanos = LONG_MIN;
|
||||
mProposedRotation = -1;
|
||||
mFlatTimestampNanos = LONG_MIN;
|
||||
mSwingTimestampNanos = LONG_MIN;
|
||||
mAccelerationTimestampNanos = LONG_MIN;
|
||||
ClearPredictedRotation();
|
||||
ClearTiltHistory();
|
||||
return -1;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessOrientation::ClearPredictedRotation()
|
||||
{
|
||||
mPredictedRotation = -1;
|
||||
mPredictedRotationTimestampNanos = LONG_MIN;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessOrientation::UpdatePredictedRotation(long now, int rotation)
|
||||
{
|
||||
if (mPredictedRotation != rotation) {
|
||||
mPredictedRotation = rotation;
|
||||
mPredictedRotationTimestampNanos = now;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessOrientation::IsAccelerating(float magnitude)
|
||||
{
|
||||
return magnitude < MIN_ACCELERATION_MAGNITUDE
|
||||
|| magnitude > MAX_ACCELERATION_MAGNITUDE;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessOrientation::ClearTiltHistory()
|
||||
{
|
||||
mTiltHistory.history[0].timestampNanos = LONG_MIN;
|
||||
mTiltHistory.index = 1;
|
||||
}
|
||||
|
||||
void
|
||||
ProcessOrientation::AddTiltHistoryEntry(long now, float tilt)
|
||||
{
|
||||
mTiltHistory.history[mTiltHistory.index].tiltAngle = tilt;
|
||||
mTiltHistory.history[mTiltHistory.index].timestampNanos = now;
|
||||
mTiltHistory.index = (mTiltHistory.index + 1) % TILT_HISTORY_SIZE;
|
||||
mTiltHistory.history[mTiltHistory.index].timestampNanos = LONG_MIN;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessOrientation::IsFlat(long now)
|
||||
{
|
||||
for (int i = mTiltHistory.index; (i = NextTiltHistoryIndex(i)) >= 0;) {
|
||||
if (mTiltHistory.history[i].tiltAngle < FLAT_ANGLE) {
|
||||
break;
|
||||
}
|
||||
if (mTiltHistory.history[i].timestampNanos + FLAT_TIME_NANOS <= now) {
|
||||
// Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ProcessOrientation::IsSwinging(long now, float tilt)
|
||||
{
|
||||
for (int i = mTiltHistory.index; (i = NextTiltHistoryIndex(i)) >= 0;) {
|
||||
if (mTiltHistory.history[i].timestampNanos + SWING_TIME_NANOS < now) {
|
||||
break;
|
||||
}
|
||||
if (mTiltHistory.history[i].tiltAngle + SWING_AWAY_ANGLE_DELTA <= tilt) {
|
||||
// Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int
|
||||
ProcessOrientation::NextTiltHistoryIndex(int index)
|
||||
{
|
||||
index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1;
|
||||
return mTiltHistory.history[index].timestampNanos != LONG_MIN ? index : -1;
|
||||
}
|
||||
|
||||
float
|
||||
ProcessOrientation::RemainingMS(long now, long until)
|
||||
{
|
||||
return now >= until ? 0 : (until - now) * 0.000001f;
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
110
widget/gonk/ProcessOrientation.h
Normal file
110
widget/gonk/ProcessOrientation.h
Normal file
@ -0,0 +1,110 @@
|
||||
/*
|
||||
* Copyright (c) 2013, Linux Foundation. All rights reserved
|
||||
*
|
||||
* Copyright (C) 2008 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef ProcessOrientation_h
|
||||
#define ProcessOrientation_h
|
||||
|
||||
#include "mozilla/Hal.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// History of observed tilt angles.
|
||||
#define TILT_HISTORY_SIZE 40
|
||||
|
||||
class ProcessOrientation {
|
||||
public:
|
||||
ProcessOrientation() {};
|
||||
~ProcessOrientation() {};
|
||||
|
||||
int OnSensorChanged(const mozilla::hal::SensorData& event, int deviceCurrentRotation);
|
||||
int Reset();
|
||||
|
||||
private:
|
||||
int GetProposedRotation();
|
||||
|
||||
// Returns true if the tilt angle is acceptable for a given predicted
|
||||
// rotation.
|
||||
bool IsTiltAngleAcceptable(int rotation, int tiltAngle);
|
||||
|
||||
// Returns true if the orientation angle is acceptable for a given predicted
|
||||
// rotation. This function takes into account the gap between adjacent
|
||||
// orientations for hysteresis.
|
||||
bool IsOrientationAngleAcceptable(int rotation, int orientationAngle,
|
||||
int currentRotation);
|
||||
|
||||
// Returns true if the predicted rotation is ready to be advertised as a
|
||||
// proposed rotation.
|
||||
bool IsPredictedRotationAcceptable(long now);
|
||||
|
||||
void ClearPredictedRotation();
|
||||
void UpdatePredictedRotation(long now, int rotation);
|
||||
bool IsAccelerating(float magnitude);
|
||||
void ClearTiltHistory();
|
||||
void AddTiltHistoryEntry(long now, float tilt);
|
||||
bool IsFlat(long now);
|
||||
bool IsSwinging(long now, float tilt);
|
||||
int NextTiltHistoryIndex(int index);
|
||||
float RemainingMS(long now, long until);
|
||||
|
||||
// The tilt angle range in degrees for each orientation. Beyond these tilt
|
||||
// angles, we don't even consider transitioning into the specified orientation.
|
||||
// We place more stringent requirements on unnatural orientations than natural
|
||||
// ones to make it less likely to accidentally transition into those states.
|
||||
// The first value of each pair is negative so it applies a limit when the
|
||||
// device is facing down (overhead reading in bed). The second value of each
|
||||
// pair is positive so it applies a limit when the device is facing up
|
||||
// (resting on a table). The ideal tilt angle is 0 (when the device is vertical)
|
||||
// so the limits establish how close to vertical the device must be in order
|
||||
// to change orientation.
|
||||
static const int tiltTolerance[][4];
|
||||
|
||||
// Timestamp and value of the last accelerometer sample.
|
||||
long mLastFilteredTimestampNanos;
|
||||
float mLastFilteredX, mLastFilteredY, mLastFilteredZ;
|
||||
|
||||
// The last proposed rotation, -1 if unknown.
|
||||
int mProposedRotation;
|
||||
|
||||
// Value of the current predicted rotation, -1 if unknown.
|
||||
int mPredictedRotation;
|
||||
|
||||
// Timestamp of when the predicted rotation most recently changed.
|
||||
long mPredictedRotationTimestampNanos;
|
||||
|
||||
// Timestamp when the device last appeared to be flat for sure (the flat delay
|
||||
// elapsed).
|
||||
long mFlatTimestampNanos;
|
||||
|
||||
// Timestamp when the device last appeared to be swinging.
|
||||
long mSwingTimestampNanos;
|
||||
|
||||
// Timestamp when the device last appeared to be undergoing external
|
||||
// acceleration.
|
||||
long mAccelerationTimestampNanos;
|
||||
|
||||
struct {
|
||||
struct {
|
||||
float tiltAngle;
|
||||
long timestampNanos;
|
||||
} history[TILT_HISTORY_SIZE];
|
||||
int index;
|
||||
} mTiltHistory;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
#endif
|
@ -37,6 +37,7 @@ CPP_SOURCES += [
|
||||
'KeyLayoutMap.cpp',
|
||||
'Keyboard.cpp',
|
||||
'OrientationObserver.cpp',
|
||||
'ProcessOrientation.cpp',
|
||||
'PixelFormat.cpp',
|
||||
'PointerController.cpp',
|
||||
'PropertyMap.cpp',
|
||||
|
Loading…
Reference in New Issue
Block a user