blob: c1009dbc9caf4c6957ff89c606f9ed5ec3aff02e [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/display/screen_orientation_controller.h"
#include <memory>
#include <vector>
#include "ash/accelerometer/accelerometer_reader.h"
#include "ash/accelerometer/accelerometer_types.h"
#include "ash/display/screen_orientation_controller.h"
#include "ash/display/screen_orientation_controller_test_api.h"
#include "ash/public/cpp/app_types.h"
#include "ash/public/cpp/ash_switches.h"
#include "ash/shell.h"
#include "ash/system/screen_layout_observer.h"
#include "ash/test/ash_test_base.h"
#include "ash/test/ash_test_helper.h"
#include "ash/test_shell_delegate.h"
#include "ash/window_factory.h"
#include "ash/wm/tablet_mode/tablet_mode_controller.h"
#include "ash/wm/window_state.h"
#include "base/command_line.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/aura/window.h"
#include "ui/compositor/layer_type.h"
#include "ui/display/display.h"
#include "ui/display/display_switches.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/display/test/display_manager_test_api.h"
#include "ui/message_center/message_center.h"
#include "ui/wm/public/activation_client.h"
namespace ash {
namespace {
const float kDegreesToRadians = 3.1415926f / 180.0f;
const float kMeanGravity = -9.8066f;
display::ManagedDisplayInfo CreateDisplayInfo(int64_t id,
const gfx::Rect& bounds) {
display::ManagedDisplayInfo info(id, "dummy", false);
info.SetBounds(bounds);
return info;
}
void EnableTabletMode(bool enable) {
Shell::Get()->tablet_mode_controller()->EnableTabletModeWindowManager(enable);
}
bool RotationLocked() {
return Shell::Get()->screen_orientation_controller()->rotation_locked();
}
bool UserRotationLocked() {
return Shell::Get()->screen_orientation_controller()->user_rotation_locked();
}
void SetDisplayRotationById(int64_t display_id,
display::Display::Rotation rotation) {
Shell::Get()->display_manager()->SetDisplayRotation(
display_id, rotation, display::Display::RotationSource::USER);
}
void SetInternalDisplayRotation(display::Display::Rotation rotation) {
SetDisplayRotationById(display::Display::InternalDisplayId(), rotation);
}
void TriggerLidUpdate(const gfx::Vector3dF& lid) {
scoped_refptr<AccelerometerUpdate> update(new AccelerometerUpdate());
update->Set(ACCELEROMETER_SOURCE_SCREEN, lid.x(), lid.y(), lid.z());
Shell::Get()->screen_orientation_controller()->OnAccelerometerUpdated(update);
}
// Shows |child| and adds |child| to |parent|.
void AddWindowAndShow(aura::Window* child, aura::Window* parent) {
child->Show();
parent->AddChild(child);
}
// Adds |child| to |parent| and activates |parent|.
void AddWindowAndActivateParent(aura::Window* child, aura::Window* parent) {
AddWindowAndShow(child, parent);
Shell::Get()->activation_client()->ActivateWindow(parent);
}
void Lock(aura::Window* window, OrientationLockType orientation_lock) {
Shell::Get()->screen_orientation_controller()->LockOrientationForWindow(
window, orientation_lock);
}
void Unlock(aura::Window* window) {
Shell::Get()->screen_orientation_controller()->UnlockOrientationForWindow(
window);
}
// Creates a window of type WINDOW_TYPE_CONTROL.
std::unique_ptr<aura::Window> CreateControlWindow() {
std::unique_ptr<aura::Window> window = window_factory::NewWindow(
nullptr, aura::client::WindowType::WINDOW_TYPE_CONTROL);
window->Init(ui::LAYER_NOT_DRAWN);
window->set_owned_by_parent(false);
return window;
}
} // namespace
class ScreenOrientationControllerTest : public AshTestBase {
public:
ScreenOrientationControllerTest() = default;
~ScreenOrientationControllerTest() override = default;
// AshTestBase:
void SetUp() override {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
::switches::kUseFirstDisplayAsInternal);
AshTestBase::SetUp();
}
protected:
aura::Window* CreateAppWindowInShellWithId(int id) {
aura::Window* window = CreateTestWindowInShellWithId(id);
window->SetProperty(aura::client::kAppType,
static_cast<int>(AppType::CHROME_APP));
return window;
}
void SetSystemRotationLocked(bool rotation_locked) {
ScreenOrientationControllerTestApi(
Shell::Get()->screen_orientation_controller())
.SetRotationLocked(rotation_locked);
}
void SetUserRotationLocked(bool rotation_locked) {
if (Shell::Get()->screen_orientation_controller()->user_rotation_locked() !=
rotation_locked) {
Shell::Get()->screen_orientation_controller()->ToggleUserRotationLock();
}
}
OrientationLockType UserLockedOrientation() const {
ScreenOrientationControllerTestApi test_api(
Shell::Get()->screen_orientation_controller());
return test_api.UserLockedOrientation();
}
private:
DISALLOW_COPY_AND_ASSIGN(ScreenOrientationControllerTest);
};
// Tests that a Window can lock rotation.
TEST_F(ScreenOrientationControllerTest, LockOrientation) {
EnableTabletMode(true);
std::unique_ptr<aura::Window> child_window = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
ASSERT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
ASSERT_FALSE(RotationLocked());
AddWindowAndActivateParent(child_window.get(), focus_window.get());
Lock(child_window.get(), OrientationLockType::kLandscape);
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
EXPECT_TRUE(RotationLocked());
}
// Tests that a Window can unlock rotation.
TEST_F(ScreenOrientationControllerTest, Unlock) {
EnableTabletMode(true);
std::unique_ptr<aura::Window> child_window = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
ASSERT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
ASSERT_FALSE(RotationLocked());
AddWindowAndActivateParent(child_window.get(), focus_window.get());
Lock(child_window.get(), OrientationLockType::kLandscape);
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
EXPECT_TRUE(RotationLocked());
Unlock(child_window.get());
EXPECT_FALSE(RotationLocked());
}
// Tests that a Window is able to change the orientation of the display after
// having locked rotation.
TEST_F(ScreenOrientationControllerTest, OrientationChanges) {
EnableTabletMode(true);
std::unique_ptr<aura::Window> child_window = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
ASSERT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
ASSERT_FALSE(RotationLocked());
AddWindowAndActivateParent(child_window.get(), focus_window.get());
Lock(child_window.get(), OrientationLockType::kPortrait);
EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
EXPECT_TRUE(RotationLocked());
Lock(child_window.get(), OrientationLockType::kLandscape);
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
}
// Tests that orientation can only be set by the first Window that has set a
// rotation lock.
TEST_F(ScreenOrientationControllerTest, SecondContentCannotChangeOrientation) {
EnableTabletMode(true);
std::unique_ptr<aura::Window> child_window1 = CreateControlWindow();
std::unique_ptr<aura::Window> child_window2 = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window1(CreateAppWindowInShellWithId(0));
std::unique_ptr<aura::Window> focus_window2(CreateAppWindowInShellWithId(1));
AddWindowAndActivateParent(child_window1.get(), focus_window1.get());
AddWindowAndShow(child_window2.get(), focus_window2.get());
Lock(child_window1.get(), OrientationLockType::kLandscape);
Lock(child_window2.get(), OrientationLockType::kPortrait);
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
}
// Tests that only the Window that set a rotation lock can perform an unlock.
TEST_F(ScreenOrientationControllerTest, SecondContentCannotUnlock) {
EnableTabletMode(true);
std::unique_ptr<aura::Window> child_window1 = CreateControlWindow();
std::unique_ptr<aura::Window> child_window2 = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window1(CreateAppWindowInShellWithId(0));
std::unique_ptr<aura::Window> focus_window2(CreateAppWindowInShellWithId(1));
AddWindowAndActivateParent(child_window1.get(), focus_window1.get());
AddWindowAndShow(child_window2.get(), focus_window2.get());
Lock(child_window1.get(), OrientationLockType::kLandscape);
Unlock(child_window2.get());
EXPECT_TRUE(RotationLocked());
}
// Tests that a rotation lock is applied only while the Window are a part of the
// active window.
TEST_F(ScreenOrientationControllerTest, ActiveWindowChangesUpdateLock) {
EnableTabletMode(true);
std::unique_ptr<aura::Window> child_window = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window1(CreateAppWindowInShellWithId(0));
std::unique_ptr<aura::Window> focus_window2(CreateAppWindowInShellWithId(1));
AddWindowAndActivateParent(child_window.get(), focus_window1.get());
Lock(child_window.get(), OrientationLockType::kLandscape);
ASSERT_TRUE(RotationLocked());
::wm::ActivationClient* activation_client = Shell::Get()->activation_client();
activation_client->ActivateWindow(focus_window2.get());
EXPECT_FALSE(RotationLocked());
activation_client->ActivateWindow(focus_window1.get());
EXPECT_TRUE(RotationLocked());
}
// Tests that switching between windows with different orientation locks change
// the orientation.
TEST_F(ScreenOrientationControllerTest, ActiveWindowChangesUpdateOrientation) {
EnableTabletMode(true);
std::unique_ptr<aura::Window> child_window1 = CreateControlWindow();
std::unique_ptr<aura::Window> child_window2 = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window1(CreateAppWindowInShellWithId(0));
std::unique_ptr<aura::Window> focus_window2(CreateAppWindowInShellWithId(1));
AddWindowAndActivateParent(child_window1.get(), focus_window1.get());
AddWindowAndShow(child_window2.get(), focus_window2.get());
Lock(child_window1.get(), OrientationLockType::kLandscape);
Lock(child_window2.get(), OrientationLockType::kPortrait);
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
::wm::ActivationClient* activation_client = Shell::Get()->activation_client();
activation_client->ActivateWindow(focus_window2.get());
EXPECT_TRUE(RotationLocked());
EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
activation_client->ActivateWindow(focus_window1.get());
EXPECT_TRUE(RotationLocked());
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
}
// Tests that a rotation lock is removed when the setting window is hidden, and
// that it is reapplied when the window becomes visible.
TEST_F(ScreenOrientationControllerTest, VisibilityChangesLock) {
EnableTabletMode(true);
std::unique_ptr<aura::Window> child_window = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
AddWindowAndActivateParent(child_window.get(), focus_window.get());
Lock(child_window.get(), OrientationLockType::kLandscape);
EXPECT_TRUE(RotationLocked());
child_window->Hide();
EXPECT_FALSE(RotationLocked());
child_window->Show();
EXPECT_TRUE(RotationLocked());
}
// Tests that when a window is destroyed that its rotation lock is removed, and
// window activations no longer change the lock
TEST_F(ScreenOrientationControllerTest, WindowDestructionRemovesLock) {
EnableTabletMode(true);
std::unique_ptr<aura::Window> child_window = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window1(CreateAppWindowInShellWithId(0));
std::unique_ptr<aura::Window> focus_window2(CreateAppWindowInShellWithId(1));
AddWindowAndActivateParent(child_window.get(), focus_window1.get());
Lock(child_window.get(), OrientationLockType::kLandscape);
ASSERT_TRUE(RotationLocked());
focus_window1->RemoveChild(child_window.get());
child_window.reset();
EXPECT_FALSE(RotationLocked());
::wm::ActivationClient* activation_client = Shell::Get()->activation_client();
activation_client->ActivateWindow(focus_window2.get());
EXPECT_FALSE(RotationLocked());
activation_client->ActivateWindow(focus_window1.get());
EXPECT_FALSE(RotationLocked());
}
// Tests that accelerometer readings in each of the screen angles will trigger a
// rotation of the internal display.
TEST_F(ScreenOrientationControllerTest, DisplayRotation) {
EnableTabletMode(true);
// Now test rotating in all directions.
TriggerLidUpdate(gfx::Vector3dF(-kMeanGravity, 0.0f, 0.0f));
EXPECT_EQ(display::Display::ROTATE_90, GetCurrentInternalDisplayRotation());
TriggerLidUpdate(gfx::Vector3dF(0.0f, kMeanGravity, 0.0f));
EXPECT_EQ(display::Display::ROTATE_180, GetCurrentInternalDisplayRotation());
TriggerLidUpdate(gfx::Vector3dF(kMeanGravity, 0.0f, 0.0f));
EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
TriggerLidUpdate(gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f));
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
}
// Tests that low angles are ignored by the accelerometer (i.e. when the device
// is almost laying flat).
TEST_F(ScreenOrientationControllerTest, RotationIgnoresLowAngles) {
EnableTabletMode(true);
TriggerLidUpdate(gfx::Vector3dF(0.0f, -kMeanGravity, -kMeanGravity));
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
TriggerLidUpdate(gfx::Vector3dF(-2.0f, 0.0f, -kMeanGravity));
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
TriggerLidUpdate(gfx::Vector3dF(0.0f, 2.0f, -kMeanGravity));
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
TriggerLidUpdate(gfx::Vector3dF(2.0f, 0.0f, -kMeanGravity));
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
TriggerLidUpdate(gfx::Vector3dF(0.0f, -2.0f, -kMeanGravity));
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
}
// Tests that the display will stick to the current orientation beyond the
// halfway point, preventing frequent updates back and forth.
TEST_F(ScreenOrientationControllerTest, RotationSticky) {
EnableTabletMode(true);
gfx::Vector3dF gravity(0.0f, -kMeanGravity, 0.0f);
TriggerLidUpdate(gravity);
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
// Turn past half-way point to next direction and rotation should remain
// the same.
float degrees = 50.0;
gravity.set_x(-sin(degrees * kDegreesToRadians) * kMeanGravity);
gravity.set_y(-cos(degrees * kDegreesToRadians) * kMeanGravity);
TriggerLidUpdate(gravity);
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
// Turn more and the screen should rotate.
degrees = 70.0;
gravity.set_x(-sin(degrees * kDegreesToRadians) * kMeanGravity);
gravity.set_y(-cos(degrees * kDegreesToRadians) * kMeanGravity);
TriggerLidUpdate(gravity);
EXPECT_EQ(display::Display::ROTATE_90, GetCurrentInternalDisplayRotation());
// Turn back just beyond the half-way point and the new rotation should
// still be in effect.
degrees = 40.0;
gravity.set_x(-sin(degrees * kDegreesToRadians) * kMeanGravity);
gravity.set_y(-cos(degrees * kDegreesToRadians) * kMeanGravity);
TriggerLidUpdate(gravity);
EXPECT_EQ(display::Display::ROTATE_90, GetCurrentInternalDisplayRotation());
}
// Tests that the display will stick to its current orientation when the
// rotation lock has been set.
TEST_F(ScreenOrientationControllerTest, RotationLockPreventsRotation) {
EnableTabletMode(true);
SetUserRotationLocked(true);
// Turn past the threshold for rotation.
float degrees = 90.0;
gfx::Vector3dF gravity(-sin(degrees * kDegreesToRadians) * kMeanGravity,
-cos(degrees * kDegreesToRadians) * kMeanGravity,
0.0f);
TriggerLidUpdate(gravity);
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
SetUserRotationLocked(false);
TriggerLidUpdate(gravity);
EXPECT_EQ(display::Display::ROTATE_90, GetCurrentInternalDisplayRotation());
}
// The ScreenLayoutObserver class that is responsible for adding/updating
// MessageCenter notifications is only added to the SystemTray on ChromeOS.
// Tests that the screen rotation notifications are suppressed when
// triggered by the accelerometer.
TEST_F(ScreenOrientationControllerTest, BlockRotationNotifications) {
EnableTabletMode(true);
Shell::Get()->screen_layout_observer()->set_show_notifications_for_testing(
true);
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
message_center::MessageCenter* message_center =
message_center::MessageCenter::Get();
EXPECT_EQ(0u, message_center->NotificationCount());
EXPECT_FALSE(message_center->HasPopupNotifications());
// Make sure notifications are still displayed when
// adjusting the screen rotation directly when in tablet mode
ASSERT_NE(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
SetInternalDisplayRotation(display::Display::ROTATE_270);
SetSystemRotationLocked(false);
EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
EXPECT_EQ(1u, message_center->NotificationCount());
EXPECT_TRUE(message_center->HasPopupNotifications());
// Clear all notifications
message_center->RemoveAllNotifications(
false /* by_user */, message_center::MessageCenter::RemoveType::ALL);
EXPECT_EQ(0u, message_center->NotificationCount());
EXPECT_FALSE(message_center->HasPopupNotifications());
// Make sure notifications are blocked when adjusting the screen rotation
// via the accelerometer while in tablet mode
// Rotate the screen 90 degrees
ASSERT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
TriggerLidUpdate(gfx::Vector3dF(-kMeanGravity, 0.0f, 0.0f));
ASSERT_EQ(display::Display::ROTATE_90, GetCurrentInternalDisplayRotation());
EXPECT_EQ(0u, message_center->NotificationCount());
EXPECT_FALSE(message_center->HasPopupNotifications());
// Make sure notifications are still displayed when
// adjusting the screen rotation directly when not in tablet mode
EnableTabletMode(false);
// Reset the screen rotation.
SetInternalDisplayRotation(display::Display::ROTATE_0);
// Clear all notifications
message_center->RemoveAllNotifications(
false /* by_user */, message_center::MessageCenter::RemoveType::ALL);
ASSERT_NE(display::Display::ROTATE_180, GetCurrentInternalDisplayRotation());
ASSERT_EQ(0u, message_center->NotificationCount());
ASSERT_FALSE(message_center->HasPopupNotifications());
SetInternalDisplayRotation(display::Display::ROTATE_180);
EXPECT_EQ(display::Display::ROTATE_180, GetCurrentInternalDisplayRotation());
EXPECT_EQ(1u, message_center->NotificationCount());
EXPECT_TRUE(message_center->HasPopupNotifications());
}
// Tests that if a user has set a display rotation that it is restored upon
// exiting tablet mode.
TEST_F(ScreenOrientationControllerTest, ResetUserRotationUponExit) {
display::test::DisplayManagerTestApi(display_manager())
.SetFirstDisplayAsInternalDisplay();
SetInternalDisplayRotation(display::Display::ROTATE_90);
EnableTabletMode(true);
TriggerLidUpdate(gfx::Vector3dF(0.0f, kMeanGravity, 0.0f));
EXPECT_EQ(display::Display::ROTATE_180, GetCurrentInternalDisplayRotation());
EnableTabletMode(false);
EXPECT_EQ(display::Display::ROTATE_90, GetCurrentInternalDisplayRotation());
}
// Tests that if a user changes the display rotation, while rotation is locked,
// that the updates are recorded. Upon exiting tablet mode the latest user
// rotation should be applied.
TEST_F(ScreenOrientationControllerTest, UpdateUserRotationWhileRotationLocked) {
EnableTabletMode(true);
SetInternalDisplayRotation(display::Display::ROTATE_270);
// User sets rotation to the same rotation that the display was at when
// tablet mode was activated.
SetInternalDisplayRotation(display::Display::ROTATE_0);
EnableTabletMode(false);
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
}
// Tests that when the orientation lock is set to Landscape, that rotation can
// be done between the two angles of the orientation.
TEST_F(ScreenOrientationControllerTest, LandscapeOrientationAllowsRotation) {
std::unique_ptr<aura::Window> child_window = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
EnableTabletMode(true);
AddWindowAndActivateParent(child_window.get(), focus_window.get());
Lock(child_window.get(), OrientationLockType::kLandscape);
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
EXPECT_TRUE(RotationLocked());
// Inverse of orientation is allowed
TriggerLidUpdate(gfx::Vector3dF(0.0f, kMeanGravity, 0.0f));
EXPECT_EQ(display::Display::ROTATE_180, GetCurrentInternalDisplayRotation());
// Display rotations between are not allowed
TriggerLidUpdate(gfx::Vector3dF(kMeanGravity, 0.0f, 0.0f));
EXPECT_EQ(display::Display::ROTATE_180, GetCurrentInternalDisplayRotation());
TriggerLidUpdate(gfx::Vector3dF(-kMeanGravity, 0.0f, 0.0f));
EXPECT_EQ(display::Display::ROTATE_180, GetCurrentInternalDisplayRotation());
}
// Tests that when the orientation lock is set to Portrait, that rotation can be
// done between the two angles of the orientation.
TEST_F(ScreenOrientationControllerTest, PortraitOrientationAllowsRotation) {
std::unique_ptr<aura::Window> child_window = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
EnableTabletMode(true);
AddWindowAndActivateParent(child_window.get(), focus_window.get());
Lock(child_window.get(), OrientationLockType::kPortrait);
EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
EXPECT_TRUE(RotationLocked());
// Inverse of orientation is allowed
TriggerLidUpdate(gfx::Vector3dF(-kMeanGravity, 0.0f, 0.0f));
EXPECT_EQ(display::Display::ROTATE_90, GetCurrentInternalDisplayRotation());
// Display rotations between are not allowed
TriggerLidUpdate(gfx::Vector3dF(0.0f, kMeanGravity, 0.0f));
EXPECT_EQ(display::Display::ROTATE_90, GetCurrentInternalDisplayRotation());
TriggerLidUpdate(gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f));
EXPECT_EQ(display::Display::ROTATE_90, GetCurrentInternalDisplayRotation());
}
// Tests that for an orientation lock which does not allow rotation, that the
// display rotation remains constant.
TEST_F(ScreenOrientationControllerTest, OrientationLockDisallowsRotation) {
std::unique_ptr<aura::Window> child_window = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
EnableTabletMode(true);
AddWindowAndActivateParent(child_window.get(), focus_window.get());
Lock(child_window.get(), OrientationLockType::kPortraitPrimary);
EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
EXPECT_TRUE(RotationLocked());
// Rotation does not change.
TriggerLidUpdate(gfx::Vector3dF(kMeanGravity, 0.0f, 0.0f));
EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
TriggerLidUpdate(gfx::Vector3dF(0.0f, kMeanGravity, 0.0f));
EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
TriggerLidUpdate(gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f));
EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
}
// Tests that after a Window has applied an orientation lock which supports
// rotation, that a user rotation lock does not allow rotation.
TEST_F(ScreenOrientationControllerTest, UserRotationLockDisallowsRotation) {
std::unique_ptr<aura::Window> child_window = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window(CreateAppWindowInShellWithId(0));
EnableTabletMode(true);
AddWindowAndActivateParent(child_window.get(), focus_window.get());
Lock(child_window.get(), OrientationLockType::kLandscape);
Unlock(child_window.get());
SetUserRotationLocked(true);
EXPECT_TRUE(RotationLocked());
EXPECT_TRUE(UserRotationLocked());
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
TriggerLidUpdate(gfx::Vector3dF(0.0f, kMeanGravity, 0.0f));
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
}
// Verifies rotating an inactive Display is successful.
TEST_F(ScreenOrientationControllerTest, RotateInactiveDisplay) {
const int64_t kInternalDisplayId = 9;
const int64_t kExternalDisplayId = 10;
const display::Display::Rotation kNewRotation = display::Display::ROTATE_180;
const display::ManagedDisplayInfo internal_display_info =
CreateDisplayInfo(kInternalDisplayId, gfx::Rect(0, 0, 500, 500));
const display::ManagedDisplayInfo external_display_info =
CreateDisplayInfo(kExternalDisplayId, gfx::Rect(1, 1, 500, 500));
std::vector<display::ManagedDisplayInfo> display_info_list_two_active;
display_info_list_two_active.push_back(internal_display_info);
display_info_list_two_active.push_back(external_display_info);
std::vector<display::ManagedDisplayInfo> display_info_list_one_active;
display_info_list_one_active.push_back(external_display_info);
// The display::ManagedDisplayInfo list with two active displays needs to be
// added first so that the DisplayManager can track the
// |internal_display_info| as inactive instead of non-existent.
display_manager()->UpdateDisplaysWith(display_info_list_two_active);
display_manager()->UpdateDisplaysWith(display_info_list_one_active);
display::test::ScopedSetInternalDisplayId set_internal(display_manager(),
kInternalDisplayId);
ASSERT_NE(kNewRotation, display_manager()
->GetDisplayInfo(kInternalDisplayId)
.GetActiveRotation());
ScreenOrientationControllerTestApi(
Shell::Get()->screen_orientation_controller())
.SetDisplayRotation(kNewRotation,
display::Display::RotationSource::ACTIVE);
EXPECT_EQ(kNewRotation, display_manager()
->GetDisplayInfo(kInternalDisplayId)
.GetActiveRotation());
}
TEST_F(ScreenOrientationControllerTest, UserRotationLockedOrientation) {
ScreenOrientationController* orientation_controller =
Shell::Get()->screen_orientation_controller();
orientation_controller->ToggleUserRotationLock();
EXPECT_TRUE(orientation_controller->user_rotation_locked());
EXPECT_EQ(OrientationLockType::kLandscapePrimary, UserLockedOrientation());
orientation_controller->ToggleUserRotationLock();
SetInternalDisplayRotation(display::Display::ROTATE_270);
orientation_controller->ToggleUserRotationLock();
EXPECT_EQ(OrientationLockType::kPortraitPrimary, UserLockedOrientation());
orientation_controller->ToggleUserRotationLock();
SetInternalDisplayRotation(display::Display::ROTATE_180);
orientation_controller->ToggleUserRotationLock();
EXPECT_EQ(OrientationLockType::kLandscapeSecondary, UserLockedOrientation());
orientation_controller->ToggleUserRotationLock();
SetInternalDisplayRotation(display::Display::ROTATE_90);
orientation_controller->ToggleUserRotationLock();
EXPECT_EQ(OrientationLockType::kPortraitSecondary, UserLockedOrientation());
orientation_controller->ToggleUserRotationLock();
SetInternalDisplayRotation(display::Display::ROTATE_270);
UpdateDisplay("800x1280");
orientation_controller->ToggleUserRotationLock();
EXPECT_TRUE(orientation_controller->user_rotation_locked());
EXPECT_EQ(OrientationLockType::kPortraitPrimary, UserLockedOrientation());
orientation_controller->ToggleUserRotationLock();
SetInternalDisplayRotation(display::Display::ROTATE_90);
orientation_controller->ToggleUserRotationLock();
EXPECT_EQ(OrientationLockType::kLandscapePrimary, UserLockedOrientation());
orientation_controller->ToggleUserRotationLock();
SetInternalDisplayRotation(display::Display::ROTATE_180);
orientation_controller->ToggleUserRotationLock();
EXPECT_EQ(OrientationLockType::kPortraitSecondary, UserLockedOrientation());
orientation_controller->ToggleUserRotationLock();
SetInternalDisplayRotation(display::Display::ROTATE_270);
orientation_controller->ToggleUserRotationLock();
EXPECT_EQ(OrientationLockType::kLandscapeSecondary, UserLockedOrientation());
orientation_controller->ToggleUserRotationLock();
}
TEST_F(ScreenOrientationControllerTest, UserRotationLock) {
EnableTabletMode(true);
std::unique_ptr<aura::Window> child_window1 = CreateControlWindow();
std::unique_ptr<aura::Window> child_window2 = CreateControlWindow();
std::unique_ptr<aura::Window> focus_window1(CreateAppWindowInShellWithId(0));
std::unique_ptr<aura::Window> focus_window2(CreateAppWindowInShellWithId(1));
AddWindowAndActivateParent(child_window2.get(), focus_window2.get());
AddWindowAndActivateParent(child_window1.get(), focus_window1.get());
ASSERT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
ASSERT_FALSE(RotationLocked());
ASSERT_FALSE(UserRotationLocked());
ScreenOrientationController* orientation_controller =
Shell::Get()->screen_orientation_controller();
ASSERT_FALSE(orientation_controller->user_rotation_locked());
orientation_controller->ToggleUserRotationLock();
ASSERT_TRUE(orientation_controller->user_rotation_locked());
Lock(child_window1.get(), OrientationLockType::kPortrait);
EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
::wm::ActivationClient* activation_client = Shell::Get()->activation_client();
// Activating any will switch to the natural orientation.
activation_client->ActivateWindow(focus_window2.get());
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
// Activating the portrait window will rotate to the portrait.
activation_client->ActivateWindow(focus_window1.get());
EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
// User locked to the 90 dig.
orientation_controller->ToggleUserRotationLock();
orientation_controller->ToggleUserRotationLock();
// Switching to Any orientation will stay to the user locked orientation.
activation_client->ActivateWindow(focus_window2.get());
EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
// Application forced to be landscape.
Lock(child_window2.get(), OrientationLockType::kLandscape);
EXPECT_EQ(display::Display::ROTATE_0, GetCurrentInternalDisplayRotation());
Lock(child_window1.get(), OrientationLockType::kAny);
activation_client->ActivateWindow(focus_window1.get());
// Switching back to any will rotate to user rotation.
EXPECT_EQ(display::Display::ROTATE_270, GetCurrentInternalDisplayRotation());
}
} // namespace ash