summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorflackr@chromium.org <flackr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-11 23:39:12 +0000
committerflackr@chromium.org <flackr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-11 23:39:12 +0000
commit504ec34cba59dd49ca83b70dae3a41b714add4da (patch)
tree668b4aced42b587135c108ada436849cf6b3897d
parent48e375ec3121a731dcf3198fbf2ed25c0b3c217b (diff)
downloadchromium_src-504ec34cba59dd49ca83b70dae3a41b714add4da.zip
chromium_src-504ec34cba59dd49ca83b70dae3a41b714add4da.tar.gz
chromium_src-504ec34cba59dd49ca83b70dae3a41b714add4da.tar.bz2
Merge 261692 "Auto rotate on lid rotation changes."
> Auto rotate on lid rotation changes. > > Based on patch to read the accelerometer https://codereview.chromium.org/200643005, this listens to accelerometer updates and rotates the screen. > > BUG=342907 > TEST=MaximizeModeControllerTest.AccelerometerRotation, MaximizeModeControllerTest.AccelerometerIgnoresLowAngles, MaximizeModeControllerTest.AccelerometerSticky, MaximizeModeControllerTest.RotationOnlyInMaximizeMode > TEST=Rotate device, ash should rotate to match device orientation. > > Review URL: https://codereview.chromium.org/196413017 TBR=flackr@chromium.org Review URL: https://codereview.chromium.org/228663010 git-svn-id: svn://svn.chromium.org/chrome/branches/1916/src@263409 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--ash/wm/maximize_mode/maximize_mode_controller.cc104
-rw-r--r--ash/wm/maximize_mode/maximize_mode_controller.h11
-rw-r--r--ash/wm/maximize_mode/maximize_mode_controller_unittest.cc124
3 files changed, 235 insertions, 4 deletions
diff --git a/ash/wm/maximize_mode/maximize_mode_controller.cc b/ash/wm/maximize_mode/maximize_mode_controller.cc
index 34fc1ff..d9e3668 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();
+
+ internal::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
diff --git a/ash/wm/maximize_mode/maximize_mode_controller.h b/ash/wm/maximize_mode/maximize_mode_controller.h
index f9919fa..0ef1b9f 100644
--- a/ash/wm/maximize_mode/maximize_mode_controller.h
+++ b/ash/wm/maximize_mode/maximize_mode_controller.h
@@ -12,7 +12,7 @@ namespace ash {
// MaximizeModeController listens to accelerometer events and automatically
// enters and exits maximize mode when the lid is opened beyond the triggering
-// angle.
+// angle and rotates the display to match the device when in maximize mode.
class MaximizeModeController : public AccelerometerObserver {
public:
MaximizeModeController();
@@ -21,6 +21,15 @@ class MaximizeModeController : public AccelerometerObserver {
virtual void OnAccelerometerUpdated(const gfx::Vector3dF& base,
const gfx::Vector3dF& lid) OVERRIDE;
private:
+ // Detect hinge rotation from |base| and |lid| accelerometers and
+ // automatically start / stop maximize mode.
+ void HandleHingeRotation(const gfx::Vector3dF& base,
+ const gfx::Vector3dF& lid);
+
+ // Detect screen rotation from |lid| accelerometer and automatically rotate
+ // screen.
+ void HandleScreenRotation(const gfx::Vector3dF& lid);
+
DISALLOW_COPY_AND_ASSIGN(MaximizeModeController);
};
diff --git a/ash/wm/maximize_mode/maximize_mode_controller_unittest.cc b/ash/wm/maximize_mode/maximize_mode_controller_unittest.cc
index ee3afc4..3237524 100644
--- a/ash/wm/maximize_mode/maximize_mode_controller_unittest.cc
+++ b/ash/wm/maximize_mode/maximize_mode_controller_unittest.cc
@@ -5,12 +5,20 @@
#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 "ash/test/ash_test_base.h"
+#include "ash/test/display_manager_test_api.h"
#include "ui/gfx/vector3d_f.h"
namespace ash {
+namespace {
+
+const float kDegreesToRadians = 3.14159265f / 180.0f;
+
+} // namespace
+
class MaximizeModeControllerTest : public test::AshTestBase {
public:
MaximizeModeControllerTest() {}
@@ -20,6 +28,11 @@ class MaximizeModeControllerTest : public test::AshTestBase {
test::AshTestBase::SetUp();
Shell::GetInstance()->accelerometer_controller()->RemoveObserver(
maximize_mode_controller());
+
+ // Set the first display to be the internal display for the accelerometer
+ // screen rotation tests.
+ test::DisplayManagerTestApi(Shell::GetInstance()->display_manager()).
+ SetFirstDisplayAsInternalDisplay();
}
virtual void TearDown() OVERRIDE {
@@ -41,6 +54,11 @@ class MaximizeModeControllerTest : public test::AshTestBase {
return Shell::GetInstance()->IsMaximizeModeWindowManagerEnabled();
}
+ gfx::Display::Rotation GetInternalDisplayRotation() const {
+ return Shell::GetInstance()->display_manager()->GetDisplayInfo(
+ gfx::Display::InternalDisplayId()).rotation();
+ }
+
private:
DISALLOW_COPY_AND_ASSIGN(MaximizeModeControllerTest);
};
@@ -108,4 +126,110 @@ TEST_F(MaximizeModeControllerTest, HingeAligned) {
EXPECT_TRUE(IsMaximizeModeStarted());
}
+// Tests that accelerometer readings in each of the screen angles will trigger
+// a rotation of the internal display.
+TEST_F(MaximizeModeControllerTest, DisplayRotation) {
+ // Trigger maximize mode by opening to 270.
+ TriggerAccelerometerUpdate(gfx::Vector3dF(0.0f, 0.0f, -1.0f),
+ gfx::Vector3dF(-1.0f, 0.0f, 0.0f));
+ ASSERT_TRUE(IsMaximizeModeStarted());
+
+ // Now test rotating in all directions.
+ TriggerAccelerometerUpdate(gfx::Vector3dF(0.0f, 1.0f, 0.0f),
+ gfx::Vector3dF(0.0f, 1.0f, 0.0f));
+ EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation());
+ TriggerAccelerometerUpdate(gfx::Vector3dF(1.0f, 0.0f, 0.0f),
+ gfx::Vector3dF(1.0f, 0.0f, 0.0f));
+ EXPECT_EQ(gfx::Display::ROTATE_180, GetInternalDisplayRotation());
+ TriggerAccelerometerUpdate(gfx::Vector3dF(0.0f, -1.0f, 0.0f),
+ gfx::Vector3dF(0.0f, -1.0f, 0.0f));
+ EXPECT_EQ(gfx::Display::ROTATE_270, GetInternalDisplayRotation());
+ TriggerAccelerometerUpdate(gfx::Vector3dF(-1.0f, 0.0f, 0.0f),
+ gfx::Vector3dF(-1.0f, 0.0f, 0.0f));
+ EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation());
+}
+
+// Tests that low angles are ignored by the accelerometer (i.e. when the device
+// is almost laying flat).
+TEST_F(MaximizeModeControllerTest, RotationIgnoresLowAngles) {
+ // Trigger maximize mode by opening to 270.
+ TriggerAccelerometerUpdate(gfx::Vector3dF(0.0f, 0.0f, -1.0f),
+ gfx::Vector3dF(-1.0f, 0.0f, 0.0f));
+ ASSERT_TRUE(IsMaximizeModeStarted());
+
+ TriggerAccelerometerUpdate(gfx::Vector3dF(-1.0f, 0.0f, -1.0f),
+ gfx::Vector3dF(-1.0f, 0.0f, -1.0f));
+ EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation());
+ TriggerAccelerometerUpdate(gfx::Vector3dF(0.0f, 0.2f, -1.0f),
+ gfx::Vector3dF(0.0f, 0.2f, -1.0f));
+ EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation());
+ TriggerAccelerometerUpdate(gfx::Vector3dF(0.2f, 0.0f, -1.0f),
+ gfx::Vector3dF(0.2f, 0.0f, -1.0f));
+ EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation());
+ TriggerAccelerometerUpdate(gfx::Vector3dF(0.0f, -0.2f, -1.0f),
+ gfx::Vector3dF(0.0f, -0.2f, -1.0f));
+ EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation());
+ TriggerAccelerometerUpdate(gfx::Vector3dF(-0.2f, 0.0f, -1.0f),
+ gfx::Vector3dF(-0.2f, 0.0f, -1.0f));
+ EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation());
+}
+
+// Tests that the display will stick to the current orientation beyond the
+// halfway point, preventing frequent updates back and forth.
+TEST_F(MaximizeModeControllerTest, RotationSticky) {
+ // Trigger maximize mode by opening to 270.
+ TriggerAccelerometerUpdate(gfx::Vector3dF(0.0f, 0.0f, -1.0f),
+ gfx::Vector3dF(-1.0f, 0.0f, 0.0f));
+ ASSERT_TRUE(IsMaximizeModeStarted());
+
+ gfx::Vector3dF gravity(-1.0f, 0.0f, 0.0f);
+ TriggerAccelerometerUpdate(gravity, gravity);
+ EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation());
+
+ // Turn past half-way point to next direction and rotation should remain
+ // the same.
+ float degrees = 50.0;
+ gravity.set_x(-cos(degrees * kDegreesToRadians));
+ gravity.set_y(sin(degrees * kDegreesToRadians));
+ TriggerAccelerometerUpdate(gravity, gravity);
+ EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation());
+
+ // Turn more and the screen should rotate.
+ degrees = 70.0;
+ gravity.set_x(-cos(degrees * kDegreesToRadians));
+ gravity.set_y(sin(degrees * kDegreesToRadians));
+ TriggerAccelerometerUpdate(gravity, gravity);
+ EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation());
+
+ // Turn back just beyond the half-way point and the new rotation should
+ // still be in effect.
+ degrees = 40.0;
+ gravity.set_x(-cos(degrees * kDegreesToRadians));
+ gravity.set_y(sin(degrees * kDegreesToRadians));
+ TriggerAccelerometerUpdate(gravity, gravity);
+ EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation());
+}
+
+// Tests that the screen only rotates when maximize mode is engaged, and will
+// return to the standard orientation on exiting maximize mode.
+TEST_F(MaximizeModeControllerTest, RotationOnlyInMaximizeMode) {
+ // Rotate on side with lid only open 90 degrees.
+ TriggerAccelerometerUpdate(gfx::Vector3dF(0.0f, 0.8f, 0.3f),
+ gfx::Vector3dF(-0.3f, 0.8f, 0.0f));
+ ASSERT_FALSE(IsMaximizeModeStarted());
+ EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation());
+
+ // Open lid, screen should now rotate to match orientation.
+ TriggerAccelerometerUpdate(gfx::Vector3dF(0.0f, 0.8f, -0.3f),
+ gfx::Vector3dF(-0.3f, 0.8f, 0.0f));
+ ASSERT_TRUE(IsMaximizeModeStarted());
+ EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation());
+
+ // Close lid back to 90, screen should rotate back.
+ TriggerAccelerometerUpdate(gfx::Vector3dF(0.0f, 0.8f, 0.3f),
+ gfx::Vector3dF(-0.3f, 0.8f, 0.0f));
+ ASSERT_FALSE(IsMaximizeModeStarted());
+ EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation());
+}
+
} // namespace ash