blob: d128b57486984ec8f31829547bc1204274a22fc5 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view;
18
19import android.content.Context;
20import android.hardware.Sensor;
21import android.hardware.SensorEvent;
22import android.hardware.SensorEventListener;
23import android.hardware.SensorManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.util.Log;
Jeff Brown4519f072011-01-23 13:16:01 -080025import android.util.Slog;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026
27/**
28 * A special helper class used by the WindowManager
29 * for receiving notifications from the SensorManager when
30 * the orientation of the device has changed.
Dianne Hackborne5439f22010-10-02 16:53:50 -070031 *
32 * NOTE: If changing anything here, please run the API demo
33 * "App/Activity/Screen Orientation" to ensure that all orientation
34 * modes still work correctly.
35 *
Jeff Brown4519f072011-01-23 13:16:01 -080036 * You can also visualize the behavior of the WindowOrientationListener by
37 * enabling the window orientation listener log using the Development Settings
38 * in the Dev Tools application (Development.apk)
39 * and running frameworks/base/tools/orientationplot/orientationplot.py.
40 *
41 * More information about how to tune this algorithm in
42 * frameworks/base/tools/orientationplot/README.txt.
43 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 * @hide
45 */
46public abstract class WindowOrientationListener {
47 private static final String TAG = "WindowOrientationListener";
Suchi Amalapurapud9328512010-01-04 16:18:06 -080048 private static final boolean DEBUG = false;
Joe Onorato43a17652011-04-06 19:22:23 -070049 private static final boolean localLOGV = DEBUG || false;
Jeff Brown4519f072011-01-23 13:16:01 -080050
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051 private SensorManager mSensorManager;
Jeff Brown4519f072011-01-23 13:16:01 -080052 private boolean mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053 private int mRate;
54 private Sensor mSensor;
Suchi Amalapurapu63104ed2009-12-15 14:06:08 -080055 private SensorEventListenerImpl mSensorEventListener;
Jeff Brown4519f072011-01-23 13:16:01 -080056 boolean mLogEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057
58 /**
59 * Creates a new WindowOrientationListener.
60 *
61 * @param context for the WindowOrientationListener.
62 */
63 public WindowOrientationListener(Context context) {
Jeff Brown4519f072011-01-23 13:16:01 -080064 this(context, SensorManager.SENSOR_DELAY_UI);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065 }
66
67 /**
68 * Creates a new WindowOrientationListener.
69 *
70 * @param context for the WindowOrientationListener.
71 * @param rate at which sensor events are processed (see also
72 * {@link android.hardware.SensorManager SensorManager}). Use the default
73 * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL
74 * SENSOR_DELAY_NORMAL} for simple screen orientation change detection.
Steve Howard1ba101f2010-02-23 14:30:13 -080075 *
Jeff Brown4519f072011-01-23 13:16:01 -080076 * This constructor is private since no one uses it.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 */
Steve Howard1ba101f2010-02-23 14:30:13 -080078 private WindowOrientationListener(Context context, int rate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
80 mRate = rate;
81 mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
82 if (mSensor != null) {
83 // Create listener only if sensors do exist
Steve Howard5f531ae2010-08-05 17:14:53 -070084 mSensorEventListener = new SensorEventListenerImpl(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 }
86 }
87
88 /**
89 * Enables the WindowOrientationListener so it will monitor the sensor and call
90 * {@link #onOrientationChanged} when the device orientation changes.
91 */
92 public void enable() {
93 if (mSensor == null) {
94 Log.w(TAG, "Cannot detect sensors. Not enabled");
95 return;
96 }
97 if (mEnabled == false) {
98 if (localLOGV) Log.d(TAG, "WindowOrientationListener enabled");
99 mSensorManager.registerListener(mSensorEventListener, mSensor, mRate);
100 mEnabled = true;
101 }
102 }
103
104 /**
105 * Disables the WindowOrientationListener.
106 */
107 public void disable() {
108 if (mSensor == null) {
109 Log.w(TAG, "Cannot detect sensors. Invalid disable");
110 return;
111 }
112 if (mEnabled == true) {
113 if (localLOGV) Log.d(TAG, "WindowOrientationListener disabled");
114 mSensorManager.unregisterListener(mSensorEventListener);
115 mEnabled = false;
116 }
117 }
118
Jeff Brown4519f072011-01-23 13:16:01 -0800119 /**
120 * Gets the current orientation.
121 * @param lastRotation
122 * @return
123 */
Steve Howard5eb49582010-08-16 11:41:58 -0700124 public int getCurrentRotation(int lastRotation) {
Suchi Amalapurapu63104ed2009-12-15 14:06:08 -0800125 if (mEnabled) {
Steve Howard5eb49582010-08-16 11:41:58 -0700126 return mSensorEventListener.getCurrentRotation(lastRotation);
Suchi Amalapurapu63104ed2009-12-15 14:06:08 -0800127 }
Steve Howard5eb49582010-08-16 11:41:58 -0700128 return lastRotation;
Dianne Hackborne4fbd622009-03-27 18:09:16 -0700129 }
Steve Howard5f531ae2010-08-05 17:14:53 -0700130
131 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 * Returns true if sensor is enabled and false otherwise
133 */
134 public boolean canDetectOrientation() {
135 return mSensor != null;
136 }
Steve Howard1ba101f2010-02-23 14:30:13 -0800137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800138 /**
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700139 * Called when the rotation view of the device has changed.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 *
Steve Howard1ba101f2010-02-23 14:30:13 -0800141 * @param rotation The new orientation of the device, one of the Surface.ROTATION_* constants.
142 * @see Surface
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800143 */
Jeff Brown4519f072011-01-23 13:16:01 -0800144 public abstract void onOrientationChanged(int rotation);
145
146 /**
147 * Enables or disables the window orientation listener logging for use with
148 * the orientationplot.py tool.
149 * Logging is usually enabled via Development Settings. (See class comments.)
150 * @param enable True to enable logging.
151 */
152 public void setLogEnabled(boolean enable) {
153 mLogEnabled = enable;
154 }
155
156 /**
157 * This class filters the raw accelerometer data and tries to detect actual changes in
158 * orientation. This is a very ill-defined problem so there are a lot of tweakable parameters,
159 * but here's the outline:
160 *
161 * - Low-pass filter the accelerometer vector in cartesian coordinates. We do it in
162 * cartesian space because the orientation calculations are sensitive to the
163 * absolute magnitude of the acceleration. In particular, there are singularities
164 * in the calculation as the magnitude approaches 0. By performing the low-pass
165 * filtering early, we can eliminate high-frequency impulses systematically.
166 *
167 * - Convert the acceleromter vector from cartesian to spherical coordinates.
168 * Since we're dealing with rotation of the device, this is the sensible coordinate
169 * system to work in. The zenith direction is the Z-axis, the direction the screen
170 * is facing. The radial distance is referred to as the magnitude below.
171 * The elevation angle is referred to as the "tilt" below.
172 * The azimuth angle is referred to as the "orientation" below (and the azimuth axis is
173 * the Y-axis).
174 * See http://en.wikipedia.org/wiki/Spherical_coordinate_system for reference.
175 *
176 * - If the tilt angle is too close to horizontal (near 90 or -90 degrees), do nothing.
177 * The orientation angle is not meaningful when the device is nearly horizontal.
178 * The tilt angle thresholds are set differently for each orientation and different
179 * limits are applied when the device is facing down as opposed to when it is facing
180 * forward or facing up.
181 *
182 * - When the orientation angle reaches a certain threshold, consider transitioning
183 * to the corresponding orientation. These thresholds have some hysteresis built-in
184 * to avoid oscillations between adjacent orientations.
185 *
186 * - Use the magnitude to judge the confidence of the orientation.
187 * Under ideal conditions, the magnitude should equal to that of gravity. When it
188 * differs significantly, we know the device is under external acceleration and
189 * we can't trust the data.
190 *
191 * - Use the tilt angle to judge the confidence of the orientation.
192 * When the tilt angle is high in absolute value then the device is nearly flat
193 * so small physical movements produce large changes in orientation angle.
194 * This can be the case when the device is being picked up from a table.
195 *
196 * - Use the orientation angle to judge the confidence of the orientation.
197 * The close the orientation angle is to the canonical orientation angle, the better.
198 *
199 * - Based on the aggregate confidence, we determine how long we want to wait for
200 * the new orientation to settle. This is accomplished by integrating the confidence
201 * for each orientation over time. When a threshold integration sum is reached
202 * then we actually change orientations.
203 *
204 * Details are explained inline.
205 */
206 static final class SensorEventListenerImpl implements SensorEventListener {
207 // We work with all angles in degrees in this class.
208 private static final float RADIANS_TO_DEGREES = (float) (180 / Math.PI);
209
210 // Indices into SensorEvent.values for the accelerometer sensor.
211 private static final int ACCELEROMETER_DATA_X = 0;
212 private static final int ACCELEROMETER_DATA_Y = 1;
213 private static final int ACCELEROMETER_DATA_Z = 2;
214
215 // Rotation constants.
216 // These are the same as Surface rotation constants with the addition of a 5th
217 // unknown state when we are not confident about the proporsed orientation.
218 // One important property of these constants is that they are equal to the
219 // orientation angle itself divided by 90. We use this fact to map
220 // back and forth between orientation angles and rotation values.
221 private static final int ROTATION_UNKNOWN = -1;
222 //private static final int ROTATION_0 = Surface.ROTATION_0; // 0
223 //private static final int ROTATION_90 = Surface.ROTATION_90; // 1
224 //private static final int ROTATION_180 = Surface.ROTATION_180; // 2
225 //private static final int ROTATION_270 = Surface.ROTATION_270; // 3
226
227 private final WindowOrientationListener mOrientationListener;
228
229 private int mRotation = ROTATION_UNKNOWN;
230
231 /* State for first order low-pass filtering of accelerometer data.
232 * See http://en.wikipedia.org/wiki/Low-pass_filter#Discrete-time_realization for
233 * signal processing background.
234 */
235
236 private long mLastTimestamp = Long.MAX_VALUE; // in nanoseconds
237 private float mLastFilteredX, mLastFilteredY, mLastFilteredZ;
238
239 // The maximum sample inter-arrival time in milliseconds.
240 // If the acceleration samples are further apart than this amount in time, we reset the
241 // state of the low-pass filter and orientation properties. This helps to handle
242 // boundary conditions when the device is turned on, wakes from suspend or there is
243 // a significant gap in samples.
244 private static final float MAX_FILTER_DELTA_TIME_MS = 1000;
245
246 // The acceleration filter cutoff frequency.
247 // This is the frequency at which signals are attenuated by 3dB (half the passband power).
248 // Each successive octave beyond this frequency is attenuated by an additional 6dB.
249 //
250 // We choose the cutoff frequency such that impulses and vibrational noise
251 // (think car dock) is suppressed. However, this filtering does not eliminate
252 // all possible sources of orientation ambiguity so we also rely on a dynamic
253 // settle time for establishing a new orientation. Filtering adds latency
254 // inversely proportional to the cutoff frequency so we don't want to make
255 // it too small or we can lose hundreds of milliseconds of responsiveness.
256 private static final float FILTER_CUTOFF_FREQUENCY_HZ = 1f;
257 private static final float FILTER_TIME_CONSTANT_MS = (float)(500.0f
258 / (Math.PI * FILTER_CUTOFF_FREQUENCY_HZ)); // t = 1 / (2pi * Fc) * 1000ms
259
260 // The filter gain.
261 // We choose a value slightly less than unity to avoid numerical instabilities due
262 // to floating-point error accumulation.
263 private static final float FILTER_GAIN = 0.999f;
264
265 /* State for orientation detection. */
266
267 // Thresholds for minimum and maximum allowable deviation from gravity.
268 //
269 // If the device is undergoing external acceleration (being bumped, in a car
270 // that is turning around a corner or a plane taking off) then the magnitude
271 // may be substantially more or less than gravity. This can skew our orientation
272 // detection by making us think that up is pointed in a different direction.
273 //
274 // Conversely, if the device is in freefall, then there will be no gravity to
275 // measure at all. This is problematic because we cannot detect the orientation
276 // without gravity to tell us which way is up. A magnitude near 0 produces
277 // singularities in the tilt and orientation calculations.
278 //
279 // In both cases, we postpone choosing an orientation.
280 private static final float MIN_ACCELERATION_MAGNITUDE =
281 SensorManager.STANDARD_GRAVITY * 0.5f;
282 private static final float MAX_ACCELERATION_MAGNITUDE =
283 SensorManager.STANDARD_GRAVITY * 1.5f;
284
285 // Maximum absolute tilt angle at which to consider orientation data. Beyond this (i.e.
286 // when screen is facing the sky or ground), we completely ignore orientation data.
287 private static final int MAX_TILT = 75;
288
289 // The tilt angle range in degrees for each orientation.
290 // Beyond these tilt angles, we don't even consider transitioning into the
291 // specified orientation. We place more stringent requirements on unnatural
292 // orientations than natural ones to make it less likely to accidentally transition
293 // into those states.
294 // The first value of each pair is negative so it applies a limit when the device is
295 // facing down (overhead reading in bed).
296 // The second value of each pair is positive so it applies a limit when the device is
297 // facing up (resting on a table).
298 // The ideal tilt angle is 0 (when the device is vertical) so the limits establish
299 // how close to vertical the device must be in order to change orientation.
300 private static final int[][] TILT_TOLERANCE = new int[][] {
301 /* ROTATION_0 */ { -20, 75 },
302 /* ROTATION_90 */ { -20, 70 },
303 /* ROTATION_180 */ { -20, 65 },
304 /* ROTATION_270 */ { -20, 70 }
305 };
306
307 // The gap angle in degrees between adjacent orientation angles for hysteresis.
308 // This creates a "dead zone" between the current orientation and a proposed
309 // adjacent orientation. No orientation proposal is made when the orientation
310 // angle is within the gap between the current orientation and the adjacent
311 // orientation.
312 private static final int ADJACENT_ORIENTATION_ANGLE_GAP = 30;
313
314 // The confidence scale factors for angle, tilt and magnitude.
315 // When the distance between the actual value and the ideal value is the
316 // specified delta, orientation transitions will take twice as long as they would
317 // in the ideal case. Increasing or decreasing the delta has an exponential effect
318 // on each factor's influence over the transition time.
319
320 // Transition takes 2x longer when angle is 30 degrees from ideal orientation angle.
321 private static final float ORIENTATION_ANGLE_CONFIDENCE_SCALE =
322 confidenceScaleFromDelta(30);
323
324 // Transition takes 2x longer when tilt is 45 degrees from vertical.
325 private static final float TILT_ANGLE_CONFIDENCE_SCALE = confidenceScaleFromDelta(45);
326
327 // Transition takes 2x longer when acceleration is 0.25 Gs.
328 private static final float MAGNITUDE_CONFIDENCE_SCALE = confidenceScaleFromDelta(
329 SensorManager.STANDARD_GRAVITY * 0.25f);
330
331 // The number of milliseconds for which a new orientation must be stable before
332 // we perform an orientation change under ideal conditions. It will take
333 // proportionally longer than this to effect an orientation change when
334 // the proposed orientation confidence is low.
335 private static final float ORIENTATION_SETTLE_TIME_MS = 250;
336
337 // The confidence that we have abount effecting each orientation change.
338 // When one of these values exceeds 1.0, we have determined our new orientation!
339 private float mConfidence[] = new float[4];
340
341 public SensorEventListenerImpl(WindowOrientationListener orientationListener) {
342 mOrientationListener = orientationListener;
343 }
344
345 public int getCurrentRotation(int lastRotation) {
346 return mRotation != ROTATION_UNKNOWN ? mRotation : lastRotation;
347 }
348
349 @Override
350 public void onAccuracyChanged(Sensor sensor, int accuracy) {
351 }
352
353 @Override
354 public void onSensorChanged(SensorEvent event) {
355 final boolean log = mOrientationListener.mLogEnabled;
356
357 // The vector given in the SensorEvent points straight up (towards the sky) under ideal
358 // conditions (the phone is not accelerating). I'll call this up vector elsewhere.
359 float x = event.values[ACCELEROMETER_DATA_X];
360 float y = event.values[ACCELEROMETER_DATA_Y];
361 float z = event.values[ACCELEROMETER_DATA_Z];
362
363 if (log) {
364 Slog.v(TAG, "Raw acceleration vector: " +
365 "x=" + x + ", y=" + y + ", z=" + z);
366 }
367
368 // Apply a low-pass filter to the acceleration up vector in cartesian space.
369 // Reset the orientation listener state if the samples are too far apart in time
370 // or when we see values of (0, 0, 0) which indicates that we polled the
371 // accelerometer too soon after turning it on and we don't have any data yet.
372 final float timeDeltaMS = (event.timestamp - mLastTimestamp) * 0.000001f;
373 boolean skipSample;
374 if (timeDeltaMS <= 0 || timeDeltaMS > MAX_FILTER_DELTA_TIME_MS
375 || (x == 0 && y == 0 && z == 0)) {
376 if (log) {
377 Slog.v(TAG, "Resetting orientation listener.");
378 }
379 for (int i = 0; i < 4; i++) {
380 mConfidence[i] = 0;
381 }
382 skipSample = true;
383 } else {
384 final float alpha = timeDeltaMS
385 / (FILTER_TIME_CONSTANT_MS + timeDeltaMS) * FILTER_GAIN;
386 x = alpha * (x - mLastFilteredX) + mLastFilteredX;
387 y = alpha * (y - mLastFilteredY) + mLastFilteredY;
388 z = alpha * (z - mLastFilteredZ) + mLastFilteredZ;
389 if (log) {
390 Slog.v(TAG, "Filtered acceleration vector: " +
391 "x=" + x + ", y=" + y + ", z=" + z);
392 }
393 skipSample = false;
394 }
395 mLastTimestamp = event.timestamp;
396 mLastFilteredX = x;
397 mLastFilteredY = y;
398 mLastFilteredZ = z;
399
400 boolean orientationChanged = false;
401 if (!skipSample) {
402 // Determine a proposed orientation based on the currently available data.
403 int proposedOrientation = ROTATION_UNKNOWN;
404 float combinedConfidence = 1.0f;
405
406 // Calculate the magnitude of the acceleration vector.
407 final float magnitude = (float) Math.sqrt(x * x + y * y + z * z);
408 if (magnitude < MIN_ACCELERATION_MAGNITUDE
409 || magnitude > MAX_ACCELERATION_MAGNITUDE) {
410 if (log) {
411 Slog.v(TAG, "Ignoring sensor data, magnitude out of range: "
412 + "magnitude=" + magnitude);
413 }
414 } else {
415 // Calculate the tilt angle.
416 // This is the angle between the up vector and the x-y plane (the plane of
417 // the screen) in a range of [-90, 90] degrees.
418 // -90 degrees: screen horizontal and facing the ground (overhead)
419 // 0 degrees: screen vertical
420 // 90 degrees: screen horizontal and facing the sky (on table)
421 final int tiltAngle = (int) Math.round(
422 Math.asin(z / magnitude) * RADIANS_TO_DEGREES);
423
424 // If the tilt angle is too close to horizontal then we cannot determine
425 // the orientation angle of the screen.
426 if (Math.abs(tiltAngle) > MAX_TILT) {
427 if (log) {
428 Slog.v(TAG, "Ignoring sensor data, tilt angle too high: "
429 + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle);
430 }
431 } else {
432 // Calculate the orientation angle.
433 // This is the angle between the x-y projection of the up vector onto
434 // the +y-axis, increasing clockwise in a range of [0, 360] degrees.
435 int orientationAngle = (int) Math.round(
436 -Math.atan2(-x, y) * RADIANS_TO_DEGREES);
437 if (orientationAngle < 0) {
438 // atan2 returns [-180, 180]; normalize to [0, 360]
439 orientationAngle += 360;
440 }
441
442 // Find the nearest orientation.
443 // An orientation of 0 can have a nearest angle of 0 or 360 depending
444 // on which is closer to the measured orientation angle. We leave the
445 // nearest angle at 360 in that case since it makes the delta calculation
446 // for orientation angle confidence easier below.
447 int nearestOrientation = (orientationAngle + 45) / 90;
448 int nearestOrientationAngle = nearestOrientation * 90;
449 if (nearestOrientation == 4) {
450 nearestOrientation = 0;
451 }
452
453 // Determine the proposed orientation.
454 // The confidence of the proposal is 1.0 when it is ideal and it
455 // decays exponentially as the proposal moves further from the ideal
456 // angle, tilt and magnitude of the proposed orientation.
457 if (isTiltAngleAcceptable(nearestOrientation, tiltAngle)
458 && isOrientationAngleAcceptable(nearestOrientation,
459 orientationAngle)) {
460 proposedOrientation = nearestOrientation;
461
462 final float idealOrientationAngle = nearestOrientationAngle;
463 final float orientationConfidence = confidence(orientationAngle,
464 idealOrientationAngle, ORIENTATION_ANGLE_CONFIDENCE_SCALE);
465
466 final float idealTiltAngle = 0;
467 final float tiltConfidence = confidence(tiltAngle,
468 idealTiltAngle, TILT_ANGLE_CONFIDENCE_SCALE);
469
470 final float idealMagnitude = SensorManager.STANDARD_GRAVITY;
471 final float magnitudeConfidence = confidence(magnitude,
472 idealMagnitude, MAGNITUDE_CONFIDENCE_SCALE);
473
474 combinedConfidence = orientationConfidence
475 * tiltConfidence * magnitudeConfidence;
476
477 if (log) {
478 Slog.v(TAG, "Proposal: "
479 + "magnitude=" + magnitude
480 + ", tiltAngle=" + tiltAngle
481 + ", orientationAngle=" + orientationAngle
482 + ", proposedOrientation=" + proposedOrientation
483 + ", combinedConfidence=" + combinedConfidence
484 + ", orientationConfidence=" + orientationConfidence
485 + ", tiltConfidence=" + tiltConfidence
486 + ", magnitudeConfidence=" + magnitudeConfidence);
487 }
488 } else {
489 if (log) {
490 Slog.v(TAG, "Ignoring sensor data, no proposal: "
491 + "magnitude=" + magnitude + ", tiltAngle=" + tiltAngle
492 + ", orientationAngle=" + orientationAngle);
493 }
494 }
495 }
496 }
497
498 // Sum up the orientation confidence weights.
499 // Detect an orientation change when the sum reaches 1.0.
500 final float confidenceAmount = combinedConfidence * timeDeltaMS
501 / ORIENTATION_SETTLE_TIME_MS;
502 for (int i = 0; i < 4; i++) {
503 if (i == proposedOrientation) {
504 mConfidence[i] += confidenceAmount;
505 if (mConfidence[i] >= 1.0f) {
506 mConfidence[i] = 1.0f;
507
508 if (i != mRotation) {
509 if (log) {
510 Slog.v(TAG, "Orientation changed! rotation=" + i);
511 }
512 mRotation = i;
513 orientationChanged = true;
514 }
515 }
516 } else {
517 mConfidence[i] -= confidenceAmount;
518 if (mConfidence[i] < 0.0f) {
519 mConfidence[i] = 0.0f;
520 }
521 }
522 }
523 }
524
525 // Write final statistics about where we are in the orientation detection process.
526 if (log) {
527 Slog.v(TAG, "Result: rotation=" + mRotation
528 + ", confidence=["
529 + mConfidence[0] + ", "
530 + mConfidence[1] + ", "
531 + mConfidence[2] + ", "
532 + mConfidence[3] + "], timeDeltaMS=" + timeDeltaMS);
533 }
534
535 // Tell the listener.
536 if (orientationChanged) {
537 mOrientationListener.onOrientationChanged(mRotation);
538 }
539 }
540
541 /**
542 * Returns true if the tilt angle is acceptable for a proposed
543 * orientation transition.
544 */
545 private boolean isTiltAngleAcceptable(int proposedOrientation,
546 int tiltAngle) {
547 return tiltAngle >= TILT_TOLERANCE[proposedOrientation][0]
548 && tiltAngle <= TILT_TOLERANCE[proposedOrientation][1];
549 }
550
551 /**
552 * Returns true if the orientation angle is acceptable for a proposed
553 * orientation transition.
554 * This function takes into account the gap between adjacent orientations
555 * for hysteresis.
556 */
557 private boolean isOrientationAngleAcceptable(int proposedOrientation,
558 int orientationAngle) {
559 final int currentOrientation = mRotation;
560
561 // If there is no current rotation, then there is no gap.
562 if (currentOrientation != ROTATION_UNKNOWN) {
563 // If the proposed orientation is the same or is counter-clockwise adjacent,
564 // then we set a lower bound on the orientation angle.
565 // For example, if currentOrientation is ROTATION_0 and proposed is ROTATION_90,
566 // then we want to check orientationAngle > 45 + GAP / 2.
567 if (proposedOrientation == currentOrientation
568 || proposedOrientation == (currentOrientation + 1) % 4) {
569 int lowerBound = proposedOrientation * 90 - 45
570 + ADJACENT_ORIENTATION_ANGLE_GAP / 2;
571 if (proposedOrientation == 0) {
572 if (orientationAngle >= 315 && orientationAngle < lowerBound + 360) {
573 return false;
574 }
575 } else {
576 if (orientationAngle < lowerBound) {
577 return false;
578 }
579 }
580 }
581
582 // If the proposed orientation is the same or is clockwise adjacent,
583 // then we set an upper bound on the orientation angle.
584 // For example, if currentOrientation is ROTATION_0 and proposed is ROTATION_270,
585 // then we want to check orientationAngle < 315 - GAP / 2.
586 if (proposedOrientation == currentOrientation
587 || proposedOrientation == (currentOrientation + 3) % 4) {
588 int upperBound = proposedOrientation * 90 + 45
589 - ADJACENT_ORIENTATION_ANGLE_GAP / 2;
590 if (proposedOrientation == 0) {
591 if (orientationAngle <= 45 && orientationAngle > upperBound) {
592 return false;
593 }
594 } else {
595 if (orientationAngle > upperBound) {
596 return false;
597 }
598 }
599 }
600 }
601 return true;
602 }
603
604 /**
605 * Calculate an exponentially weighted confidence value in the range [0.0, 1.0].
606 * The further the value is from the target, the more the confidence trends to 0.
607 */
608 private static float confidence(float value, float target, float scale) {
609 return (float) Math.exp(-Math.abs(value - target) * scale);
610 }
611
612 /**
613 * Calculate a scale factor for the confidence weight exponent.
614 * The scale value is chosen such that confidence(value, target, scale) == 0.5
615 * whenever abs(value - target) == cutoffDelta.
616 */
617 private static float confidenceScaleFromDelta(float cutoffDelta) {
618 return (float) -Math.log(0.5) / cutoffDelta;
619 }
620 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621}