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