OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ash/wm/maximize_mode/maximize_mode_controller.h" | 5 #include "ash/wm/maximize_mode/maximize_mode_controller.h" |
6 | 6 |
7 #include "ash/accelerators/accelerator_controller.h" | 7 #include "ash/accelerators/accelerator_controller.h" |
8 #include "ash/accelerators/accelerator_table.h" | 8 #include "ash/accelerators/accelerator_table.h" |
9 #include "ash/accelerometer/accelerometer_controller.h" | 9 #include "ash/accelerometer/accelerometer_controller.h" |
10 #include "ash/ash_switches.h" | 10 #include "ash/ash_switches.h" |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
49 const float kMinStableAngle = 20.0f; | 49 const float kMinStableAngle = 20.0f; |
50 const float kMaxStableAngle = 340.0f; | 50 const float kMaxStableAngle = 340.0f; |
51 | 51 |
52 // The time duration to consider the lid to be recently opened. | 52 // The time duration to consider the lid to be recently opened. |
53 // This is used to prevent entering maximize mode if an erroneous accelerometer | 53 // This is used to prevent entering maximize mode if an erroneous accelerometer |
54 // reading makes the lid appear to be fully open when the user is opening the | 54 // reading makes the lid appear to be fully open when the user is opening the |
55 // lid from a closed position. | 55 // lid from a closed position. |
56 const base::TimeDelta kLidRecentlyOpenedDuration = | 56 const base::TimeDelta kLidRecentlyOpenedDuration = |
57 base::TimeDelta::FromSeconds(2); | 57 base::TimeDelta::FromSeconds(2); |
58 | 58 |
| 59 // The mean acceleration due to gravity on Earth in m/s^2. |
| 60 const float kMeanGravity = 9.80665f; |
| 61 |
59 // When the device approaches vertical orientation (i.e. portrait orientation) | 62 // When the device approaches vertical orientation (i.e. portrait orientation) |
60 // the accelerometers for the base and lid approach the same values (i.e. | 63 // the accelerometers for the base and lid approach the same values (i.e. |
61 // gravity pointing in the direction of the hinge). When this happens we cannot | 64 // gravity pointing in the direction of the hinge). When this happens we cannot |
62 // compute the hinge angle reliably and must turn ignore accelerometer readings. | 65 // compute the hinge angle reliably and must turn ignore accelerometer readings. |
63 // This is the minimum acceleration perpendicular to the hinge under which to | 66 // This is the minimum acceleration perpendicular to the hinge under which to |
64 // detect hinge angle. | 67 // detect hinge angle in m/s^2. |
65 const float kHingeAngleDetectionThreshold = 0.25f; | 68 const float kHingeAngleDetectionThreshold = 2.5f; |
66 | 69 |
67 // The maximum deviation from the acceleration expected due to gravity under | 70 // The maximum deviation from the acceleration expected due to gravity under |
68 // which to detect hinge angle and screen rotation. | 71 // which to detect hinge angle and screen rotation in m/s^2 |
69 const float kDeviationFromGravityThreshold = 0.1f; | 72 const float kDeviationFromGravityThreshold = 1.0f; |
70 | 73 |
71 // The maximum deviation between the magnitude of the two accelerometers under | 74 // The maximum deviation between the magnitude of the two accelerometers under |
72 // which to detect hinge angle and screen rotation. These accelerometers are | 75 // which to detect hinge angle and screen rotation in m/s^2. These |
73 // attached to the same physical device and so should be under the same | 76 // accelerometers are attached to the same physical device and so should be |
74 // acceleration. | 77 // under the same acceleration. |
75 const float kNoisyMagnitudeDeviation = 0.1f; | 78 const float kNoisyMagnitudeDeviation = 1.0f; |
76 | 79 |
77 // The angle which the screen has to be rotated past before the display will | 80 // The angle which the screen has to be rotated past before the display will |
78 // rotate to match it (i.e. 45.0f is no stickiness). | 81 // rotate to match it (i.e. 45.0f is no stickiness). |
79 const float kDisplayRotationStickyAngleDegrees = 60.0f; | 82 const float kDisplayRotationStickyAngleDegrees = 60.0f; |
80 | 83 |
81 // The minimum acceleration in a direction required to trigger screen rotation. | 84 // The minimum acceleration in m/s^2 in a direction required to trigger screen |
82 // This prevents rapid toggling of rotation when the device is near flat and | 85 // rotation. This prevents rapid toggling of rotation when the device is near |
83 // there is very little screen aligned force on it. The value is effectively the | 86 // flat and there is very little screen aligned force on it. The value is |
84 // sine of the rise angle required, with the current value requiring at least a | 87 // effectively the sine of the rise angle required times the acceleration due |
85 // 25 degree rise. | 88 // to gravity, with the current value requiring at least a 25 degree rise. |
86 const float kMinimumAccelerationScreenRotation = 0.42f; | 89 const float kMinimumAccelerationScreenRotation = 4.2f; |
87 | 90 |
88 const float kRadiansToDegrees = 180.0f / 3.14159265f; | 91 const float kRadiansToDegrees = 180.0f / 3.14159265f; |
89 | 92 |
90 // Returns the angle between |base| and |other| in degrees. | 93 // Returns the angle between |base| and |other| in degrees. |
91 float AngleBetweenVectorsInDegrees(const gfx::Vector3dF& base, | 94 float AngleBetweenVectorsInDegrees(const gfx::Vector3dF& base, |
92 const gfx::Vector3dF& other) { | 95 const gfx::Vector3dF& other) { |
93 return acos(gfx::DotProduct(base, other) / | 96 return acos(gfx::DotProduct(base, other) / |
94 base.Length() / other.Length()) * kRadiansToDegrees; | 97 base.Length() / other.Length()) * kRadiansToDegrees; |
95 } | 98 } |
96 | 99 |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
229 if (IsMaximizeModeWindowManagerEnabled()) | 232 if (IsMaximizeModeWindowManagerEnabled()) |
230 maximize_mode_window_manager_->AddWindow(window); | 233 maximize_mode_window_manager_->AddWindow(window); |
231 } | 234 } |
232 | 235 |
233 void MaximizeModeController::Shutdown() { | 236 void MaximizeModeController::Shutdown() { |
234 shutting_down_ = true; | 237 shutting_down_ = true; |
235 LeaveMaximizeMode(); | 238 LeaveMaximizeMode(); |
236 } | 239 } |
237 | 240 |
238 void MaximizeModeController::OnAccelerometerUpdated( | 241 void MaximizeModeController::OnAccelerometerUpdated( |
239 const gfx::Vector3dF& base, | 242 const ui::AccelerometerUpdate& update) { |
240 const gfx::Vector3dF& lid) { | |
241 bool first_accelerometer_update = !have_seen_accelerometer_data_; | 243 bool first_accelerometer_update = !have_seen_accelerometer_data_; |
242 have_seen_accelerometer_data_ = true; | 244 have_seen_accelerometer_data_ = true; |
243 | 245 |
244 // Ignore the reading if it appears unstable. The reading is considered | 246 // Ignore the reading if it appears unstable. The reading is considered |
245 // unstable if it deviates too much from gravity and/or the magnitude of the | 247 // unstable if it deviates too much from gravity and/or the magnitude of the |
246 // reading from the lid differs too much from the reading from the base. | 248 // reading from the lid differs too much from the reading from the base. |
247 float base_magnitude = base.Length(); | 249 float base_magnitude = |
248 float lid_magnitude = lid.Length(); | 250 update.has(ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD) ? |
249 if (std::abs(base_magnitude - lid_magnitude) > kNoisyMagnitudeDeviation || | 251 update.get(ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD).Length() : |
250 std::abs(base_magnitude - 1.0f) > kDeviationFromGravityThreshold || | 252 0.0f; |
251 std::abs(lid_magnitude - 1.0f) > kDeviationFromGravityThreshold) { | 253 float lid_magnitude = update.has(ui::ACCELEROMETER_SOURCE_SCREEN) ? |
252 return; | 254 update.get(ui::ACCELEROMETER_SOURCE_SCREEN).Length() : 0.0f; |
| 255 bool lid_stable = update.has(ui::ACCELEROMETER_SOURCE_SCREEN) && |
| 256 std::abs(lid_magnitude - kMeanGravity) <= kDeviationFromGravityThreshold; |
| 257 bool base_angle_stable = lid_stable && |
| 258 update.has(ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD) && |
| 259 std::abs(base_magnitude - lid_magnitude) <= kNoisyMagnitudeDeviation && |
| 260 std::abs(base_magnitude - kMeanGravity) <= kDeviationFromGravityThreshold; |
| 261 |
| 262 if (base_angle_stable) { |
| 263 // Responding to the hinge rotation can change the maximize mode state which |
| 264 // affects screen rotation, so we handle hinge rotation first. |
| 265 HandleHingeRotation( |
| 266 update.get(ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD), |
| 267 update.get(ui::ACCELEROMETER_SOURCE_SCREEN)); |
253 } | 268 } |
254 | 269 if (lid_stable) |
255 // Responding to the hinge rotation can change the maximize mode state which | 270 HandleScreenRotation(update.get(ui::ACCELEROMETER_SOURCE_SCREEN)); |
256 // affects screen rotation, so we handle hinge rotation first. | |
257 HandleHingeRotation(base, lid); | |
258 HandleScreenRotation(lid); | |
259 | 271 |
260 if (first_accelerometer_update) { | 272 if (first_accelerometer_update) { |
261 // On the first accelerometer update we will know if we have entered | 273 // On the first accelerometer update we will know if we have entered |
262 // maximize mode or not. Update the preferences to reflect the current | 274 // maximize mode or not. Update the preferences to reflect the current |
263 // state. | 275 // state. |
264 Shell::GetInstance()->display_manager()-> | 276 Shell::GetInstance()->display_manager()-> |
265 RegisterDisplayRotationProperties(rotation_locked_, current_rotation_); | 277 RegisterDisplayRotationProperties(rotation_locked_, current_rotation_); |
266 } | 278 } |
267 } | 279 } |
268 | 280 |
(...skipping 27 matching lines...) Expand all Loading... |
296 } | 308 } |
297 | 309 |
298 void MaximizeModeController::SuspendDone( | 310 void MaximizeModeController::SuspendDone( |
299 const base::TimeDelta& sleep_duration) { | 311 const base::TimeDelta& sleep_duration) { |
300 last_touchview_transition_time_ = base::Time::Now(); | 312 last_touchview_transition_time_ = base::Time::Now(); |
301 } | 313 } |
302 #endif // OS_CHROMEOS | 314 #endif // OS_CHROMEOS |
303 | 315 |
304 void MaximizeModeController::HandleHingeRotation(const gfx::Vector3dF& base, | 316 void MaximizeModeController::HandleHingeRotation(const gfx::Vector3dF& base, |
305 const gfx::Vector3dF& lid) { | 317 const gfx::Vector3dF& lid) { |
306 static const gfx::Vector3dF hinge_vector(0.0f, 1.0f, 0.0f); | 318 static const gfx::Vector3dF hinge_vector(1.0f, 0.0f, 0.0f); |
307 bool maximize_mode_engaged = IsMaximizeModeWindowManagerEnabled(); | 319 bool maximize_mode_engaged = IsMaximizeModeWindowManagerEnabled(); |
308 // Ignore the component of acceleration parallel to the hinge for the purposes | 320 // Ignore the component of acceleration parallel to the hinge for the purposes |
309 // of hinge angle calculation. | 321 // of hinge angle calculation. |
310 gfx::Vector3dF base_flattened(base); | 322 gfx::Vector3dF base_flattened(base); |
311 gfx::Vector3dF lid_flattened(lid); | 323 gfx::Vector3dF lid_flattened(lid); |
312 base_flattened.set_y(0.0f); | 324 base_flattened.set_x(0.0f); |
313 lid_flattened.set_y(0.0f); | 325 lid_flattened.set_x(0.0f); |
314 | 326 |
315 // As the hinge approaches a vertical angle, the base and lid accelerometers | 327 // As the hinge approaches a vertical angle, the base and lid accelerometers |
316 // approach the same values making any angle calculations highly inaccurate. | 328 // approach the same values making any angle calculations highly inaccurate. |
317 // Bail out early when it is too close. | 329 // Bail out early when it is too close. |
318 if (base_flattened.Length() < kHingeAngleDetectionThreshold || | 330 if (base_flattened.Length() < kHingeAngleDetectionThreshold || |
319 lid_flattened.Length() < kHingeAngleDetectionThreshold) { | 331 lid_flattened.Length() < kHingeAngleDetectionThreshold) { |
320 return; | 332 return; |
321 } | 333 } |
322 | 334 |
323 // Compute the angle between the base and the lid. | 335 // Compute the angle between the base and the lid. |
324 float angle = ClockwiseAngleBetweenVectorsInDegrees(base_flattened, | 336 float lid_angle = 180.0f - ClockwiseAngleBetweenVectorsInDegrees( |
325 lid_flattened, hinge_vector); | 337 base_flattened, lid_flattened, hinge_vector); |
| 338 if (lid_angle < 0.0f) |
| 339 lid_angle += 360.0f; |
326 | 340 |
327 bool is_angle_stable = angle > kMinStableAngle && angle < kMaxStableAngle; | 341 bool is_angle_stable = lid_angle >= kMinStableAngle && |
| 342 lid_angle <= kMaxStableAngle; |
328 | 343 |
329 // Clear the last_lid_open_time_ for a stable reading so that there is less | 344 // Clear the last_lid_open_time_ for a stable reading so that there is less |
330 // chance of a delay if the lid is moved from the close state to the fully | 345 // chance of a delay if the lid is moved from the close state to the fully |
331 // open state very quickly. | 346 // open state very quickly. |
332 if (is_angle_stable) | 347 if (is_angle_stable) |
333 last_lid_open_time_ = base::TimeTicks(); | 348 last_lid_open_time_ = base::TimeTicks(); |
334 | 349 |
335 // Toggle maximize mode on or off when corresponding thresholds are passed. | 350 // Toggle maximize mode on or off when corresponding thresholds are passed. |
336 // TODO(flackr): Make MaximizeModeController own the MaximizeModeWindowManager | 351 // TODO(flackr): Make MaximizeModeController own the MaximizeModeWindowManager |
337 // such that observations of state changes occur after the change and shell | 352 // such that observations of state changes occur after the change and shell |
338 // has fewer states to track. | 353 // has fewer states to track. |
339 if (maximize_mode_engaged && is_angle_stable && | 354 if (maximize_mode_engaged && is_angle_stable && |
340 angle < kExitMaximizeModeAngle) { | 355 lid_angle <= kExitMaximizeModeAngle) { |
341 LeaveMaximizeMode(); | 356 LeaveMaximizeMode(); |
342 } else if (!lid_is_closed_ && !maximize_mode_engaged && | 357 } else if (!lid_is_closed_ && !maximize_mode_engaged && |
343 angle > kEnterMaximizeModeAngle && | 358 lid_angle >= kEnterMaximizeModeAngle && |
344 (is_angle_stable || !WasLidOpenedRecently())) { | 359 (is_angle_stable || !WasLidOpenedRecently())) { |
345 EnterMaximizeMode(); | 360 EnterMaximizeMode(); |
346 } | 361 } |
347 } | 362 } |
348 | 363 |
349 void MaximizeModeController::HandleScreenRotation(const gfx::Vector3dF& lid) { | 364 void MaximizeModeController::HandleScreenRotation(const gfx::Vector3dF& lid) { |
350 bool maximize_mode_engaged = IsMaximizeModeWindowManagerEnabled(); | 365 bool maximize_mode_engaged = IsMaximizeModeWindowManagerEnabled(); |
351 | 366 |
352 // TODO(jonross): track the updated rotation angle even when locked. So that | 367 // TODO(jonross): track the updated rotation angle even when locked. So that |
353 // when rotation lock is removed the accelerometer rotation can be applied | 368 // when rotation lock is removed the accelerometer rotation can be applied |
(...skipping 11 matching lines...) Expand all Loading... |
365 gfx::Vector3dF lid_flattened(lid.x(), lid.y(), 0.0f); | 380 gfx::Vector3dF lid_flattened(lid.x(), lid.y(), 0.0f); |
366 float lid_flattened_length = lid_flattened.Length(); | 381 float lid_flattened_length = lid_flattened.Length(); |
367 // When the lid is close to being flat, don't change rotation as it is too | 382 // When the lid is close to being flat, don't change rotation as it is too |
368 // sensitive to slight movements. | 383 // sensitive to slight movements. |
369 if (lid_flattened_length < kMinimumAccelerationScreenRotation) | 384 if (lid_flattened_length < kMinimumAccelerationScreenRotation) |
370 return; | 385 return; |
371 | 386 |
372 // The reference vector is the angle of gravity when the device is rotated | 387 // The reference vector is the angle of gravity when the device is rotated |
373 // clockwise by 45 degrees. Computing the angle between this vector and | 388 // clockwise by 45 degrees. Computing the angle between this vector and |
374 // gravity we can easily determine the expected display rotation. | 389 // gravity we can easily determine the expected display rotation. |
375 static gfx::Vector3dF rotation_reference(-1.0f, 1.0f, 0.0f); | 390 static const gfx::Vector3dF rotation_reference(-1.0f, -1.0f, 0.0f); |
376 | 391 |
377 // Set the down vector to match the expected direction of gravity given the | 392 // Set the down vector to match the expected direction of gravity given the |
378 // last configured rotation. This is used to enforce a stickiness that the | 393 // last configured rotation. This is used to enforce a stickiness that the |
379 // user must overcome to rotate the display and prevents frequent rotations | 394 // user must overcome to rotate the display and prevents frequent rotations |
380 // when holding the device near 45 degrees. | 395 // when holding the device near 45 degrees. |
381 gfx::Vector3dF down(0.0f, 0.0f, 0.0f); | 396 gfx::Vector3dF down(0.0f, 0.0f, 0.0f); |
382 if (current_rotation == gfx::Display::ROTATE_0) | 397 if (current_rotation == gfx::Display::ROTATE_0) |
| 398 down.set_y(-1.0f); |
| 399 else if (current_rotation == gfx::Display::ROTATE_90) |
383 down.set_x(-1.0f); | 400 down.set_x(-1.0f); |
384 else if (current_rotation == gfx::Display::ROTATE_90) | 401 else if (current_rotation == gfx::Display::ROTATE_180) |
385 down.set_y(1.0f); | 402 down.set_y(1.0f); |
386 else if (current_rotation == gfx::Display::ROTATE_180) | 403 else |
387 down.set_x(1.0f); | 404 down.set_x(1.0f); |
388 else | |
389 down.set_y(-1.0f); | |
390 | 405 |
391 // Don't rotate if the screen has not passed the threshold. | 406 // Don't rotate if the screen has not passed the threshold. |
392 if (AngleBetweenVectorsInDegrees(down, lid_flattened) < | 407 if (AngleBetweenVectorsInDegrees(down, lid_flattened) < |
393 kDisplayRotationStickyAngleDegrees) { | 408 kDisplayRotationStickyAngleDegrees) { |
394 return; | 409 return; |
395 } | 410 } |
396 | 411 |
397 float angle = ClockwiseAngleBetweenVectorsInDegrees(rotation_reference, | 412 float angle = ClockwiseAngleBetweenVectorsInDegrees(rotation_reference, |
398 lid_flattened, gfx::Vector3dF(0.0f, 0.0f, -1.0f)); | 413 lid_flattened, gfx::Vector3dF(0.0f, 0.0f, -1.0f)); |
399 | 414 |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
521 return elapsed_time <= kLidRecentlyOpenedDuration; | 536 return elapsed_time <= kLidRecentlyOpenedDuration; |
522 } | 537 } |
523 | 538 |
524 void MaximizeModeController::SetTickClockForTest( | 539 void MaximizeModeController::SetTickClockForTest( |
525 scoped_ptr<base::TickClock> tick_clock) { | 540 scoped_ptr<base::TickClock> tick_clock) { |
526 DCHECK(tick_clock_); | 541 DCHECK(tick_clock_); |
527 tick_clock_ = tick_clock.Pass(); | 542 tick_clock_ = tick_clock.Pass(); |
528 } | 543 } |
529 | 544 |
530 } // namespace ash | 545 } // namespace ash |
OLD | NEW |