widget/gonk/ProcessOrientation.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/gonk/ProcessOrientation.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,511 @@
     1.4 +/*
     1.5 + * Copyright (c) 2013, Linux Foundation. All rights reserved
     1.6 + *
     1.7 + * Copyright (C) 2008 The Android Open Source Project
     1.8 + *
     1.9 + * Licensed under the Apache License, Version 2.0 (the "License");
    1.10 + * you may not use this file except in compliance with the License.
    1.11 + * You may obtain a copy of the License at
    1.12 + *
    1.13 + *      http://www.apache.org/licenses/LICENSE-2.0
    1.14 + *
    1.15 + * Unless required by applicable law or agreed to in writing, software
    1.16 + * distributed under the License is distributed on an "AS IS" BASIS,
    1.17 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    1.18 + * See the License for the specific language governing permissions and
    1.19 + * limitations under the License.
    1.20 + */
    1.21 +
    1.22 +#include "base/basictypes.h"
    1.23 +#include "mozilla/Hal.h"
    1.24 +#include "nsIScreen.h"
    1.25 +#include "nsIScreenManager.h"
    1.26 +#include "OrientationObserver.h"
    1.27 +#include "ProcessOrientation.h"
    1.28 +#include "mozilla/HalSensor.h"
    1.29 +#include "math.h"
    1.30 +#include "limits.h"
    1.31 +#include "android/log.h"
    1.32 +
    1.33 +#if 0
    1.34 +#define LOGD(args...)  __android_log_print(ANDROID_LOG_DEBUG, "ProcessOrientation" , ## args)
    1.35 +#else
    1.36 +#define LOGD(args...)
    1.37 +#endif
    1.38 +
    1.39 +namespace mozilla {
    1.40 +
    1.41 +// We work with all angles in degrees in this class.
    1.42 +#define RADIANS_TO_DEGREES (180/M_PI)
    1.43 +
    1.44 +// Number of nanoseconds per millisecond.
    1.45 +#define NANOS_PER_MS 1000000
    1.46 +
    1.47 +// Indices into SensorEvent.values for the accelerometer sensor.
    1.48 +#define ACCELEROMETER_DATA_X 0
    1.49 +#define ACCELEROMETER_DATA_Y 1
    1.50 +#define ACCELEROMETER_DATA_Z 2
    1.51 +
    1.52 +// The minimum amount of time that a predicted rotation must be stable before
    1.53 +// it is accepted as a valid rotation proposal. This value can be quite small
    1.54 +// because the low-pass filter already suppresses most of the noise so we're
    1.55 +// really just looking for quick confirmation that the last few samples are in
    1.56 +// agreement as to the desired orientation.
    1.57 +#define PROPOSAL_SETTLE_TIME_NANOS (40*NANOS_PER_MS)
    1.58 +
    1.59 +// The minimum amount of time that must have elapsed since the device last
    1.60 +// exited the flat state (time since it was picked up) before the proposed
    1.61 +// rotation can change.
    1.62 +#define PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS (500*NANOS_PER_MS)
    1.63 +
    1.64 +// The minimum amount of time that must have elapsed since the device stopped
    1.65 +// swinging (time since device appeared to be in the process of being put down
    1.66 +// or put away into a pocket) before the proposed rotation can change.
    1.67 +#define PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS (300*NANOS_PER_MS)
    1.68 +
    1.69 +// The minimum amount of time that must have elapsed since the device stopped
    1.70 +// undergoing external acceleration before the proposed rotation can change.
    1.71 +#define PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS (500*NANOS_PER_MS)
    1.72 +
    1.73 +// If the tilt angle remains greater than the specified angle for a minimum of
    1.74 +// the specified time, then the device is deemed to be lying flat
    1.75 +// (just chillin' on a table).
    1.76 +#define FLAT_ANGLE 75
    1.77 +#define FLAT_TIME_NANOS (1000*NANOS_PER_MS)
    1.78 +
    1.79 +// If the tilt angle has increased by at least delta degrees within the
    1.80 +// specified amount of time, then the device is deemed to be swinging away
    1.81 +// from the user down towards flat (tilt = 90).
    1.82 +#define SWING_AWAY_ANGLE_DELTA 20
    1.83 +#define SWING_TIME_NANOS (300*NANOS_PER_MS)
    1.84 +
    1.85 +// The maximum sample inter-arrival time in milliseconds. If the acceleration
    1.86 +// samples are further apart than this amount in time, we reset the state of
    1.87 +// the low-pass filter and orientation properties.  This helps to handle
    1.88 +// boundary conditions when the device is turned on, wakes from suspend or
    1.89 +// there is a significant gap in samples.
    1.90 +#define MAX_FILTER_DELTA_TIME_NANOS (1000*NANOS_PER_MS)
    1.91 +
    1.92 +// The acceleration filter time constant.
    1.93 +//
    1.94 +// This time constant is used to tune the acceleration filter such that
    1.95 +// impulses and vibrational noise (think car dock) is suppressed before we try
    1.96 +// to calculate the tilt and orientation angles.
    1.97 +//
    1.98 +// The filter time constant is related to the filter cutoff frequency, which
    1.99 +// is the frequency at which signals are attenuated by 3dB (half the passband
   1.100 +// power). Each successive octave beyond this frequency is attenuated by an
   1.101 +// additional 6dB.
   1.102 +//
   1.103 +// Given a time constant t in seconds, the filter cutoff frequency Fc in Hertz
   1.104 +// is given by Fc = 1 / (2pi * t).
   1.105 +//
   1.106 +// The higher the time constant, the lower the cutoff frequency, so more noise
   1.107 +// will be suppressed.
   1.108 +//
   1.109 +// Filtering adds latency proportional the time constant (inversely
   1.110 +// proportional to the cutoff frequency) so we don't want to make the time
   1.111 +// constant too large or we can lose responsiveness.  Likewise we don't want
   1.112 +// to make it too small or we do a poor job suppressing acceleration spikes.
   1.113 +// Empirically, 100ms seems to be too small and 500ms is too large. Android
   1.114 +// default is 200.
   1.115 +#define FILTER_TIME_CONSTANT_MS 200.0f
   1.116 +
   1.117 +// State for orientation detection. Thresholds for minimum and maximum
   1.118 +// allowable deviation from gravity.
   1.119 +//
   1.120 +// If the device is undergoing external acceleration (being bumped, in a car
   1.121 +// that is turning around a corner or a plane taking off) then the magnitude
   1.122 +// may be substantially more or less than gravity.  This can skew our
   1.123 +// orientation detection by making us think that up is pointed in a different
   1.124 +// direction.
   1.125 +//
   1.126 +// Conversely, if the device is in freefall, then there will be no gravity to
   1.127 +// measure at all.  This is problematic because we cannot detect the orientation
   1.128 +// without gravity to tell us which way is up. A magnitude near 0 produces
   1.129 +// singularities in the tilt and orientation calculations.
   1.130 +//
   1.131 +// In both cases, we postpone choosing an orientation.
   1.132 +//
   1.133 +// However, we need to tolerate some acceleration because the angular momentum
   1.134 +// of turning the device can skew the observed acceleration for a short period
   1.135 +// of time.
   1.136 +#define NEAR_ZERO_MAGNITUDE 1 // m/s^2
   1.137 +#define ACCELERATION_TOLERANCE 4 // m/s^2
   1.138 +#define STANDARD_GRAVITY 9.80665f
   1.139 +#define MIN_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY-ACCELERATION_TOLERANCE)
   1.140 +#define MAX_ACCELERATION_MAGNITUDE (STANDARD_GRAVITY+ACCELERATION_TOLERANCE)
   1.141 +
   1.142 +// Maximum absolute tilt angle at which to consider orientation data. Beyond
   1.143 +// this (i.e. when screen is facing the sky or ground), we completely ignore
   1.144 +// orientation data.
   1.145 +#define MAX_TILT 75
   1.146 +
   1.147 +// The gap angle in degrees between adjacent orientation angles for
   1.148 +// hysteresis.This creates a "dead zone" between the current orientation and a
   1.149 +// proposed adjacent orientation. No orientation proposal is made when the
   1.150 +// orientation angle is within the gap between the current orientation and the
   1.151 +// adjacent orientation.
   1.152 +#define ADJACENT_ORIENTATION_ANGLE_GAP 45
   1.153 +
   1.154 +const int
   1.155 +ProcessOrientation::tiltTolerance[][4] = {
   1.156 +  {-25, 70}, // ROTATION_0
   1.157 +  {-25, 65}, // ROTATION_90
   1.158 +  {-25, 60}, // ROTATION_180
   1.159 +  {-25, 65}  // ROTATION_270
   1.160 +};
   1.161 +
   1.162 +int
   1.163 +ProcessOrientation::GetProposedRotation()
   1.164 +{
   1.165 +  return mProposedRotation;
   1.166 +}
   1.167 +
   1.168 +int
   1.169 +ProcessOrientation::OnSensorChanged(const SensorData& event,
   1.170 +                                    int deviceCurrentRotation)
   1.171 +{
   1.172 +  // The vector given in the SensorEvent points straight up (towards the sky)
   1.173 +  // under ideal conditions (the phone is not accelerating). I'll call this up
   1.174 +  // vector elsewhere.
   1.175 +  const InfallibleTArray<float>& values = event.values();
   1.176 +  float x = values[ACCELEROMETER_DATA_X];
   1.177 +  float y = values[ACCELEROMETER_DATA_Y];
   1.178 +  float z = values[ACCELEROMETER_DATA_Z];
   1.179 +
   1.180 +  LOGD
   1.181 +    ("ProcessOrientation: Raw acceleration vector: x = %f, y = %f, z = %f,"
   1.182 +     "magnitude = %f\n", x, y, z, sqrt(x * x + y * y + z * z));
   1.183 +  // Apply a low-pass filter to the acceleration up vector in cartesian space.
   1.184 +  // Reset the orientation listener state if the samples are too far apart in
   1.185 +  // time or when we see values of (0, 0, 0) which indicates that we polled the
   1.186 +  // accelerometer too soon after turning it on and we don't have any data yet.
   1.187 +  const long now = event.timestamp();
   1.188 +  const long then = mLastFilteredTimestampNanos;
   1.189 +  const float timeDeltaMS = (now - then) * 0.000001f;
   1.190 +  bool skipSample = false;
   1.191 +  if (now < then
   1.192 +      || now > then + MAX_FILTER_DELTA_TIME_NANOS
   1.193 +      || (x == 0 && y == 0 && z == 0)) {
   1.194 +    LOGD
   1.195 +      ("ProcessOrientation: Resetting orientation listener.");
   1.196 +    Reset();
   1.197 +    skipSample = true;
   1.198 +  } else {
   1.199 +    const float alpha = timeDeltaMS / (FILTER_TIME_CONSTANT_MS + timeDeltaMS);
   1.200 +    x = alpha * (x - mLastFilteredX) + mLastFilteredX;
   1.201 +    y = alpha * (y - mLastFilteredY) + mLastFilteredY;
   1.202 +    z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
   1.203 +    LOGD
   1.204 +      ("ProcessOrientation: Filtered acceleration vector: x=%f, y=%f, z=%f,"
   1.205 +       "magnitude=%f", z, y, z, sqrt(x * x + y * y + z * z));
   1.206 +    skipSample = false;
   1.207 +  }
   1.208 +  mLastFilteredTimestampNanos = now;
   1.209 +  mLastFilteredX = x;
   1.210 +  mLastFilteredY = y;
   1.211 +  mLastFilteredZ = z;
   1.212 +
   1.213 +  bool isAccelerating = false;
   1.214 +  bool isFlat = false;
   1.215 +  bool isSwinging = false;
   1.216 +  if (skipSample) {
   1.217 +    return -1;
   1.218 +  }
   1.219 +
   1.220 +  // Calculate the magnitude of the acceleration vector.
   1.221 +  const float magnitude = sqrt(x * x + y * y + z * z);
   1.222 +  if (magnitude < NEAR_ZERO_MAGNITUDE) {
   1.223 +    LOGD
   1.224 +      ("ProcessOrientation: Ignoring sensor data, magnitude too close to"
   1.225 +       " zero.");
   1.226 +    ClearPredictedRotation();
   1.227 +  } else {
   1.228 +    // Determine whether the device appears to be undergoing external
   1.229 +    // acceleration.
   1.230 +    if (this->IsAccelerating(magnitude)) {
   1.231 +      isAccelerating = true;
   1.232 +      mAccelerationTimestampNanos = now;
   1.233 +    }
   1.234 +    // Calculate the tilt angle. This is the angle between the up vector and
   1.235 +    // the x-y plane (the plane of the screen) in a range of [-90, 90]
   1.236 +    // degrees.
   1.237 +    //   -90 degrees: screen horizontal and facing the ground (overhead)
   1.238 +    //     0 degrees: screen vertical
   1.239 +    //    90 degrees: screen horizontal and facing the sky (on table)
   1.240 +    const int tiltAngle =
   1.241 +      static_cast<int>(roundf(asin(z / magnitude) * RADIANS_TO_DEGREES));
   1.242 +    AddTiltHistoryEntry(now, tiltAngle);
   1.243 +
   1.244 +    // Determine whether the device appears to be flat or swinging.
   1.245 +    if (this->IsFlat(now)) {
   1.246 +      isFlat = true;
   1.247 +      mFlatTimestampNanos = now;
   1.248 +    }
   1.249 +    if (this->IsSwinging(now, tiltAngle)) {
   1.250 +      isSwinging = true;
   1.251 +      mSwingTimestampNanos = now;
   1.252 +    }
   1.253 +    // If the tilt angle is too close to horizontal then we cannot determine
   1.254 +    // the orientation angle of the screen.
   1.255 +    if (abs(tiltAngle) > MAX_TILT) {
   1.256 +      LOGD
   1.257 +        ("ProcessOrientation: Ignoring sensor data, tilt angle too high:"
   1.258 +         " tiltAngle=%d", tiltAngle);
   1.259 +      ClearPredictedRotation();
   1.260 +    } else {
   1.261 +      // Calculate the orientation angle.
   1.262 +      // This is the angle between the x-y projection of the up vector onto
   1.263 +      // the +y-axis, increasing clockwise in a range of [0, 360] degrees.
   1.264 +      int orientationAngle =
   1.265 +        static_cast<int>(roundf(-atan2f(-x, y) * RADIANS_TO_DEGREES));
   1.266 +      if (orientationAngle < 0) {
   1.267 +        // atan2 returns [-180, 180]; normalize to [0, 360]
   1.268 +        orientationAngle += 360;
   1.269 +      }
   1.270 +      // Find the nearest rotation.
   1.271 +      int nearestRotation = (orientationAngle + 45) / 90;
   1.272 +      if (nearestRotation == 4) {
   1.273 +        nearestRotation = 0;
   1.274 +      }
   1.275 +      // Determine the predicted orientation.
   1.276 +      if (IsTiltAngleAcceptable(nearestRotation, tiltAngle)
   1.277 +          &&
   1.278 +          IsOrientationAngleAcceptable
   1.279 +          (nearestRotation, orientationAngle, deviceCurrentRotation)) {
   1.280 +        UpdatePredictedRotation(now, nearestRotation);
   1.281 +        LOGD
   1.282 +          ("ProcessOrientation: Predicted: tiltAngle=%d, orientationAngle=%d,"
   1.283 +           " predictedRotation=%d, predictedRotationAgeMS=%f",
   1.284 +           tiltAngle,
   1.285 +           orientationAngle,
   1.286 +           mPredictedRotation,
   1.287 +           ((now - mPredictedRotationTimestampNanos) * 0.000001f));
   1.288 +      } else {
   1.289 +        LOGD
   1.290 +          ("ProcessOrientation: Ignoring sensor data, no predicted rotation:"
   1.291 +           " tiltAngle=%d, orientationAngle=%d",
   1.292 +           tiltAngle,
   1.293 +           orientationAngle);
   1.294 +        ClearPredictedRotation();
   1.295 +      }
   1.296 +    }
   1.297 +  }
   1.298 +
   1.299 +  // Determine new proposed rotation.
   1.300 +  const int oldProposedRotation = mProposedRotation;
   1.301 +  if (mPredictedRotation < 0 || IsPredictedRotationAcceptable(now)) {
   1.302 +    mProposedRotation = mPredictedRotation;
   1.303 +  }
   1.304 +  // Write final statistics about where we are in the orientation detection
   1.305 +  // process.
   1.306 +  LOGD
   1.307 +    ("ProcessOrientation: Result: oldProposedRotation=%d,currentRotation=%d, "
   1.308 +     "proposedRotation=%d, predictedRotation=%d, timeDeltaMS=%f, "
   1.309 +     "isAccelerating=%d, isFlat=%d, isSwinging=%d, timeUntilSettledMS=%f, "
   1.310 +     "timeUntilAccelerationDelayExpiredMS=%f, timeUntilFlatDelayExpiredMS=%f, "
   1.311 +     "timeUntilSwingDelayExpiredMS=%f",
   1.312 +     oldProposedRotation,
   1.313 +     deviceCurrentRotation, mProposedRotation,
   1.314 +     mPredictedRotation, timeDeltaMS, isAccelerating, isFlat,
   1.315 +     isSwinging, RemainingMS(now,
   1.316 +                             mPredictedRotationTimestampNanos +
   1.317 +                             PROPOSAL_SETTLE_TIME_NANOS),
   1.318 +     RemainingMS(now,
   1.319 +                 mAccelerationTimestampNanos +
   1.320 +                 PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS),
   1.321 +     RemainingMS(now,
   1.322 +                 mFlatTimestampNanos +
   1.323 +                 PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS),
   1.324 +     RemainingMS(now,
   1.325 +                 mSwingTimestampNanos +
   1.326 +                 PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS));
   1.327 +  // Tell the listener.
   1.328 +  if (mProposedRotation != oldProposedRotation && mProposedRotation >= 0) {
   1.329 +    LOGD
   1.330 +      ("ProcessOrientation: Proposed rotation changed!  proposedRotation=%d, "
   1.331 +       "oldProposedRotation=%d",
   1.332 +       mProposedRotation,
   1.333 +       oldProposedRotation);
   1.334 +    return mProposedRotation;
   1.335 +  }
   1.336 +  // Don't rotate screen
   1.337 +  return -1;
   1.338 +}
   1.339 +
   1.340 +bool
   1.341 +ProcessOrientation::IsTiltAngleAcceptable(int rotation, int tiltAngle)
   1.342 +{
   1.343 +  return (tiltAngle >= tiltTolerance[rotation][0]
   1.344 +          && tiltAngle <= tiltTolerance[rotation][1]);
   1.345 +}
   1.346 +
   1.347 +bool
   1.348 +ProcessOrientation::IsOrientationAngleAcceptable(int rotation,
   1.349 +                                                 int orientationAngle,
   1.350 +                                                 int currentRotation)
   1.351 +{
   1.352 +  // If there is no current rotation, then there is no gap.
   1.353 +  // The gap is used only to introduce hysteresis among advertised orientation
   1.354 +  // changes to avoid flapping.
   1.355 +  if (currentRotation < 0) {
   1.356 +    return true;
   1.357 +  }
   1.358 +  // If the specified rotation is the same or is counter-clockwise adjacent
   1.359 +  // to the current rotation, then we set a lower bound on the orientation
   1.360 +  // angle. For example, if currentRotation is ROTATION_0 and proposed is
   1.361 +  // ROTATION_90, then we want to check orientationAngle > 45 + GAP / 2.
   1.362 +  if (rotation == currentRotation || rotation == (currentRotation + 1) % 4) {
   1.363 +    int lowerBound = rotation * 90 - 45 + ADJACENT_ORIENTATION_ANGLE_GAP / 2;
   1.364 +    if (rotation == 0) {
   1.365 +      if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) {
   1.366 +        return false;
   1.367 +      }
   1.368 +    } else {
   1.369 +      if (orientationAngle < lowerBound) {
   1.370 +        return false;
   1.371 +      }
   1.372 +    }
   1.373 +  }
   1.374 +  // If the specified rotation is the same or is clockwise adjacent, then we
   1.375 +  // set an upper bound on the orientation angle. For example, if
   1.376 +  // currentRotation is ROTATION_0 and rotation is ROTATION_270, then we want
   1.377 +  // to check orientationAngle < 315 - GAP / 2.
   1.378 +  if (rotation == currentRotation || rotation == (currentRotation + 3) % 4) {
   1.379 +    int upperBound = rotation * 90 + 45 - ADJACENT_ORIENTATION_ANGLE_GAP / 2;
   1.380 +    if (rotation == 0) {
   1.381 +      if (orientationAngle <= 45 && orientationAngle > upperBound) {
   1.382 +        return false;
   1.383 +      }
   1.384 +    } else {
   1.385 +      if (orientationAngle > upperBound) {
   1.386 +        return false;
   1.387 +      }
   1.388 +    }
   1.389 +  }
   1.390 +  return true;
   1.391 +}
   1.392 +
   1.393 +bool
   1.394 +ProcessOrientation::IsPredictedRotationAcceptable(long now)
   1.395 +{
   1.396 +  // The predicted rotation must have settled long enough.
   1.397 +  if (now < mPredictedRotationTimestampNanos + PROPOSAL_SETTLE_TIME_NANOS) {
   1.398 +    return false;
   1.399 +  }
   1.400 +  // The last flat state (time since picked up) must have been sufficiently long
   1.401 +  // ago.
   1.402 +  if (now < mFlatTimestampNanos + PROPOSAL_MIN_TIME_SINCE_FLAT_ENDED_NANOS) {
   1.403 +    return false;
   1.404 +  }
   1.405 +  // The last swing state (time since last movement to put down) must have been
   1.406 +  // sufficiently long ago.
   1.407 +  if (now < mSwingTimestampNanos + PROPOSAL_MIN_TIME_SINCE_SWING_ENDED_NANOS) {
   1.408 +    return false;
   1.409 +  }
   1.410 +  // The last acceleration state must have been sufficiently long ago.
   1.411 +  if (now < mAccelerationTimestampNanos
   1.412 +      + PROPOSAL_MIN_TIME_SINCE_ACCELERATION_ENDED_NANOS) {
   1.413 +    return false;
   1.414 +  }
   1.415 +  // Looks good!
   1.416 +  return true;
   1.417 +}
   1.418 +
   1.419 +int
   1.420 +ProcessOrientation::Reset()
   1.421 +{
   1.422 +  mLastFilteredTimestampNanos = LONG_MIN;
   1.423 +  mProposedRotation = -1;
   1.424 +  mFlatTimestampNanos = LONG_MIN;
   1.425 +  mSwingTimestampNanos = LONG_MIN;
   1.426 +  mAccelerationTimestampNanos = LONG_MIN;
   1.427 +  ClearPredictedRotation();
   1.428 +  ClearTiltHistory();
   1.429 +  return -1;
   1.430 +}
   1.431 +
   1.432 +void
   1.433 +ProcessOrientation::ClearPredictedRotation()
   1.434 +{
   1.435 +  mPredictedRotation = -1;
   1.436 +  mPredictedRotationTimestampNanos = LONG_MIN;
   1.437 +}
   1.438 +
   1.439 +void
   1.440 +ProcessOrientation::UpdatePredictedRotation(long now, int rotation)
   1.441 +{
   1.442 +  if (mPredictedRotation != rotation) {
   1.443 +    mPredictedRotation = rotation;
   1.444 +    mPredictedRotationTimestampNanos = now;
   1.445 +  }
   1.446 +}
   1.447 +
   1.448 +bool
   1.449 +ProcessOrientation::IsAccelerating(float magnitude)
   1.450 +{
   1.451 +  return magnitude < MIN_ACCELERATION_MAGNITUDE
   1.452 +    || magnitude > MAX_ACCELERATION_MAGNITUDE;
   1.453 +}
   1.454 +
   1.455 +void
   1.456 +ProcessOrientation::ClearTiltHistory()
   1.457 +{
   1.458 +  mTiltHistory.history[0].timestampNanos = LONG_MIN;
   1.459 +  mTiltHistory.index = 1;
   1.460 +}
   1.461 +
   1.462 +void
   1.463 +ProcessOrientation::AddTiltHistoryEntry(long now, float tilt)
   1.464 +{
   1.465 +  mTiltHistory.history[mTiltHistory.index].tiltAngle = tilt;
   1.466 +  mTiltHistory.history[mTiltHistory.index].timestampNanos = now;
   1.467 +  mTiltHistory.index = (mTiltHistory.index + 1) % TILT_HISTORY_SIZE;
   1.468 +  mTiltHistory.history[mTiltHistory.index].timestampNanos = LONG_MIN;
   1.469 +}
   1.470 +
   1.471 +bool
   1.472 +ProcessOrientation::IsFlat(long now)
   1.473 +{
   1.474 +  for (int i = mTiltHistory.index; (i = NextTiltHistoryIndex(i)) >= 0;) {
   1.475 +    if (mTiltHistory.history[i].tiltAngle < FLAT_ANGLE) {
   1.476 +      break;
   1.477 +    }
   1.478 +    if (mTiltHistory.history[i].timestampNanos + FLAT_TIME_NANOS <= now) {
   1.479 +      // Tilt has remained greater than FLAT_TILT_ANGLE for FLAT_TIME_NANOS.
   1.480 +      return true;
   1.481 +    }
   1.482 +  }
   1.483 +  return false;
   1.484 +}
   1.485 +
   1.486 +bool
   1.487 +ProcessOrientation::IsSwinging(long now, float tilt)
   1.488 +{
   1.489 +  for (int i = mTiltHistory.index; (i = NextTiltHistoryIndex(i)) >= 0;) {
   1.490 +    if (mTiltHistory.history[i].timestampNanos + SWING_TIME_NANOS < now) {
   1.491 +      break;
   1.492 +    }
   1.493 +    if (mTiltHistory.history[i].tiltAngle + SWING_AWAY_ANGLE_DELTA <= tilt) {
   1.494 +      // Tilted away by SWING_AWAY_ANGLE_DELTA within SWING_TIME_NANOS.
   1.495 +      return true;
   1.496 +    }
   1.497 +  }
   1.498 +  return false;
   1.499 +}
   1.500 +
   1.501 +int
   1.502 +ProcessOrientation::NextTiltHistoryIndex(int index)
   1.503 +{
   1.504 +  index = (index == 0 ? TILT_HISTORY_SIZE : index) - 1;
   1.505 +  return mTiltHistory.history[index].timestampNanos != LONG_MIN ? index : -1;
   1.506 +}
   1.507 +
   1.508 +float
   1.509 +ProcessOrientation::RemainingMS(long now, long until)
   1.510 +{
   1.511 +  return now >= until ? 0 : (until - now) * 0.000001f;
   1.512 +}
   1.513 +
   1.514 +} // namespace mozilla

mercurial