Index: ash/wm/maximize_mode/maximize_mode_controller.cc |
diff --git a/ash/wm/maximize_mode/maximize_mode_controller.cc b/ash/wm/maximize_mode/maximize_mode_controller.cc |
index 34fc1ff273f85d13644374ded94fa94b70a32303..7fec42064d7b923798fb3c180d1ccd67484ed159 100644 |
--- a/ash/wm/maximize_mode/maximize_mode_controller.cc |
+++ b/ash/wm/maximize_mode/maximize_mode_controller.cc |
@@ -5,6 +5,7 @@ |
#include "ash/wm/maximize_mode/maximize_mode_controller.h" |
#include "ash/accelerometer/accelerometer_controller.h" |
+#include "ash/display/display_manager.h" |
#include "ash/shell.h" |
#include "ui/gfx/vector3d_f.h" |
@@ -33,6 +34,15 @@ const float kFullyOpenAngleErrorTolerance = 10.0f; |
// angle. |
const float kHingeAxisAlignedThreshold = 15.0f; |
+// The angle which the screen has to be rotated past before the display will |
+// rotate to match it (i.e. 45.0f is no stickiness). |
+const float kDisplayRotationStickyAngleDegrees = 60.0f; |
+ |
+// The minimum acceleration in a direction required to trigger screen rotation. |
+// This prevents rapid toggling of rotation when the device is near flat and |
+// there is very little screen aligned force on it. |
+const float kMinimumAccelerationScreenRotation = 0.3f; |
+ |
const float kRadiansToDegrees = 180.0f / 3.14159265f; |
// Returns the angle between |base| and |other| in degrees. |
@@ -71,15 +81,26 @@ MaximizeModeController::~MaximizeModeController() { |
void MaximizeModeController::OnAccelerometerUpdated( |
const gfx::Vector3dF& base, |
const gfx::Vector3dF& lid) { |
+ // Responding to the hinge rotation can change the maximize mode state which |
+ // affects screen rotation, so we handle hinge rotation first. |
+ HandleHingeRotation(base, lid); |
+ HandleScreenRotation(lid); |
+} |
+ |
+void MaximizeModeController::HandleHingeRotation(const gfx::Vector3dF& base, |
+ const gfx::Vector3dF& lid) { |
static const gfx::Vector3dF hinge_vector(0.0f, 1.0f, 0.0f); |
+ bool maximize_mode_engaged = |
+ Shell::GetInstance()->IsMaximizeModeWindowManagerEnabled(); |
// As the hinge approaches a vertical angle, the base and lid accelerometers |
// approach the same values making any angle calculations highly inaccurate. |
// Bail out early when it is too close. |
float hinge_angle = AngleBetweenVectorsInDegrees(base, hinge_vector); |
if (hinge_angle < kHingeAxisAlignedThreshold || |
- hinge_angle > 180.0f - kHingeAxisAlignedThreshold) |
+ hinge_angle > 180.0f - kHingeAxisAlignedThreshold) { |
return; |
+ } |
// Compute the angle between the base and the lid. |
float angle = ClockwiseAngleBetweenVectorsInDegrees(base, lid, hinge_vector); |
@@ -88,8 +109,6 @@ void MaximizeModeController::OnAccelerometerUpdated( |
// TODO(flackr): Make MaximizeModeController own the MaximizeModeWindowManager |
// such that observations of state changes occur after the change and shell |
// has fewer states to track. |
- bool maximize_mode_engaged = |
- Shell::GetInstance()->IsMaximizeModeWindowManagerEnabled(); |
if (maximize_mode_engaged && |
angle > kFullyOpenAngleErrorTolerance && |
angle < kExitMaximizeModeAngle) { |
@@ -100,4 +119,83 @@ void MaximizeModeController::OnAccelerometerUpdated( |
} |
} |
+void MaximizeModeController::HandleScreenRotation(const gfx::Vector3dF& lid) { |
+ bool maximize_mode_engaged = |
+ Shell::GetInstance()->IsMaximizeModeWindowManagerEnabled(); |
+ |
+ DisplayManager* display_manager = |
+ Shell::GetInstance()->display_manager(); |
+ gfx::Display::Rotation current_rotation = display_manager->GetDisplayInfo( |
+ gfx::Display::InternalDisplayId()).rotation(); |
+ |
+ // If maximize mode is not engaged, ensure the screen is not rotated and |
+ // do not rotate to match the current device orientation. |
+ if (!maximize_mode_engaged) { |
+ if (current_rotation != gfx::Display::ROTATE_0) { |
+ // TODO(flackr): Currently this will prevent setting a manual rotation on |
+ // the screen of a device with an accelerometer, this should only set it |
+ // back to ROTATE_0 if it was last set by the accelerometer. |
+ // Also, SetDisplayRotation will save the setting to the local store, |
+ // this should be stored in a way that we can distinguish what the |
+ // rotation was set by. |
+ display_manager->SetDisplayRotation(gfx::Display::InternalDisplayId(), |
+ gfx::Display::ROTATE_0); |
+ } |
+ return; |
+ } |
+ |
+ // After determining maximize mode state, determine if the screen should |
+ // be rotated. |
+ gfx::Vector3dF lid_flattened(lid.x(), lid.y(), 0.0f); |
+ float lid_flattened_length = lid_flattened.Length(); |
+ // When the lid is close to being flat, don't change rotation as it is too |
+ // sensitive to slight movements. |
+ if (lid_flattened_length < kMinimumAccelerationScreenRotation) |
+ return; |
+ |
+ // The reference vector is the angle of gravity when the device is rotated |
+ // clockwise by 45 degrees. Computing the angle between this vector and |
+ // gravity we can easily determine the expected display rotation. |
+ static gfx::Vector3dF rotation_reference(-1.0f, 1.0f, 0.0f); |
+ |
+ // Set the down vector to match the expected direction of gravity given the |
+ // last configured rotation. This is used to enforce a stickiness that the |
+ // user must overcome to rotate the display and prevents frequent rotations |
+ // when holding the device near 45 degrees. |
+ gfx::Vector3dF down(0.0f, 0.0f, 0.0f); |
+ if (current_rotation == gfx::Display::ROTATE_0) |
+ down.set_x(-1.0f); |
+ else if (current_rotation == gfx::Display::ROTATE_90) |
+ down.set_y(1.0f); |
+ else if (current_rotation == gfx::Display::ROTATE_180) |
+ down.set_x(1.0f); |
+ else |
+ down.set_y(-1.0f); |
+ |
+ // Don't rotate if the screen has not passed the threshold. |
+ if (AngleBetweenVectorsInDegrees(down, lid_flattened) < |
+ kDisplayRotationStickyAngleDegrees) { |
+ return; |
+ } |
+ |
+ float angle = ClockwiseAngleBetweenVectorsInDegrees(rotation_reference, |
+ lid_flattened, gfx::Vector3dF(0.0f, 0.0f, -1.0f)); |
+ |
+ gfx::Display::Rotation new_rotation = gfx::Display::ROTATE_90; |
+ if (angle < 90.0f) |
+ new_rotation = gfx::Display::ROTATE_0; |
+ else if (angle < 180.0f) |
+ new_rotation = gfx::Display::ROTATE_270; |
+ else if (angle < 270.0f) |
+ new_rotation = gfx::Display::ROTATE_180; |
+ |
+ // When exiting maximize mode return rotation to 0. When entering, rotate to |
+ // match screen orientation. |
+ if (new_rotation == gfx::Display::ROTATE_0 || |
+ maximize_mode_engaged) { |
+ display_manager->SetDisplayRotation(gfx::Display::InternalDisplayId(), |
+ new_rotation); |
+ } |
+} |
+ |
} // namespace ash |