From 0af4521339c7ca15535ea92dd4c27f94610b9a67 Mon Sep 17 00:00:00 2001 From: jonross Date: Tue, 13 Jan 2015 10:55:46 -0800 Subject: [PATCH] This change renames ScreenOrientationDelegate to ScreenOrientationController to reflect on the new responsibilities that it is assuming. The tracking of the accelerometer screen rotation, as well as the rotation lock, has been moved from MaximizeModeController to ScreenOrientationController. ScreenOrientationController is now responsible for all rotation lock requests, from both TouchView user interface controls, as well as the Screen Orientation JavaScript API. ScreenOrientationController is Chrome OS only. Classes using the rotation lock API have been updated to only do so on Chrome OS. Screen rotation tests have been moved from MaximizeModeControllerTest to ScreenOrientationControllerTest. Other unittests have been updated to reflect the Chrome OS change. All pre-existing tests for rotation behaviour have been ran. Manual testing of both TouchView and Screen Orientation API have been done. TEST=ScreenOrientationControllerTest, TrayRotationLockTest, MaximizeModeControllerTest, DisplayPreferencesTest BUG=396760 Review URL: https://codereview.chromium.org/759063002 Cr-Commit-Position: refs/heads/master@{#311299} --- ash/ash.gyp | 8 +- .../screen_orientation_controller_chromeos.cc | 342 +++++++++++++++++ .../screen_orientation_controller_chromeos.h | 145 ++++++++ ...een_orientation_controller_chromeos_unittest.cc | 408 +++++++++++++++++++++ .../display/screen_orientation_delegate_chromeos.h | 64 ---- ...creen_orientation_delegate_chromeos_unittest.cc | 194 ---------- ash/shell.cc | 15 +- ash/shell.h | 10 +- ash/system/chromeos/rotation/tray_rotation_lock.cc | 37 +- ash/system/chromeos/rotation/tray_rotation_lock.h | 23 +- .../rotation/tray_rotation_lock_unittest.cc | 23 +- ash/system/chromeos/tray_display.cc | 17 +- ash/wm/maximize_mode/maximize_mode_controller.cc | 252 +------------ ash/wm/maximize_mode/maximize_mode_controller.h | 97 +---- .../maximize_mode_controller_unittest.cc | 284 +------------- .../display/display_preferences_unittest.cc | 71 +--- chromeos/accelerometer/accelerometer_reader.cc | 12 + chromeos/accelerometer/accelerometer_reader.h | 6 + ui/gfx/geometry/vector3d_f.cc | 25 ++ ui/gfx/geometry/vector3d_f.h | 11 + ui/gfx/geometry/vector3d_unittest.cc | 53 +++ 21 files changed, 1126 insertions(+), 971 deletions(-) create mode 100644 ash/content/display/screen_orientation_controller_chromeos.cc create mode 100644 ash/content/display/screen_orientation_controller_chromeos.h create mode 100644 ash/content/display/screen_orientation_controller_chromeos_unittest.cc delete mode 100644 ash/content/display/screen_orientation_delegate_chromeos.h delete mode 100644 ash/content/display/screen_orientation_delegate_chromeos_unittest.cc diff --git a/ash/ash.gyp b/ash/ash.gyp index 0af524a51d31..c3644175538c 100644 --- a/ash/ash.gyp +++ b/ash/ash.gyp @@ -44,8 +44,8 @@ 'ash_touch_exploration_manager_chromeos.h', 'cancel_mode.cc', 'cancel_mode.h', - 'content/display/screen_orientation_delegate_chromeos.cc', - 'content/display/screen_orientation_delegate_chromeos.h', + 'content/display/screen_orientation_controller_chromeos.cc', + 'content/display/screen_orientation_controller_chromeos.h', 'debug.cc', 'debug.h', 'default_accessibility_delegate.cc', @@ -757,7 +757,7 @@ 'accelerators/spoken_feedback_toggler_unittest.cc', 'ash_touch_exploration_manager_chromeos_unittest.cc', 'autoclick/autoclick_unittest.cc', - 'content/display/screen_orientation_delegate_chromeos_unittest.cc', + 'content/display/screen_orientation_controller_chromeos_unittest.cc', 'desktop_background/desktop_background_controller_unittest.cc', 'dip_unittest.cc', 'display/display_change_observer_chromeos_unittest.cc', @@ -1096,7 +1096,6 @@ 'conditions': [ ['chromeos==0', { 'sources!': [ - 'content/display/screen_orientation_delegate_chromeos_unittest.cc', # TODO(zork): fix this test to build on Windows. See: crosbug.com/26906 'focus_cycler_unittest.cc', # All tests for multiple displays: not supported on Windows Ash. @@ -1107,6 +1106,7 @@ 'wm/workspace/workspace_window_resizer_unittest.cc', 'sticky_keys/sticky_keys_overlay_unittest.cc', 'sticky_keys/sticky_keys_unittest.cc', + 'system/chromeos/rotation/tray_rotation_lock_unittest.cc', 'system/tray/media_security/multi_profile_media_tray_item_unittest.cc', 'autoclick/autoclick_unittest.cc', "virtual_keyboard_controller_unittest.cc" diff --git a/ash/content/display/screen_orientation_controller_chromeos.cc b/ash/content/display/screen_orientation_controller_chromeos.cc new file mode 100644 index 000000000000..f36f1931b9c0 --- /dev/null +++ b/ash/content/display/screen_orientation_controller_chromeos.cc @@ -0,0 +1,342 @@ +// 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/content/display/screen_orientation_controller_chromeos.h" + +#include "ash/ash_switches.h" +#include "ash/display/display_info.h" +#include "ash/display/display_manager.h" +#include "ash/shell.h" +#include "ash/wm/maximize_mode/maximize_mode_controller.h" +#include "base/auto_reset.h" +#include "base/command_line.h" +#include "chromeos/accelerometer/accelerometer_reader.h" +#include "content/public/browser/screen_orientation_provider.h" +#include "content/public/browser/web_contents.h" +#include "ui/aura/window.h" +#include "ui/gfx/display.h" +#include "ui/gfx/geometry/size.h" + +namespace { + +// 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 m/s^2 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. The value is +// effectively the sine of the rise angle required times the acceleration due +// to gravity, with the current value requiring at least a 25 degree rise. +const float kMinimumAccelerationScreenRotation = 4.2f; + +blink::WebScreenOrientationLockType GetDisplayNaturalOrientation() { + ash::DisplayManager* display_manager = + ash::Shell::GetInstance()->display_manager(); + if (!display_manager->HasInternalDisplay()) + return blink::WebScreenOrientationLockLandscape; + + ash::DisplayInfo info = + display_manager->GetDisplayInfo(gfx::Display::InternalDisplayId()); + gfx::Size size = info.size_in_pixel(); + switch (info.rotation()) { + case gfx::Display::ROTATE_0: + case gfx::Display::ROTATE_180: + return size.height() >= size.width() + ? blink::WebScreenOrientationLockPortrait + : blink::WebScreenOrientationLockLandscape; + case gfx::Display::ROTATE_90: + case gfx::Display::ROTATE_270: + return size.height() < size.width() + ? blink::WebScreenOrientationLockPortrait + : blink::WebScreenOrientationLockLandscape; + } + NOTREACHED(); + return blink::WebScreenOrientationLockLandscape; +} + +} // namespace + +namespace ash { + +ScreenOrientationController::ScreenOrientationController() + : locking_window_(NULL), + natural_orientation_(GetDisplayNaturalOrientation()), + ignore_display_configuration_updates_(false), + rotation_locked_(false), + user_rotation_(gfx::Display::ROTATE_0), + current_rotation_(gfx::Display::ROTATE_0) { + content::ScreenOrientationProvider::SetDelegate(this); + Shell::GetInstance()->AddShellObserver(this); +} + +ScreenOrientationController::~ScreenOrientationController() { + content::ScreenOrientationProvider::SetDelegate(NULL); + Shell::GetInstance()->RemoveShellObserver(this); + Shell::GetInstance()->accelerometer_reader()->RemoveObserver(this); + Shell::GetInstance()->display_controller()->RemoveObserver(this); +} + +void ScreenOrientationController::AddObserver(Observer* observer) { + observers_.AddObserver(observer); +} + +void ScreenOrientationController::RemoveObserver(Observer* observer) { + observers_.RemoveObserver(observer); +} + +void ScreenOrientationController::SetRotationLocked(bool rotation_locked) { + if (rotation_locked_ == rotation_locked) + return; + rotation_locked_ = rotation_locked; + FOR_EACH_OBSERVER(Observer, observers_, + OnRotationLockChanged(rotation_locked_)); + DisplayManager* display_manager = Shell::GetInstance()->display_manager(); + if (!display_manager->HasInternalDisplay()) + return; + base::AutoReset auto_ignore_display_configuration_updates( + &ignore_display_configuration_updates_, true); + display_manager->RegisterDisplayRotationProperties(rotation_locked_, + current_rotation_); +} + +void ScreenOrientationController::SetDisplayRotation( + gfx::Display::Rotation rotation) { + DisplayManager* display_manager = Shell::GetInstance()->display_manager(); + if (!display_manager->HasInternalDisplay()) + return; + current_rotation_ = rotation; + base::AutoReset auto_ignore_display_configuration_updates( + &ignore_display_configuration_updates_, true); + display_manager->SetDisplayRotation(gfx::Display::InternalDisplayId(), + rotation); +} + +void ScreenOrientationController::OnAccelerometerUpdated( + const ui::AccelerometerUpdate& update) { + if (rotation_locked_) + return; + if (!update.has(ui::ACCELEROMETER_SOURCE_SCREEN)) + return; + // Ignore the reading if it appears unstable. The reading is considered + // unstable if it deviates too much from gravity + if (chromeos::AccelerometerReader::IsReadingStable( + update, ui::ACCELEROMETER_SOURCE_SCREEN)) { + HandleScreenRotation(update.get(ui::ACCELEROMETER_SOURCE_SCREEN)); + } +} + +bool ScreenOrientationController::FullScreenRequired( + content::WebContents* web_contents) { + return true; +} + +void ScreenOrientationController::Lock( + content::WebContents* web_contents, + blink::WebScreenOrientationLockType lock_orientation) { + aura::Window* requesting_window = web_contents->GetNativeView(); + // TODO(jonross): Track one rotation lock per window. When the active window + // changes apply any corresponding rotation lock. + if (!locking_window_) + locking_window_ = requesting_window; + else if (requesting_window != locking_window_) + return; + + switch (lock_orientation) { + case blink::WebScreenOrientationLockAny: + SetRotationLocked(false); + locking_window_ = NULL; + break; + case blink::WebScreenOrientationLockDefault: + NOTREACHED(); + break; + case blink::WebScreenOrientationLockPortraitPrimary: + LockRotationToPrimaryOrientation(blink::WebScreenOrientationLockPortrait); + break; + case blink::WebScreenOrientationLockLandscape: + case blink::WebScreenOrientationLockPortrait: + LockToRotationMatchingOrientation(lock_orientation); + break; + case blink::WebScreenOrientationLockPortraitSecondary: + LockRotationToSecondaryOrientation( + blink::WebScreenOrientationLockPortrait); + break; + case blink::WebScreenOrientationLockLandscapeSecondary: + LockRotationToSecondaryOrientation( + blink::WebScreenOrientationLockLandscape); + break; + case blink::WebScreenOrientationLockLandscapePrimary: + LockRotationToPrimaryOrientation( + blink::WebScreenOrientationLockLandscape); + break; + case blink::WebScreenOrientationLockNatural: + LockRotation(gfx::Display::ROTATE_0); + break; + default: + NOTREACHED(); + break; + } +} + +bool ScreenOrientationController::ScreenOrientationProviderSupported() { + return Shell::GetInstance() + ->maximize_mode_controller() + ->IsMaximizeModeWindowManagerEnabled() && + base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kAshEnableTouchViewTesting); +} + +void ScreenOrientationController::Unlock(content::WebContents* web_contents) { + aura::Window* requesting_window = web_contents->GetNativeView(); + if (requesting_window != locking_window_) + return; + locking_window_ = NULL; + SetRotationLocked(false); +} + +void ScreenOrientationController::OnDisplayConfigurationChanged() { + if (ignore_display_configuration_updates_) + return; + gfx::Display::Rotation user_rotation = + Shell::GetInstance() + ->display_manager() + ->GetDisplayInfo(gfx::Display::InternalDisplayId()) + .rotation(); + if (user_rotation != current_rotation_) { + // A user may change other display configuration settings. When the user + // does change the rotation setting, then lock rotation to prevent the + // accelerometer from erasing their change. + SetRotationLocked(true); + user_rotation_ = current_rotation_ = user_rotation; + } +} + +void ScreenOrientationController::OnMaximizeModeStarted() { + DisplayManager* display_manager = Shell::GetInstance()->display_manager(); + if (!display_manager->HasInternalDisplay()) + return; + current_rotation_ = user_rotation_ = + display_manager->GetDisplayInfo(gfx::Display::InternalDisplayId()) + .rotation(); + LoadDisplayRotationProperties(); + Shell::GetInstance()->accelerometer_reader()->AddObserver(this); + Shell::GetInstance()->display_controller()->AddObserver(this); +} + +void ScreenOrientationController::OnMaximizeModeEnded() { + if (!Shell::GetInstance()->display_manager()->HasInternalDisplay()) + return; + Shell::GetInstance()->accelerometer_reader()->RemoveObserver(this); + Shell::GetInstance()->display_controller()->RemoveObserver(this); + if (current_rotation_ != user_rotation_) + SetDisplayRotation(user_rotation_); +} + +void ScreenOrientationController::LockRotation( + gfx::Display::Rotation rotation) { + SetRotationLocked(true); + SetDisplayRotation(rotation); +} + +void ScreenOrientationController::LockRotationToPrimaryOrientation( + blink::WebScreenOrientationLockType lock_orientation) { + LockRotation(natural_orientation_ == lock_orientation + ? gfx::Display::ROTATE_0 + : gfx::Display::ROTATE_90); +} + +void ScreenOrientationController::LockRotationToSecondaryOrientation( + blink::WebScreenOrientationLockType lock_orientation) { + LockRotation(natural_orientation_ == lock_orientation + ? gfx::Display::ROTATE_180 + : gfx::Display::ROTATE_270); +} + +void ScreenOrientationController::LockToRotationMatchingOrientation( + blink::WebScreenOrientationLockType lock_orientation) { + // TODO(jonross): Update MaximizeModeController to allow rotation between + // two angles of an orientation (e.g. from ROTATE_0 to ROTATE_180, and from + // ROTATE_90 to ROTATE_270) + DisplayManager* display_manager = Shell::GetInstance()->display_manager(); + if (!display_manager->HasInternalDisplay()) + return; + + gfx::Display::Rotation rotation = + display_manager->GetDisplayInfo(gfx::Display::InternalDisplayId()) + .rotation(); + if (natural_orientation_ == lock_orientation) { + if (rotation == gfx::Display::ROTATE_0 || + rotation == gfx::Display::ROTATE_180) { + SetRotationLocked(true); + } else { + LockRotation(gfx::Display::ROTATE_0); + } + } else { + if (rotation == gfx::Display::ROTATE_90 || + rotation == gfx::Display::ROTATE_270) { + SetRotationLocked(true); + } else { + LockRotation(gfx::Display::ROTATE_90); + } + } +} + +void ScreenOrientationController::HandleScreenRotation( + const gfx::Vector3dF& lid) { + 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 const 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_y(-1.0f); + else if (current_rotation_ == gfx::Display::ROTATE_90) + down.set_x(-1.0f); + else if (current_rotation_ == gfx::Display::ROTATE_180) + down.set_y(1.0f); + else + down.set_x(1.0f); + + // Don't rotate if the screen has not passed the threshold. + if (gfx::AngleBetweenVectorsInDegrees(down, lid_flattened) < + kDisplayRotationStickyAngleDegrees) { + return; + } + + float angle = gfx::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; + + if (new_rotation != current_rotation_) + SetDisplayRotation(new_rotation); +} + +void ScreenOrientationController::LoadDisplayRotationProperties() { + DisplayManager* display_manager = Shell::GetInstance()->display_manager(); + if (!display_manager->registered_internal_display_rotation_lock()) + return; + SetDisplayRotation(display_manager->registered_internal_display_rotation()); + SetRotationLocked(true); +} + +} // namespace ash diff --git a/ash/content/display/screen_orientation_controller_chromeos.h b/ash/content/display/screen_orientation_controller_chromeos.h new file mode 100644 index 000000000000..362e131134c4 --- /dev/null +++ b/ash/content/display/screen_orientation_controller_chromeos.h @@ -0,0 +1,145 @@ +// 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. + +#ifndef ASH_CONTENT_DISPLAY_SCREEN_ORIENTATION_CONTROLLER_CHROMEOS_H_ +#define ASH_CONTENT_DISPLAY_SCREEN_ORIENTATION_CONTROLLER_CHROMEOS_H_ + +#include "ash/ash_export.h" +#include "ash/display/display_controller.h" +#include "ash/shell_observer.h" +#include "base/macros.h" +#include "base/observer_list.h" +#include "chromeos/accelerometer/accelerometer_reader.h" +#include "content/public/browser/screen_orientation_delegate.h" +#include "third_party/WebKit/public/platform/WebScreenOrientationLockType.h" +#include "ui/gfx/display.h" + +namespace aura { +class Window; +} + +namespace content { +class WebContents; +} + +namespace ash { + +// Implements ChromeOS specific functionality for ScreenOrientationProvider. +class ASH_EXPORT ScreenOrientationController + : public chromeos::AccelerometerReader::Observer, + public content::ScreenOrientationDelegate, + public DisplayController::Observer, + public ShellObserver { + public: + // Observer that reports changes to the state of ScreenOrientationProvider's + // rotation lock. + class Observer { + public: + // Invoked when rotation is locked or unlocked. + virtual void OnRotationLockChanged(bool rotation_locked) {} + + protected: + virtual ~Observer() {} + }; + + ScreenOrientationController(); + ~ScreenOrientationController() override; + + // Add/Remove observers. + void AddObserver(Observer* observer); + void RemoveObserver(Observer* observer); + + bool ignore_display_configuration_updates() const { + return ignore_display_configuration_updates_; + } + + // True if |rotation_lock_| has been set and accelerometer updates should not + // rotate the display. + bool rotation_locked() const { return rotation_locked_; } + + // If |rotation_locked| future accelerometer updates should not change the + // display rotation. + void SetRotationLocked(bool rotation_locked); + + // Sets the display rotation and suppresses display notifications. + void SetDisplayRotation(gfx::Display::Rotation rotation); + + // chromeos::AccelerometerReader::Observer: + void OnAccelerometerUpdated(const ui::AccelerometerUpdate& update) override; + + // content::ScreenOrientationDelegate: + bool FullScreenRequired(content::WebContents* web_contents) override; + void Lock(content::WebContents* web_contents, + blink::WebScreenOrientationLockType lock_orientation) override; + bool ScreenOrientationProviderSupported() override; + void Unlock(content::WebContents* web_contents) override; + + // DisplayController::Observer: + void OnDisplayConfigurationChanged() override; + + // ShellObserver: + void OnMaximizeModeStarted() override; + void OnMaximizeModeEnded() override; + + private: + // Sets the display rotation to |rotation|. Future accelerometer updates + // should not be used to change the rotation. SetRotationLocked(false) removes + // the rotation lock. + void LockRotation(gfx::Display::Rotation rotation); + + // Locks rotation to the angle matching the primary orientation for + // |lock_orientation|. + void LockRotationToPrimaryOrientation( + blink::WebScreenOrientationLockType lock_orientation); + + // Locks rotation to the angle matching the secondary orientation for + // |lock_orientation|. + void LockRotationToSecondaryOrientation( + blink::WebScreenOrientationLockType lock_orientation); + + // For orientations that do not specify primary or secondary, locks to the + // current rotation if it matches |lock_orientation|. Otherwise locks to a + // matching rotation. + void LockToRotationMatchingOrientation( + blink::WebScreenOrientationLockType lock_orientation); + + // Detect screen rotation from |lid| accelerometer and automatically rotate + // screen. + void HandleScreenRotation(const gfx::Vector3dF& lid); + + // Checks DisplayManager for registered rotation lock, and rotation, + // preferences. These are then applied. + void LoadDisplayRotationProperties(); + + // The window that has applied the current lock. No other window can apply a + // lock until the current window unlocks rotation. + aura::Window* locking_window_; + + // The orientation of the display when at a rotation of 0. + blink::WebScreenOrientationLockType natural_orientation_; + + // True when changes being applied cause OnDisplayConfigurationChanged() to be + // called, and for which these changes should be ignored. + bool ignore_display_configuration_updates_; + + // When true then accelerometer updates should not rotate the display. + bool rotation_locked_; + + // The rotation of the display set by the user. This rotation will be + // restored upon exiting maximize mode. + gfx::Display::Rotation user_rotation_; + + // The current rotation set by ScreenOrientationController for the internal + // display. + gfx::Display::Rotation current_rotation_; + + // Rotation Lock observers. + ObserverList observers_; + + DISALLOW_COPY_AND_ASSIGN(ScreenOrientationController); +}; + +} // namespace ash + +#endif // ASH_CONTENT_DISPLAY_SCREEN_ORIENTATION_CONTROLLER_CHROMEOS_H_ diff --git a/ash/content/display/screen_orientation_controller_chromeos_unittest.cc b/ash/content/display/screen_orientation_controller_chromeos_unittest.cc new file mode 100644 index 000000000000..54e590eeb8ac --- /dev/null +++ b/ash/content/display/screen_orientation_controller_chromeos_unittest.cc @@ -0,0 +1,408 @@ +// 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/ash_switches.h" +#include "ash/content/display/screen_orientation_controller_chromeos.h" +#include "ash/display/display_info.h" +#include "ash/display/display_manager.h" +#include "ash/shell.h" +#include "ash/test/ash_test_base.h" +#include "ash/test/ash_test_helper.h" +#include "ash/test/test_shell_delegate.h" +#include "ash/test/test_system_tray_delegate.h" +#include "ash/wm/maximize_mode/maximize_mode_controller.h" +#include "base/command_line.h" +#include "base/memory/scoped_ptr.h" +#include "chromeos/accelerometer/accelerometer_reader.h" +#include "content/public/browser/browser_context.h" +#include "content/public/browser/web_contents.h" +#include "content/public/test/test_browser_context.h" +#include "third_party/WebKit/public/platform/WebScreenOrientationLockType.h" +#include "ui/gfx/display.h" +#include "ui/message_center/message_center.h" +#include "ui/views/test/webview_test_helper.h" +#include "ui/views/view.h" +#include "ui/views/views_delegate.h" + +namespace ash { + +namespace { + +const float kDegreesToRadians = 3.1415926f / 180.0f; +const float kMeanGravity = 9.8066f; + +void EnableMaximizeMode(bool enable) { + Shell::GetInstance() + ->maximize_mode_controller() + ->EnableMaximizeModeWindowManager(enable); +} + +gfx::Display::Rotation GetInternalDisplayRotation() { + return Shell::GetInstance() + ->display_manager() + ->GetDisplayInfo(gfx::Display::InternalDisplayId()) + .rotation(); +} + +gfx::Display::Rotation Rotation() { + return Shell::GetInstance() + ->display_manager() + ->GetDisplayInfo(gfx::Display::InternalDisplayId()) + .rotation(); +} + +bool RotationLocked() { + return Shell::GetInstance() + ->screen_orientation_controller() + ->rotation_locked(); +} + +void SetInternalDisplayRotation(gfx::Display::Rotation rotation) { + Shell::GetInstance()->display_manager()->SetDisplayRotation( + gfx::Display::InternalDisplayId(), rotation); +} + +void SetRotationLocked(bool rotation_locked) { + Shell::GetInstance()->screen_orientation_controller()->SetRotationLocked( + rotation_locked); +} + +void TriggerLidUpdate(const gfx::Vector3dF& lid) { + ui::AccelerometerUpdate update; + update.Set(ui::ACCELEROMETER_SOURCE_SCREEN, lid.x(), lid.y(), lid.z()); + Shell::GetInstance()->screen_orientation_controller()->OnAccelerometerUpdated( + update); +} + +} // namespace + +class ScreenOrientationControllerTest : public test::AshTestBase { + public: + ScreenOrientationControllerTest(); + ~ScreenOrientationControllerTest() override; + + ScreenOrientationController* delegate() { + return screen_orientation_controller_; + } + + // Creates and initializes and empty content::WebContents that is backed by a + // content::BrowserContext and that has an aura::Window. + content::WebContents* CreateWebContents(); + + // Creates a secondary content::WebContents, with a separate + // content::BrowserContext. + content::WebContents* CreateSecondaryWebContents(); + + // test::AshTestBase: + void SetUp() override; + + private: + ScreenOrientationController* screen_orientation_controller_; + + // Optional content::BrowserContext used for two window tests. + scoped_ptr secondary_browser_context_; + + // Setups underlying content layer so that content::WebContents can be + // generated. + scoped_ptr webview_test_helper_; + + DISALLOW_COPY_AND_ASSIGN(ScreenOrientationControllerTest); +}; + +ScreenOrientationControllerTest::ScreenOrientationControllerTest() { + webview_test_helper_.reset(new views::WebViewTestHelper()); +} + +ScreenOrientationControllerTest::~ScreenOrientationControllerTest() { +} + +content::WebContents* ScreenOrientationControllerTest::CreateWebContents() { + return views::ViewsDelegate::views_delegate->CreateWebContents( + ash_test_helper()->test_shell_delegate()->GetActiveBrowserContext(), + nullptr); +} + +content::WebContents* +ScreenOrientationControllerTest::CreateSecondaryWebContents() { + secondary_browser_context_.reset(new content::TestBrowserContext()); + return views::ViewsDelegate::views_delegate->CreateWebContents( + secondary_browser_context_.get(), nullptr); +} + +void ScreenOrientationControllerTest::SetUp() { + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kAshUseFirstDisplayAsInternal); + base::CommandLine::ForCurrentProcess()->AppendSwitch( + switches::kAshEnableTouchViewTesting); + test::AshTestBase::SetUp(); + screen_orientation_controller_ = + Shell::GetInstance()->screen_orientation_controller(); +} + +// Tests that a content::WebContents can lock rotation. +TEST_F(ScreenOrientationControllerTest, LockOrientation) { + scoped_ptr content(CreateWebContents()); + ASSERT_NE(nullptr, content->GetNativeView()); + ASSERT_EQ(gfx::Display::ROTATE_0, Rotation()); + ASSERT_FALSE(RotationLocked()); + + delegate()->Lock(content.get(), blink::WebScreenOrientationLockLandscape); + EXPECT_EQ(gfx::Display::ROTATE_0, Rotation()); + EXPECT_TRUE(RotationLocked()); +} + +// Tests that a content::WebContents can unlock rotation. +TEST_F(ScreenOrientationControllerTest, Unlock) { + scoped_ptr content(CreateWebContents()); + ASSERT_NE(nullptr, content->GetNativeView()); + ASSERT_EQ(gfx::Display::ROTATE_0, Rotation()); + ASSERT_FALSE(RotationLocked()); + + delegate()->Lock(content.get(), blink::WebScreenOrientationLockLandscape); + EXPECT_EQ(gfx::Display::ROTATE_0, Rotation()); + EXPECT_TRUE(RotationLocked()); + + delegate()->Unlock(content.get()); + EXPECT_FALSE(RotationLocked()); +} + +// Tests that a content::WebContents is able to change the orientation of the +// display after having locked rotation. +TEST_F(ScreenOrientationControllerTest, OrientationChanges) { + scoped_ptr content(CreateWebContents()); + ASSERT_NE(nullptr, content->GetNativeView()); + ASSERT_EQ(gfx::Display::ROTATE_0, Rotation()); + ASSERT_FALSE(RotationLocked()); + + delegate()->Lock(content.get(), blink::WebScreenOrientationLockPortrait); + EXPECT_EQ(gfx::Display::ROTATE_90, Rotation()); + EXPECT_TRUE(RotationLocked()); + + delegate()->Lock(content.get(), blink::WebScreenOrientationLockLandscape); + EXPECT_EQ(gfx::Display::ROTATE_0, Rotation()); +} + +// Tests that a user initiated rotation lock cannot be unlocked by a +// content::WebContents. +TEST_F(ScreenOrientationControllerTest, UserLockRejectsUnlock) { + delegate()->SetRotationLocked(true); + + scoped_ptr content(CreateWebContents()); + delegate()->Unlock(content.get()); + EXPECT_TRUE(RotationLocked()); +} + +// Tests that orientation can only be set by the first content::WebContents that +// has set a rotation lock. +TEST_F(ScreenOrientationControllerTest, SecondContentCannotChangeOrientation) { + scoped_ptr content1(CreateWebContents()); + scoped_ptr content2(CreateSecondaryWebContents()); + ASSERT_NE(content1->GetNativeView(), content2->GetNativeView()); + + delegate()->Lock(content1.get(), blink::WebScreenOrientationLockLandscape); + delegate()->Lock(content2.get(), blink::WebScreenOrientationLockPortrait); + EXPECT_EQ(gfx::Display::ROTATE_0, Rotation()); +} + +// Tests that only the content::WebContents that set a rotation lock can perform +// an unlock. +TEST_F(ScreenOrientationControllerTest, SecondContentCannotUnlock) { + scoped_ptr content1(CreateWebContents()); + scoped_ptr content2(CreateSecondaryWebContents()); + ASSERT_NE(content1->GetNativeView(), content2->GetNativeView()); + + delegate()->Lock(content1.get(), blink::WebScreenOrientationLockLandscape); + delegate()->Unlock(content2.get()); + EXPECT_TRUE(RotationLocked()); +} + +// Tests that alternate content::WebContents can set a rotation lock after a +// preexisting lock has been released. +TEST_F(ScreenOrientationControllerTest, AfterUnlockSecondContentCanLock) { + scoped_ptr content1(CreateWebContents()); + scoped_ptr content2(CreateSecondaryWebContents()); + ASSERT_NE(content1->GetNativeView(), content2->GetNativeView()); + + delegate()->Lock(content1.get(), blink::WebScreenOrientationLockLandscape); + delegate()->Unlock(content1.get()); + delegate()->Lock(content2.get(), blink::WebScreenOrientationLockPortrait); + EXPECT_EQ(gfx::Display::ROTATE_90, Rotation()); + EXPECT_TRUE(RotationLocked()); +} + +// Tests that accelerometer readings in each of the screen angles will trigger a +// rotation of the internal display. +TEST_F(ScreenOrientationControllerTest, DisplayRotation) { + EnableMaximizeMode(true); + // Now test rotating in all directions. + TriggerLidUpdate(gfx::Vector3dF(-kMeanGravity, 0.0f, 0.0f)); + EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); + TriggerLidUpdate(gfx::Vector3dF(0.0f, kMeanGravity, 0.0f)); + EXPECT_EQ(gfx::Display::ROTATE_180, GetInternalDisplayRotation()); + TriggerLidUpdate(gfx::Vector3dF(kMeanGravity, 0.0f, 0.0f)); + EXPECT_EQ(gfx::Display::ROTATE_270, GetInternalDisplayRotation()); + TriggerLidUpdate(gfx::Vector3dF(0.0f, -kMeanGravity, 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(ScreenOrientationControllerTest, RotationIgnoresLowAngles) { + EnableMaximizeMode(true); + TriggerLidUpdate(gfx::Vector3dF(0.0f, -kMeanGravity, -kMeanGravity)); + EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); + TriggerLidUpdate(gfx::Vector3dF(-2.0f, 0.0f, -kMeanGravity)); + EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); + TriggerLidUpdate(gfx::Vector3dF(0.0f, 2.0f, -kMeanGravity)); + EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); + TriggerLidUpdate(gfx::Vector3dF(2.0f, 0.0f, -kMeanGravity)); + EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); + TriggerLidUpdate(gfx::Vector3dF(0.0f, -2.0f, -kMeanGravity)); + 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(ScreenOrientationControllerTest, RotationSticky) { + EnableMaximizeMode(true); + gfx::Vector3dF gravity(0.0f, -kMeanGravity, 0.0f); + TriggerLidUpdate(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(-sin(degrees * kDegreesToRadians) * kMeanGravity); + gravity.set_y(-cos(degrees * kDegreesToRadians) * kMeanGravity); + TriggerLidUpdate(gravity); + EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); + + // 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(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(-sin(degrees * kDegreesToRadians) * kMeanGravity); + gravity.set_y(-cos(degrees * kDegreesToRadians) * kMeanGravity); + TriggerLidUpdate(gravity); + EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); +} + +// Tests that the display will stick to its current orientation when the +// rotation lock has been set. +TEST_F(ScreenOrientationControllerTest, RotationLockPreventsRotation) { + EnableMaximizeMode(true); + SetRotationLocked(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(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); + + SetRotationLocked(false); + TriggerLidUpdate(gravity); + EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); +} + +// The TrayDisplay 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) { + EnableMaximizeMode(true); + test::TestSystemTrayDelegate* tray_delegate = + static_cast( + Shell::GetInstance()->system_tray_delegate()); + tray_delegate->set_should_show_display_notification(true); + + 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 maximize mode + ASSERT_NE(gfx::Display::ROTATE_270, GetInternalDisplayRotation()); + SetInternalDisplayRotation(gfx::Display::ROTATE_270); + SetRotationLocked(false); + EXPECT_EQ(gfx::Display::ROTATE_270, GetInternalDisplayRotation()); + EXPECT_EQ(1u, message_center->NotificationCount()); + EXPECT_TRUE(message_center->HasPopupNotifications()); + + // Clear all notifications + message_center->RemoveAllNotifications(false); + 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 maximize mode + // Rotate the screen 90 degrees + ASSERT_NE(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); + TriggerLidUpdate(gfx::Vector3dF(-kMeanGravity, 0.0f, 0.0f)); + ASSERT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); + 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 maximize mode + EnableMaximizeMode(false); + // Reset the screen rotation. + SetInternalDisplayRotation(gfx::Display::ROTATE_0); + // Clear all notifications + message_center->RemoveAllNotifications(false); + ASSERT_NE(gfx::Display::ROTATE_180, GetInternalDisplayRotation()); + ASSERT_EQ(0u, message_center->NotificationCount()); + ASSERT_FALSE(message_center->HasPopupNotifications()); + SetInternalDisplayRotation(gfx::Display::ROTATE_180); + EXPECT_EQ(gfx::Display::ROTATE_180, GetInternalDisplayRotation()); + 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 maximize mode. +TEST_F(ScreenOrientationControllerTest, ResetUserRotationUponExit) { + SetInternalDisplayRotation(gfx::Display::ROTATE_90); + EnableMaximizeMode(true); + + TriggerLidUpdate(gfx::Vector3dF(0.0f, kMeanGravity, 0.0f)); + EXPECT_EQ(gfx::Display::ROTATE_180, GetInternalDisplayRotation()); + + EnableMaximizeMode(false); + EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); +} + +// Tests that if a user sets a display rotation that accelerometer rotation +// becomes locked. +TEST_F(ScreenOrientationControllerTest, + NonAccelerometerRotationChangesLockRotation) { + EnableMaximizeMode(true); + ASSERT_FALSE(RotationLocked()); + SetInternalDisplayRotation(gfx::Display::ROTATE_270); + EXPECT_TRUE(RotationLocked()); +} + +// Tests that if a user changes the display rotation, while rotation is locked, +// that the updates are recorded. Upon exiting maximize mode the latest user +// rotation should be applied. +TEST_F(ScreenOrientationControllerTest, UpdateUserRotationWhileRotationLocked) { + EnableMaximizeMode(true); + SetInternalDisplayRotation(gfx::Display::ROTATE_270); + // User sets rotation to the same rotation that the display was at when + // maximize mode was activated. + SetInternalDisplayRotation(gfx::Display::ROTATE_0); + EnableMaximizeMode(false); + EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); +} + +} // namespace ash diff --git a/ash/content/display/screen_orientation_delegate_chromeos.h b/ash/content/display/screen_orientation_delegate_chromeos.h deleted file mode 100644 index d1f445572d3e..000000000000 --- a/ash/content/display/screen_orientation_delegate_chromeos.h +++ /dev/null @@ -1,64 +0,0 @@ -// 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. - -#ifndef ASH_CONTENT_DISPLAY_SCREEN_ORIENTATION_DELEGATE_CHROMEOS_H_ -#define ASH_CONTENT_DISPLAY_SCREEN_ORIENTATION_DELEGATE_CHROMEOS_H_ - -#include "base/macros.h" -#include "content/public/browser/screen_orientation_delegate.h" -#include "third_party/WebKit/public/platform/WebScreenOrientationLockType.h" - -namespace aura { -class Window; -} - -namespace content { -class WebContents; -} - -namespace ash { - -// Implements ChromeOS specific functionality for ScreenOrientationProvider. -class ScreenOrientationDelegate : public content::ScreenOrientationDelegate { - public: - ScreenOrientationDelegate(); - virtual ~ScreenOrientationDelegate(); - - // content::ScreenOrientationDelegate: - bool FullScreenRequired(content::WebContents* web_contents) override; - void Lock(content::WebContents* web_contents, - blink::WebScreenOrientationLockType lock_orientation) override; - bool ScreenOrientationProviderSupported() override; - void Unlock(content::WebContents* web_contents) override; - - private: - // Locks rotation to the angle matching the primary orientation for - // |lock_orientation|. - void LockRotationToPrimaryOrientation( - blink::WebScreenOrientationLockType lock_orientation); - - // Locks rotation to the angle matching the secondary orientation for - // |lock_orientation|. - void LockRotationToSecondaryOrientation( - blink::WebScreenOrientationLockType lock_orientation); - - // For orientations that do not specify primary or secondary, locks to the - // current rotation if it matches |lock_orientation|. Otherwise locks to a - // matching rotation. - void LockToRotationMatchingOrientation( - blink::WebScreenOrientationLockType lock_orientation); - - // The window that has applied the current lock. No other window can apply a - // lock until the current window unlocks rotation. - aura::Window* locking_window_; - - // The orientation of the display when at a rotation of 0. - blink::WebScreenOrientationLockType natural_orientation_; - - DISALLOW_COPY_AND_ASSIGN(ScreenOrientationDelegate); -}; - -} // namespace ash - -#endif // ASH_CONTENT_DISPLAY_SCREEN_ORIENTATION_DELEGATE_CHROMEOS_H_ diff --git a/ash/content/display/screen_orientation_delegate_chromeos_unittest.cc b/ash/content/display/screen_orientation_delegate_chromeos_unittest.cc deleted file mode 100644 index 28d9bda1f507..000000000000 --- a/ash/content/display/screen_orientation_delegate_chromeos_unittest.cc +++ /dev/null @@ -1,194 +0,0 @@ -// 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/ash_switches.h" -#include "ash/content/display/screen_orientation_delegate_chromeos.h" -#include "ash/display/display_info.h" -#include "ash/display/display_manager.h" -#include "ash/shell.h" -#include "ash/test/ash_test_base.h" -#include "ash/test/ash_test_helper.h" -#include "ash/test/test_shell_delegate.h" -#include "ash/wm/maximize_mode/maximize_mode_controller.h" -#include "base/command_line.h" -#include "base/memory/scoped_ptr.h" -#include "content/public/browser/browser_context.h" -#include "content/public/browser/web_contents.h" -#include "content/public/test/test_browser_context.h" -#include "third_party/WebKit/public/platform/WebScreenOrientationLockType.h" -#include "ui/gfx/display.h" -#include "ui/views/test/webview_test_helper.h" -#include "ui/views/view.h" -#include "ui/views/views_delegate.h" - -namespace ash { - -namespace { - -gfx::Display::Rotation Rotation() { - return Shell::GetInstance() - ->display_manager() - ->GetDisplayInfo(gfx::Display::InternalDisplayId()) - .rotation(); -} - -bool RotationLocked() { - return Shell::GetInstance()->maximize_mode_controller()->rotation_locked(); -} - -} // namespace - -class ScreenOrientationDelegateTest : public test::AshTestBase { - public: - ScreenOrientationDelegateTest(); - virtual ~ScreenOrientationDelegateTest(); - - ScreenOrientationDelegate* delegate() { return screen_orientation_delegate_; } - - // Creates and initializes and empty content::WebContents that is backed by a - // content::BrowserContext and that has an aura::Window. - content::WebContents* CreateWebContents(); - - // Creates a secondary content::WebContents, with a separate - // content::BrowserContext. - content::WebContents* CreateSecondaryWebContents(); - - // test::AshTestBase: - void SetUp() override; - - private: - ScreenOrientationDelegate* screen_orientation_delegate_; - - // Optional content::BrowserContext used for two window tests. - scoped_ptr secondary_browser_context_; - - // Setups underlying content layer so that content::WebContents can be - // generated. - scoped_ptr webview_test_helper_; - - DISALLOW_COPY_AND_ASSIGN(ScreenOrientationDelegateTest); -}; - -ScreenOrientationDelegateTest::ScreenOrientationDelegateTest() { - webview_test_helper_.reset(new views::WebViewTestHelper()); -} - -ScreenOrientationDelegateTest::~ScreenOrientationDelegateTest() { -} - -content::WebContents* ScreenOrientationDelegateTest::CreateWebContents() { - return views::ViewsDelegate::views_delegate->CreateWebContents( - ash_test_helper()->test_shell_delegate()->GetActiveBrowserContext(), - nullptr); -} - -content::WebContents* -ScreenOrientationDelegateTest::CreateSecondaryWebContents() { - secondary_browser_context_.reset(new content::TestBrowserContext()); - return views::ViewsDelegate::views_delegate->CreateWebContents( - secondary_browser_context_.get(), nullptr); -} - -void ScreenOrientationDelegateTest::SetUp() { - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kAshUseFirstDisplayAsInternal); - base::CommandLine::ForCurrentProcess()->AppendSwitch( - switches::kAshEnableTouchViewTesting); - test::AshTestBase::SetUp(); - screen_orientation_delegate_ = - Shell::GetInstance()->screen_orientation_delegate(); -} - -// Tests that a content::WebContents can lock rotation. -TEST_F(ScreenOrientationDelegateTest, LockOrientation) { - scoped_ptr content(CreateWebContents()); - ASSERT_NE(nullptr, content->GetNativeView()); - ASSERT_EQ(gfx::Display::ROTATE_0, Rotation()); - ASSERT_FALSE(RotationLocked()); - - delegate()->Lock(content.get(), blink::WebScreenOrientationLockLandscape); - EXPECT_EQ(gfx::Display::ROTATE_0, Rotation()); - EXPECT_TRUE(RotationLocked()); -} - -// Tests that a content::WebContents can unlock rotation. -TEST_F(ScreenOrientationDelegateTest, Unlock) { - scoped_ptr content(CreateWebContents()); - ASSERT_NE(nullptr, content->GetNativeView()); - ASSERT_EQ(gfx::Display::ROTATE_0, Rotation()); - ASSERT_FALSE(RotationLocked()); - - delegate()->Lock(content.get(), blink::WebScreenOrientationLockLandscape); - EXPECT_EQ(gfx::Display::ROTATE_0, Rotation()); - EXPECT_TRUE(RotationLocked()); - - delegate()->Unlock(content.get()); - EXPECT_FALSE(RotationLocked()); -} - -// Tests that a content::WebContents is able to change the orientation of the -// display after having locked rotation. -TEST_F(ScreenOrientationDelegateTest, OrientationChanges) { - scoped_ptr content(CreateWebContents()); - ASSERT_NE(nullptr, content->GetNativeView()); - ASSERT_EQ(gfx::Display::ROTATE_0, Rotation()); - ASSERT_FALSE(RotationLocked()); - - delegate()->Lock(content.get(), blink::WebScreenOrientationLockPortrait); - EXPECT_EQ(gfx::Display::ROTATE_90, Rotation()); - EXPECT_TRUE(RotationLocked()); - - delegate()->Lock(content.get(), blink::WebScreenOrientationLockLandscape); - EXPECT_EQ(gfx::Display::ROTATE_0, Rotation()); -} - -// Tests that a user initiated rotation lock cannot be unlocked by a -// content::WebContents. -TEST_F(ScreenOrientationDelegateTest, UserLockRejectsUnlock) { - Shell::GetInstance()->maximize_mode_controller()->SetRotationLocked(true); - - scoped_ptr content(CreateWebContents()); - delegate()->Unlock(content.get()); - EXPECT_TRUE(RotationLocked()); -} - -// Tests that orientation can only be set by the first content::WebContents that -// has set a rotation lock. -TEST_F(ScreenOrientationDelegateTest, SecondContentCannotChangeOrientation) { - scoped_ptr content1(CreateWebContents()); - scoped_ptr content2(CreateSecondaryWebContents()); - ASSERT_NE(content1->GetNativeView(), content2->GetNativeView()); - - delegate()->Lock(content1.get(), blink::WebScreenOrientationLockLandscape); - delegate()->Lock(content2.get(), blink::WebScreenOrientationLockPortrait); - EXPECT_EQ(gfx::Display::ROTATE_0, Rotation()); -} - -// Tests that only the content::WebContents that set a rotation lock can perform -// an unlock. -TEST_F(ScreenOrientationDelegateTest, SecondContentCannotUnlock) { - scoped_ptr content1(CreateWebContents()); - scoped_ptr content2(CreateSecondaryWebContents()); - ASSERT_NE(content1->GetNativeView(), content2->GetNativeView()); - - delegate()->Lock(content1.get(), blink::WebScreenOrientationLockLandscape); - delegate()->Unlock(content2.get()); - EXPECT_TRUE(RotationLocked()); -} - -// Tests that alternate content::WebContents can set a rotation lock after a -// preexisting lock has been released. -TEST_F(ScreenOrientationDelegateTest, AfterUnlockSecondContentCanLock) { - scoped_ptr content1(CreateWebContents()); - scoped_ptr content2(CreateSecondaryWebContents()); - ASSERT_NE(content1->GetNativeView(), content2->GetNativeView()); - - delegate()->Lock(content1.get(), blink::WebScreenOrientationLockLandscape); - delegate()->Unlock(content1.get()); - delegate()->Lock(content2.get(), blink::WebScreenOrientationLockPortrait); - EXPECT_EQ(gfx::Display::ROTATE_90, Rotation()); - EXPECT_TRUE(RotationLocked()); -} - -} // namespace ash diff --git a/ash/shell.cc b/ash/shell.cc index aabbccecbbe2..0697699e5fa8 100644 --- a/ash/shell.cc +++ b/ash/shell.cc @@ -120,7 +120,7 @@ #include "ash/accelerators/magnifier_key_scroller.h" #include "ash/accelerators/spoken_feedback_toggler.h" #include "ash/ash_constants.h" -#include "ash/content/display/screen_orientation_delegate_chromeos.h" +#include "ash/content/display/screen_orientation_controller_chromeos.h" #include "ash/display/display_change_observer_chromeos.h" #include "ash/display/display_configurator_animation.h" #include "ash/display/display_error_observer_chromeos.h" @@ -485,9 +485,8 @@ void Shell::RemoveShellObserver(ShellObserver* observer) { #if defined(OS_CHROMEOS) bool Shell::ShouldSaveDisplaySettings() { - return !((maximize_mode_controller_->IsMaximizeModeWindowManagerEnabled() && - maximize_mode_controller_-> - ignore_display_configuration_updates()) || + return !(screen_orientation_controller_ + ->ignore_display_configuration_updates() || resolution_notification_controller_->DoesNotificationTimeout()); } #endif @@ -694,15 +693,19 @@ Shell::~Shell() { // TooltipController is deleted with the Shell so removing its references. RemovePreTargetHandler(tooltip_controller_.get()); +#if defined(OS_CHROMEOS) + screen_orientation_controller_.reset(); +#endif + // Destroy the virtual keyboard controller before the maximize mode controller // since the latters destructor triggers events that the former is listening // to but no longer cares about. #if defined(OS_CHROMEOS) virtual_keyboard_controller_.reset(); #endif + // Destroy maximize mode controller early on since it has some observers which // need to be removed. - maximize_mode_controller_->Shutdown(); maximize_mode_controller_.reset(); // AppList needs to be released before shelf layout manager, which is @@ -1066,7 +1069,7 @@ void Shell::Init(const ShellInitParams& init_params) { new VideoActivityNotifier(video_detector_.get())); bluetooth_notification_controller_.reset(new BluetoothNotificationController); last_window_closed_logout_reminder_.reset(new LastWindowClosedLogoutReminder); - screen_orientation_delegate_.reset(new ScreenOrientationDelegate()); + screen_orientation_controller_.reset(new ScreenOrientationController()); #endif // The compositor thread and main message loop have to be running in // order to create mirror window. Run it after the main message loop diff --git a/ash/shell.h b/ash/shell.h index c9f4eb0fe3a5..e9dd6ecbe98e 100644 --- a/ash/shell.h +++ b/ash/shell.h @@ -123,7 +123,7 @@ class ResolutionNotificationController; class RootWindowController; class ScopedTargetRootWindow; class ScreenAsh; -class ScreenOrientationDelegate; +class ScreenOrientationController; class ScreenPositionController; class SessionStateDelegate; class Shelf; @@ -540,8 +540,8 @@ class ASH_EXPORT Shell : public SystemModalContainerEventFilterDelegate, return logout_confirmation_controller_.get(); } - ScreenOrientationDelegate* screen_orientation_delegate() { - return screen_orientation_delegate_.get(); + ScreenOrientationController* screen_orientation_controller() { + return screen_orientation_controller_.get(); } VirtualKeyboardController* virtual_keyboard_controller() { @@ -736,8 +736,8 @@ class ASH_EXPORT Shell : public SystemModalContainerEventFilterDelegate, // Listens for output changes and updates the display manager. scoped_ptr display_change_observer_; - // Implements content::ScreenOrientationDelegate for ChromeOS - scoped_ptr screen_orientation_delegate_; + // Implements content::ScreenOrientationController for ChromeOS + scoped_ptr screen_orientation_controller_; scoped_ptr touch_transformer_controller_; diff --git a/ash/system/chromeos/rotation/tray_rotation_lock.cc b/ash/system/chromeos/rotation/tray_rotation_lock.cc index 36c2929949ea..dc2254d483b6 100644 --- a/ash/system/chromeos/rotation/tray_rotation_lock.cc +++ b/ash/system/chromeos/rotation/tray_rotation_lock.cc @@ -4,6 +4,7 @@ #include "ash/system/chromeos/rotation/tray_rotation_lock.h" +#include "ash/content/display/screen_orientation_controller_chromeos.h" #include "ash/shell.h" #include "ash/system/tray/system_tray.h" #include "ash/system/tray/tray_item_more.h" @@ -26,14 +27,14 @@ class RotationLockDefaultView : public TrayItemMore, public ShellObserver { public: explicit RotationLockDefaultView(SystemTrayItem* owner); - virtual ~RotationLockDefaultView(); + ~RotationLockDefaultView() override; // ActionableView: - virtual bool PerformAction(const ui::Event& event) override; + bool PerformAction(const ui::Event& event) override; // ShellObserver: - virtual void OnMaximizeModeStarted() override; - virtual void OnMaximizeModeEnded() override; + void OnMaximizeModeStarted() override; + void OnMaximizeModeEnded() override; private: void UpdateImage(); @@ -54,10 +55,10 @@ RotationLockDefaultView::~RotationLockDefaultView() { } bool RotationLockDefaultView::PerformAction(const ui::Event& event) { - MaximizeModeController* maximize_mode_controller = Shell::GetInstance()-> - maximize_mode_controller(); - maximize_mode_controller->SetRotationLocked( - !maximize_mode_controller->rotation_locked()); + ScreenOrientationController* screen_orientation_controller = + Shell::GetInstance()->screen_orientation_controller(); + screen_orientation_controller->SetRotationLocked( + !screen_orientation_controller->rotation_locked()); UpdateImage(); return true; } @@ -74,7 +75,9 @@ void RotationLockDefaultView::OnMaximizeModeEnded() { void RotationLockDefaultView::UpdateImage() { base::string16 label; ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); - if (Shell::GetInstance()->maximize_mode_controller()->rotation_locked()) { + if (Shell::GetInstance() + ->screen_orientation_controller() + ->rotation_locked()) { SetImage(bundle.GetImageNamed( IDR_AURA_UBER_TRAY_AUTO_ROTATION_LOCKED_DARK).ToImageSkia()); label = l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ROTATION_LOCK_LOCKED); @@ -121,13 +124,13 @@ views::View* TrayRotationLock::CreateDefaultView(user::LoginStatus status) { void TrayRotationLock::OnMaximizeModeStarted() { tray_view()->SetVisible( - Shell::GetInstance()->maximize_mode_controller()->rotation_locked()); - Shell::GetInstance()->maximize_mode_controller()->AddObserver(this); + Shell::GetInstance()->screen_orientation_controller()->rotation_locked()); + Shell::GetInstance()->screen_orientation_controller()->AddObserver(this); } void TrayRotationLock::OnMaximizeModeEnded() { tray_view()->SetVisible(false); - Shell::GetInstance()->maximize_mode_controller()->RemoveObserver(this); + Shell::GetInstance()->screen_orientation_controller()->RemoveObserver(this); } bool TrayRotationLock::GetInitialVisibility() { @@ -135,11 +138,13 @@ bool TrayRotationLock::GetInitialVisibility() { } bool TrayRotationLock::ShouldBeVisible() { - MaximizeModeController* controller = Shell::GetInstance()-> - maximize_mode_controller(); return on_primary_display_ && - controller->IsMaximizeModeWindowManagerEnabled() && - controller->rotation_locked(); + Shell::GetInstance() + ->maximize_mode_controller() + ->IsMaximizeModeWindowManagerEnabled() && + Shell::GetInstance() + ->screen_orientation_controller() + ->rotation_locked(); } } // namespace ash diff --git a/ash/system/chromeos/rotation/tray_rotation_lock.h b/ash/system/chromeos/rotation/tray_rotation_lock.h index f4f4fdeb4887..b9d78841f1f8 100644 --- a/ash/system/chromeos/rotation/tray_rotation_lock.h +++ b/ash/system/chromeos/rotation/tray_rotation_lock.h @@ -5,9 +5,9 @@ #ifndef ASH_SYSTEM_CHROMEOS_ROTATION_TRAY_ROTATION_LOCK_H_ #define ASH_SYSTEM_CHROMEOS_ROTATION_TRAY_ROTATION_LOCK_H_ +#include "ash/content/display/screen_orientation_controller_chromeos.h" #include "ash/shell_observer.h" #include "ash/system/tray/tray_image_item.h" -#include "ash/wm/maximize_mode/maximize_mode_controller.h" namespace ash { @@ -20,26 +20,27 @@ class RotationLockDefaultView; // the rotation lock for the display which it appears on. The default view can // be interacted with, it toggles the state of the rotation lock. // TrayRotationLock is only available on the primary display. -class ASH_EXPORT TrayRotationLock : public TrayImageItem, - public MaximizeModeController::Observer, - public ShellObserver { +class ASH_EXPORT TrayRotationLock + : public TrayImageItem, + public ScreenOrientationController::Observer, + public ShellObserver { public: explicit TrayRotationLock(SystemTray* system_tray); - virtual ~TrayRotationLock(); + ~TrayRotationLock() override; - // MaximizeModeController::Observer: - virtual void OnRotationLockChanged(bool rotation_locked) override; + // ScreenOrientationController::Observer: + void OnRotationLockChanged(bool rotation_locked) override; // SystemTrayItem: - virtual views::View* CreateDefaultView(user::LoginStatus status) override; + views::View* CreateDefaultView(user::LoginStatus status) override; // ShellObserver: - virtual void OnMaximizeModeStarted() override; - virtual void OnMaximizeModeEnded() override; + void OnMaximizeModeStarted() override; + void OnMaximizeModeEnded() override; protected: // TrayImageItem: - virtual bool GetInitialVisibility() override; + bool GetInitialVisibility() override; private: friend class TrayRotationLockTest; diff --git a/ash/system/chromeos/rotation/tray_rotation_lock_unittest.cc b/ash/system/chromeos/rotation/tray_rotation_lock_unittest.cc index c043a065e148..246772d8d0bf 100644 --- a/ash/system/chromeos/rotation/tray_rotation_lock_unittest.cc +++ b/ash/system/chromeos/rotation/tray_rotation_lock_unittest.cc @@ -5,6 +5,7 @@ #include "ash/system/chromeos/rotation/tray_rotation_lock.h" #include "ash/ash_switches.h" +#include "ash/content/display/screen_orientation_controller_chromeos.h" #include "ash/display/display_manager.h" #include "ash/root_window_controller.h" #include "ash/shelf/shelf_widget.h" @@ -53,8 +54,8 @@ class TrayRotationLockTest : public test::AshTestBase { void TearDownViews(); // test::AshTestBase: - virtual void SetUp() override; - virtual void TearDown() override; + void SetUp() override; + void TearDown() override; private: scoped_ptr tray_; @@ -118,7 +119,8 @@ TEST_F(TrayRotationLockTest, CreateTrayViewDuringMaximizeModeAndRotationLock) { TearDownViews(); Shell::GetInstance()->maximize_mode_controller()-> EnableMaximizeModeWindowManager(true); - Shell::GetInstance()-> maximize_mode_controller()->SetRotationLocked(true); + Shell::GetInstance()->screen_orientation_controller()->SetRotationLocked( + true); SetUpForStatusAreaWidget(StatusAreaWidgetTestHelper::GetStatusAreaWidget()); EXPECT_TRUE(tray_view()->visible()); Shell::GetInstance()->maximize_mode_controller()-> @@ -132,7 +134,8 @@ TEST_F(TrayRotationLockTest, TrayViewVisibilityChangesDuringMaximizeMode) { ASSERT_FALSE(tray_view()->visible()); Shell::GetInstance()->maximize_mode_controller()-> EnableMaximizeModeWindowManager(true); - Shell::GetInstance()->maximize_mode_controller()->SetRotationLocked(true); + Shell::GetInstance()->screen_orientation_controller()->SetRotationLocked( + true); EXPECT_TRUE(tray_view()->visible()); Shell::GetInstance()->maximize_mode_controller()-> EnableMaximizeModeWindowManager(false); @@ -204,19 +207,19 @@ TEST_F(TrayRotationLockTest, CreateSecondaryDefaultView) { TEST_F(TrayRotationLockTest, PerformActionOnDefaultView) { MaximizeModeController* maximize_mode_controller = Shell::GetInstance()-> maximize_mode_controller(); - ASSERT_FALSE(maximize_mode_controller->rotation_locked()); - Shell::GetInstance()->maximize_mode_controller()-> - EnableMaximizeModeWindowManager(true); + ScreenOrientationController* screen_orientation_controller = + Shell::GetInstance()->screen_orientation_controller(); + ASSERT_FALSE(screen_orientation_controller->rotation_locked()); + maximize_mode_controller->EnableMaximizeModeWindowManager(true); ASSERT_FALSE(tray_view()->visible()); ui::GestureEvent tap( 0, 0, 0, base::TimeDelta(), ui::GestureEventDetails(ui::ET_GESTURE_TAP)); default_view()->OnGestureEvent(&tap); - EXPECT_TRUE(maximize_mode_controller->rotation_locked()); + EXPECT_TRUE(screen_orientation_controller->rotation_locked()); EXPECT_TRUE(tray_view()->visible()); - Shell::GetInstance()->maximize_mode_controller()-> - EnableMaximizeModeWindowManager(false); + maximize_mode_controller->EnableMaximizeModeWindowManager(false); } } // namespace ash diff --git a/ash/system/chromeos/tray_display.cc b/ash/system/chromeos/tray_display.cc index 3de751e7b728..4612450af5c8 100644 --- a/ash/system/chromeos/tray_display.cc +++ b/ash/system/chromeos/tray_display.cc @@ -4,6 +4,9 @@ #include "ash/system/chromeos/tray_display.h" +#include + +#include "ash/content/display/screen_orientation_controller_chromeos.h" #include "ash/display/display_controller.h" #include "ash/display/display_manager.h" #include "ash/shell.h" @@ -14,7 +17,6 @@ #include "ash/system/tray/system_tray_delegate.h" #include "ash/system/tray/tray_constants.h" #include "ash/system/tray/tray_notification_view.h" -#include "ash/wm/maximize_mode/maximize_mode_controller.h" #include "base/bind.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" @@ -168,8 +170,8 @@ class DisplayView : public ActionableView { const views::Label* label() const { return label_; } // Overridden from views::View. - virtual bool GetTooltipText(const gfx::Point& p, - base::string16* tooltip) const override { + bool GetTooltipText(const gfx::Point& p, + base::string16* tooltip) const override { base::string16 tray_message = GetTrayDisplayMessage(NULL); base::string16 display_message = GetAllDisplayInfo(); if (tray_message.empty() && display_message.empty()) @@ -268,12 +270,12 @@ class DisplayView : public ActionableView { } // Overridden from ActionableView. - virtual bool PerformAction(const ui::Event& event) override { + bool PerformAction(const ui::Event& event) override { OpenSettings(); return true; } - virtual void OnBoundsChanged(const gfx::Rect& previous_bounds) override { + void OnBoundsChanged(const gfx::Rect& previous_bounds) override { int label_max_width = bounds().width() - kTrayPopupPaddingHorizontal * 2 - kTrayPopupPaddingBetweenItems - image_->GetPreferredSize().width(); label_->SizeToFit(label_max_width); @@ -379,8 +381,9 @@ void TrayDisplay::CreateOrUpdateNotification( // Don't display notifications for accelerometer triggered screen rotations. // See http://crbug.com/364949 - if (Shell::GetInstance()->maximize_mode_controller()-> - ignore_display_configuration_updates()) { + if (Shell::GetInstance() + ->screen_orientation_controller() + ->ignore_display_configuration_updates()) { return; } diff --git a/ash/wm/maximize_mode/maximize_mode_controller.cc b/ash/wm/maximize_mode/maximize_mode_controller.cc index 511df7b94d65..388f350ec6ca 100644 --- a/ash/wm/maximize_mode/maximize_mode_controller.cc +++ b/ash/wm/maximize_mode/maximize_mode_controller.cc @@ -11,7 +11,6 @@ #include "ash/shell.h" #include "ash/wm/maximize_mode/maximize_mode_window_manager.h" #include "ash/wm/maximize_mode/scoped_disable_internal_mouse_and_keyboard.h" -#include "base/auto_reset.h" #include "base/command_line.h" #include "base/metrics/histogram.h" #include "base/time/default_tick_clock.h" @@ -63,13 +62,6 @@ const base::TimeDelta kLidRecentlyOpenedDuration = const float kHingeAngleDetectionThreshold = 2.5f; #if defined(OS_CHROMEOS) -// The mean acceleration due to gravity on Earth in m/s^2. -const float kMeanGravity = 9.80665f; - -// The maximum deviation from the acceleration expected due to gravity under -// which to detect hinge angle and screen rotation in m/s^2 -const float kDeviationFromGravityThreshold = 1.0f; - // The maximum deviation between the magnitude of the two accelerometers under // which to detect hinge angle and screen rotation in m/s^2. These // accelerometers are attached to the same physical device and so should be @@ -77,51 +69,11 @@ const float kDeviationFromGravityThreshold = 1.0f; const float kNoisyMagnitudeDeviation = 1.0f; #endif -// 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 m/s^2 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. The value is -// effectively the sine of the rise angle required times the acceleration due -// to gravity, with the current value requiring at least a 25 degree rise. -const float kMinimumAccelerationScreenRotation = 4.2f; - -const float kRadiansToDegrees = 180.0f / 3.14159265f; - -// Returns the angle between |base| and |other| in degrees. -float AngleBetweenVectorsInDegrees(const gfx::Vector3dF& base, - const gfx::Vector3dF& other) { - return acos(gfx::DotProduct(base, other) / - base.Length() / other.Length()) * kRadiansToDegrees; -} - -// Returns the clockwise angle between |base| and |other| where |normal| is the -// normal of the virtual surface to measure clockwise according to. -float ClockwiseAngleBetweenVectorsInDegrees(const gfx::Vector3dF& base, - const gfx::Vector3dF& other, - const gfx::Vector3dF& normal) { - float angle = AngleBetweenVectorsInDegrees(base, other); - gfx::Vector3dF cross(base); - cross.Cross(other); - // If the dot product of this cross product is normal, it means that the - // shortest angle between |base| and |other| was counterclockwise with respect - // to the surface represented by |normal| and this angle must be reversed. - if (gfx::DotProduct(cross, normal) > 0.0f) - angle = 360.0f - angle; - return angle; -} - } // namespace MaximizeModeController::MaximizeModeController() - : rotation_locked_(false), - have_seen_accelerometer_data_(false), - ignore_display_configuration_updates_(false), + : have_seen_accelerometer_data_(false), lid_open_past_180_(false), - shutting_down_(false), - user_rotation_(gfx::Display::ROTATE_0), last_touchview_transition_time_(base::Time::Now()), tick_clock_(new base::DefaultTickClock()), lid_is_closed_(false) { @@ -142,31 +94,6 @@ MaximizeModeController::~MaximizeModeController() { #endif // OS_CHROMEOS } -void MaximizeModeController::SetRotationLocked(bool rotation_locked) { - if (rotation_locked_ == rotation_locked) - return; - base::AutoReset auto_ignore_display_configuration_updates( - &ignore_display_configuration_updates_, true); - rotation_locked_ = rotation_locked; - Shell::GetInstance()->display_manager()-> - RegisterDisplayRotationProperties(rotation_locked_, current_rotation_); - FOR_EACH_OBSERVER(Observer, observers_, - OnRotationLockChanged(rotation_locked_)); -} - -void MaximizeModeController::LockRotation(gfx::Display::Rotation rotation) { - SetRotationLocked(true); - SetDisplayRotation(Shell::GetInstance()->display_manager(), rotation); -} - -void MaximizeModeController::AddObserver(Observer* observer) { - observers_.AddObserver(observer); -} - -void MaximizeModeController::RemoveObserver(Observer* observer) { - observers_.RemoveObserver(observer); -} - bool MaximizeModeController::CanEnterMaximizeMode() { // If we have ever seen accelerometer data, then HandleHingeRotation may // trigger maximize mode at some point in the future. @@ -198,72 +125,34 @@ void MaximizeModeController::AddWindow(aura::Window* window) { maximize_mode_window_manager_->AddWindow(window); } -void MaximizeModeController::Shutdown() { - shutting_down_ = true; - LeaveMaximizeMode(); -} - -void MaximizeModeController::OnDisplayConfigurationChanged() { - if (ignore_display_configuration_updates_) - return; - DisplayManager* display_manager = Shell::GetInstance()->display_manager(); - gfx::Display::Rotation user_rotation = - display_manager->GetDisplayInfo(gfx::Display::InternalDisplayId()) - .rotation(); - if (user_rotation != current_rotation_) { - // A user may change other display configuration settings. When the user - // does change the rotation setting, then lock rotation to prevent the - // accelerometer from erasing their change. - SetRotationLocked(true); - user_rotation_ = user_rotation; - current_rotation_ = user_rotation; - } -} - #if defined(OS_CHROMEOS) void MaximizeModeController::OnAccelerometerUpdated( const ui::AccelerometerUpdate& update) { bool first_accelerometer_update = !have_seen_accelerometer_data_; have_seen_accelerometer_data_ = true; - // Ignore the reading if it appears unstable. The reading is considered - // unstable if it deviates too much from gravity and/or the magnitude of the - // reading from the lid differs too much from the reading from the base. - float lid_magnitude = update.has(ui::ACCELEROMETER_SOURCE_SCREEN) ? - update.get(ui::ACCELEROMETER_SOURCE_SCREEN).Length() : 0.0f; - bool lid_stable = update.has(ui::ACCELEROMETER_SOURCE_SCREEN) && - std::abs(lid_magnitude - kMeanGravity) <= kDeviationFromGravityThreshold; + if (!update.has(ui::ACCELEROMETER_SOURCE_SCREEN)) + return; // Whether or not we enter maximize mode affects whether we handle screen // rotation, so determine whether to enter maximize mode first. if (!update.has(ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD)) { - if (first_accelerometer_update && - update.has(ui::ACCELEROMETER_SOURCE_SCREEN)) { + if (first_accelerometer_update) EnterMaximizeMode(); - } - } else { // update.has(ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD) - float base_magnitude = - update.get(ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD).Length(); - bool base_angle_stable = lid_stable && - std::abs(base_magnitude - lid_magnitude) <= kNoisyMagnitudeDeviation && - std::abs(base_magnitude - kMeanGravity) <= - kDeviationFromGravityThreshold; - if (base_angle_stable) { - HandleHingeRotation( - update.get(ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD), - update.get(ui::ACCELEROMETER_SOURCE_SCREEN)); - } - } - - if (lid_stable) - HandleScreenRotation(update.get(ui::ACCELEROMETER_SOURCE_SCREEN)); - - if (first_accelerometer_update) { - // On the first accelerometer update we will know if we have entered - // maximize mode or not. Update the preferences to reflect the current - // state. - Shell::GetInstance()->display_manager()-> - RegisterDisplayRotationProperties(rotation_locked_, current_rotation_); + } else if (chromeos::AccelerometerReader::IsReadingStable( + update, ui::ACCELEROMETER_SOURCE_SCREEN) && + chromeos::AccelerometerReader::IsReadingStable( + update, ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD) && + std::abs(update.get(ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD) + .Length() - + update.get(ui::ACCELEROMETER_SOURCE_SCREEN).Length()) <= + kNoisyMagnitudeDeviation) { + // update.has(ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD) + // Ignore the reading if it appears unstable. The reading is considered + // unstable if it deviates too much from gravity and/or the magnitude of the + // reading from the lid differs too much from the reading from the base. + HandleHingeRotation(update.get(ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD), + update.get(ui::ACCELEROMETER_SOURCE_SCREEN)); } } @@ -304,8 +193,8 @@ void MaximizeModeController::HandleHingeRotation(const gfx::Vector3dF& base, } // Compute the angle between the base and the lid. - float lid_angle = 180.0f - ClockwiseAngleBetweenVectorsInDegrees( - base_flattened, lid_flattened, hinge_vector); + float lid_angle = 180.0f - gfx::ClockwiseAngleBetweenVectorsInDegrees( + base_flattened, lid_flattened, hinge_vector); if (lid_angle < 0.0f) lid_angle += 360.0f; @@ -341,106 +230,16 @@ void MaximizeModeController::HandleHingeRotation(const gfx::Vector3dF& base, } } -void MaximizeModeController::HandleScreenRotation(const gfx::Vector3dF& lid) { - bool maximize_mode_engaged = IsMaximizeModeWindowManagerEnabled(); - - // TODO(jonross): track the updated rotation angle even when locked. So that - // when rotation lock is removed the accelerometer rotation can be applied - // without waiting for the next update. - if (!maximize_mode_engaged || rotation_locked_) - return; - - DisplayManager* display_manager = - Shell::GetInstance()->display_manager(); - gfx::Display::Rotation current_rotation = display_manager->GetDisplayInfo( - gfx::Display::InternalDisplayId()).rotation(); - - // 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 const 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_y(-1.0f); - else if (current_rotation == gfx::Display::ROTATE_90) - down.set_x(-1.0f); - else if (current_rotation == gfx::Display::ROTATE_180) - down.set_y(1.0f); - else - down.set_x(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; - - if (new_rotation != current_rotation) - SetDisplayRotation(display_manager, new_rotation); -} - -void MaximizeModeController::SetDisplayRotation( - DisplayManager* display_manager, - gfx::Display::Rotation rotation) { - base::AutoReset auto_ignore_display_configuration_updates( - &ignore_display_configuration_updates_, true); - current_rotation_ = rotation; - display_manager->SetDisplayRotation(gfx::Display::InternalDisplayId(), - rotation); -} - void MaximizeModeController::EnterMaximizeMode() { if (IsMaximizeModeWindowManagerEnabled()) return; - DisplayManager* display_manager = Shell::GetInstance()->display_manager(); - if (display_manager->HasInternalDisplay()) { - current_rotation_ = user_rotation_ = display_manager-> - GetDisplayInfo(gfx::Display::InternalDisplayId()).rotation(); - LoadDisplayRotationProperties(); - } EnableMaximizeModeWindowManager(true); - Shell::GetInstance()->display_controller()->AddObserver(this); } void MaximizeModeController::LeaveMaximizeMode() { if (!IsMaximizeModeWindowManagerEnabled()) return; - DisplayManager* display_manager = Shell::GetInstance()->display_manager(); - if (display_manager->HasInternalDisplay()) { - gfx::Display::Rotation current_rotation = display_manager-> - GetDisplayInfo(gfx::Display::InternalDisplayId()).rotation(); - if (current_rotation != user_rotation_) - SetDisplayRotation(display_manager, user_rotation_); - } - if (!shutting_down_) - SetRotationLocked(false); EnableMaximizeModeWindowManager(false); - Shell::GetInstance()->display_controller()->RemoveObserver(this); } // Called after maximize mode has started, windows might still animate though. @@ -469,16 +268,6 @@ void MaximizeModeController::RecordTouchViewStateTransition() { } } -void MaximizeModeController::LoadDisplayRotationProperties() { - DisplayManager* display_manager = Shell::GetInstance()->display_manager(); - if (!display_manager->registered_internal_display_rotation_lock()) - return; - - SetDisplayRotation(display_manager, - display_manager->registered_internal_display_rotation()); - SetRotationLocked(true); -} - void MaximizeModeController::OnAppTerminating() { if (CanEnterMaximizeMode()) { RecordTouchViewStateTransition(); @@ -495,7 +284,6 @@ void MaximizeModeController::OnAppTerminating() { 100 * total_touchview_time_.InSeconds() / total_runtime.InSeconds()); } } - Shell::GetInstance()->display_controller()->RemoveObserver(this); } bool MaximizeModeController::WasLidOpenedRecently() const { diff --git a/ash/wm/maximize_mode/maximize_mode_controller.h b/ash/wm/maximize_mode/maximize_mode_controller.h index 1df8db642d32..5e0a9e9708bb 100644 --- a/ash/wm/maximize_mode/maximize_mode_controller.h +++ b/ash/wm/maximize_mode/maximize_mode_controller.h @@ -6,12 +6,10 @@ #define ASH_WM_MAXIMIZE_MODE_MAXIMIZE_MODE_CONTROLLER_H_ #include "ash/ash_export.h" -#include "ash/display/display_controller.h" -#include "ash/display/display_manager.h" #include "ash/shell_observer.h" #include "base/macros.h" #include "base/memory/scoped_ptr.h" -#include "base/observer_list.h" +#include "base/time/time.h" #include "ui/gfx/display.h" #if defined(OS_CHROMEOS) @@ -23,6 +21,10 @@ namespace base { class TickClock; } +namespace gfx { +class Vector3dF; +} + namespace ui { class EventHandler; } @@ -41,51 +43,16 @@ class VirtualKeyboardControllerTest; // MaximizeModeController listens to accelerometer events and automatically // enters and exits maximize mode when the lid is opened beyond the triggering // angle and rotates the display to match the device when in maximize mode. -class ASH_EXPORT MaximizeModeController - : public ShellObserver, +class ASH_EXPORT MaximizeModeController : #if defined(OS_CHROMEOS) - public chromeos::AccelerometerReader::Observer, - public chromeos::PowerManagerClient::Observer, + public chromeos::AccelerometerReader::Observer, + public chromeos::PowerManagerClient::Observer, #endif // OS_CHROMEOS - public DisplayController::Observer { + public ShellObserver { public: - // Observer that reports changes to the state of MaximizeModeController's - // rotation lock. - class Observer { - public: - // Invoked whenever |rotation_locked_| is changed. - virtual void OnRotationLockChanged(bool rotation_locked) {} - - protected: - virtual ~Observer() {} - }; - MaximizeModeController(); ~MaximizeModeController() override; - bool ignore_display_configuration_updates() const { - return ignore_display_configuration_updates_; - } - - // True if |rotation_lock_| has been set, and OnAccelerometerUpdated will not - // change the display rotation. - bool rotation_locked() { - return rotation_locked_; - } - - // If |rotation_locked| future calls to OnAccelerometerUpdated will not - // change the display rotation. - void SetRotationLocked(bool rotation_locked); - - // Sets the display rotation to |rotation| and prevents future calls to - // OnAccelerometerUpdated from changing the rotation. SetRotationLocked(false) - // removes the rotation lock. - void LockRotation(gfx::Display::Rotation rotation); - - // Add/Remove observers. - void AddObserver(Observer* observer); - void RemoveObserver(Observer* observer); - // True if it is possible to enter maximize mode in the current // configuration. If this returns false, it should never be the case that // maximize mode becomes enabled. @@ -107,21 +74,11 @@ class ASH_EXPORT MaximizeModeController // If the maximize mode is not enabled no action will be performed. void AddWindow(aura::Window* window); - // TODO(jonross): move this into the destructor. Currently separated as - // ShellOberver notifies of maximize mode ending, and the observers end up - // attempting to access MaximizeModeController via the Shell. If done in - // destructor the controller is null, and the observers segfault. - // Shuts down down the MaximizeModeWindowManager and notifies all observers. - void Shutdown(); - // ShellObserver: void OnAppTerminating() override; void OnMaximizeModeStarted() override; void OnMaximizeModeEnded() override; - // DisplayController::Observer: - void OnDisplayConfigurationChanged() override; - #if defined(OS_CHROMEOS) // chromeos::AccelerometerReader::Observer: void OnAccelerometerUpdated(const ui::AccelerometerUpdate& update) override; @@ -147,14 +104,6 @@ class ASH_EXPORT MaximizeModeController 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); - - // Sets the display rotation and suppresses display notifications. - void SetDisplayRotation(DisplayManager* display_manager, - gfx::Display::Rotation rotation); - // Returns true if the lid was recently opened. bool WasLidOpenedRecently() const; @@ -169,10 +118,6 @@ class ASH_EXPORT MaximizeModeController // Record UMA stats tracking touchview usage. void RecordTouchViewStateTransition(); - // Checks DisplayManager for registered rotation lock, and rotation, - // preferences. These are then applied. - void LoadDisplayRotationProperties(); - // The maximized window manager (if enabled). scoped_ptr maximize_mode_window_manager_; @@ -180,36 +125,12 @@ class ASH_EXPORT MaximizeModeController // internal keyboard and touchpad. scoped_ptr event_blocker_; - // When true calls to OnAccelerometerUpdated will not rotate the display. - bool rotation_locked_; - // Whether we have ever seen accelerometer data. bool have_seen_accelerometer_data_; - // True when changes being applied cause OnDisplayConfigurationChanged() to be - // called, and for which these changes should be ignored. - bool ignore_display_configuration_updates_; - // True when the hinge angle has been detected past 180 degrees. bool lid_open_past_180_; - // True when Shutdown has been called. When shutting down the non maximize - // mode state should be restored, however user preferences should not be - // altered. - bool shutting_down_; - - // The rotation of the display set by the user. This rotation will be - // restored upon exiting maximize mode. - gfx::Display::Rotation user_rotation_; - - // The current rotation set by MaximizeModeController for the internal - // display. Compared in OnDisplayConfigurationChanged to determine user - // display setting changes. - gfx::Display::Rotation current_rotation_; - - // Rotation Lock observers. - ObserverList observers_; - // Tracks time spent in (and out of) touchview mode. base::Time last_touchview_transition_time_; base::TimeDelta total_touchview_time_; diff --git a/ash/wm/maximize_mode/maximize_mode_controller_unittest.cc b/ash/wm/maximize_mode/maximize_mode_controller_unittest.cc index 30d6246b9a9e..40ce4b2c76ad 100644 --- a/ash/wm/maximize_mode/maximize_mode_controller_unittest.cc +++ b/ash/wm/maximize_mode/maximize_mode_controller_unittest.cc @@ -296,130 +296,6 @@ TEST_F(MaximizeModeControllerTest, HingeAligned) { EXPECT_TRUE(IsMaximizeModeStarted()); } -// Tests that only getting a lid accelerometer update will enter maximize -// mode and rotate the screen. -TEST_F(MaximizeModeControllerTest, LidOnlyDisplayRotation) { - ASSERT_FALSE(IsMaximizeModeStarted()); - // Test rotating in all directions. - TriggerLidUpdate(gfx::Vector3dF(-kMeanGravity, 0.0f, 0.0f)); - // The first update should have entered maximize mode. - ASSERT_TRUE(IsMaximizeModeStarted()); - EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); - TriggerLidUpdate(gfx::Vector3dF(0.0f, kMeanGravity, 0.0f)); - EXPECT_EQ(gfx::Display::ROTATE_180, GetInternalDisplayRotation()); - TriggerLidUpdate(gfx::Vector3dF(kMeanGravity, 0.0f, 0.0f)); - EXPECT_EQ(gfx::Display::ROTATE_270, GetInternalDisplayRotation()); - TriggerLidUpdate(gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f)); - EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); -} - -// 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. - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravity), - gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f)); - ASSERT_TRUE(IsMaximizeModeStarted()); - - // Now test rotating in all directions. - TriggerBaseAndLidUpdate(gfx::Vector3dF(-kMeanGravity, 0.0f, 0.0f), - gfx::Vector3dF(-kMeanGravity, 0.0f, 0.0f)); - EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f), - gfx::Vector3dF(0.0f, kMeanGravity, 0.0f)); - EXPECT_EQ(gfx::Display::ROTATE_180, GetInternalDisplayRotation()); - TriggerBaseAndLidUpdate(gfx::Vector3dF(kMeanGravity, 0.0f, 0.0f), - gfx::Vector3dF(kMeanGravity, 0.0f, 0.0f)); - EXPECT_EQ(gfx::Display::ROTATE_270, GetInternalDisplayRotation()); - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, kMeanGravity, 0.0f), - gfx::Vector3dF(0.0f, -kMeanGravity, 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. - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravity), - gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f)); - ASSERT_TRUE(IsMaximizeModeStarted()); - - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, kMeanGravity, kMeanGravity), - gfx::Vector3dF(0.0f, -kMeanGravity, -kMeanGravity)); - EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); - TriggerBaseAndLidUpdate(gfx::Vector3dF(-2.0f, 0.0f, kMeanGravity), - gfx::Vector3dF(-2.0f, 0.0f, -kMeanGravity)); - EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, -2.0f, kMeanGravity), - gfx::Vector3dF(0.0f, 2.0f, -kMeanGravity)); - EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); - TriggerBaseAndLidUpdate(gfx::Vector3dF(2.0f, 0.0f, kMeanGravity), - gfx::Vector3dF(2.0f, 0.0f, -kMeanGravity)); - EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 2.0f, kMeanGravity), - gfx::Vector3dF(0.0f, -2.0f, -kMeanGravity)); - 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. - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravity), - gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f)); - ASSERT_TRUE(IsMaximizeModeStarted()); - - gfx::Vector3dF gravity(0.0f, -kMeanGravity, 0.0f); - TriggerBaseAndLidUpdate(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(-sin(degrees * kDegreesToRadians) * kMeanGravity); - gravity.set_y(-cos(degrees * kDegreesToRadians) * kMeanGravity); - TriggerBaseAndLidUpdate(gravity, gravity); - EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); - - // Turn more and the screen should rotate. - degrees = 70.0; - gravity.set_x(-sin(degrees * kDegreesToRadians) * kMeanGravity); - gravity.set_y(-cos(degrees * kDegreesToRadians) * kMeanGravity); - TriggerBaseAndLidUpdate(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(-sin(degrees * kDegreesToRadians) * kMeanGravity); - gravity.set_y(-cos(degrees * kDegreesToRadians) * kMeanGravity); - TriggerBaseAndLidUpdate(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. - TriggerBaseAndLidUpdate(gfx::Vector3dF(-9.5f, 0.0f, -3.5f), - gfx::Vector3dF(-9.5f, -3.5f, 0.0f)); - ASSERT_FALSE(IsMaximizeModeStarted()); - EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); - - // Open lid, screen should now rotate to match orientation. - TriggerBaseAndLidUpdate(gfx::Vector3dF(-9.5f, 0.0f, 3.5f), - gfx::Vector3dF(-9.5f, -3.5f, 0.0f)); - ASSERT_TRUE(IsMaximizeModeStarted()); - EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); - - // Close lid back to 90, screen should rotate back. - TriggerBaseAndLidUpdate(gfx::Vector3dF(-9.5f, 0.0f, -3.5f), - gfx::Vector3dF(-9.5f, -3.5f, 0.0f)); - ASSERT_FALSE(IsMaximizeModeStarted()); - EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); -} - - TEST_F(MaximizeModeControllerTest, LaptopTest) { // Feeds in sample accelerometer data and verifies that there are no // transitions into touchview / maximize mode while shaking the device around @@ -469,159 +345,6 @@ TEST_F(MaximizeModeControllerTest, MaximizeModeTest) { } } -// Tests that the display will stick to its current orientation when the -// rotation lock has been set. -TEST_F(MaximizeModeControllerTest, RotationLockPreventsRotation) { - // Trigger maximize mode by opening to 270. - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravity), - gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f)); - ASSERT_TRUE(IsMaximizeModeStarted()); - - gfx::Vector3dF gravity(-kMeanGravity, 0.0f, 0.0f); - - maximize_mode_controller()->SetRotationLocked(true); - - // Turn past the threshold for rotation. - float degrees = 90.0; - gravity.set_x(-sin(degrees * kDegreesToRadians) * kMeanGravity); - gravity.set_y(-cos(degrees * kDegreesToRadians) * kMeanGravity); - TriggerBaseAndLidUpdate(gravity, gravity); - EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); - - maximize_mode_controller()->SetRotationLocked(false); - TriggerBaseAndLidUpdate(gravity, gravity); - EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); -} - -// Tests that when MaximizeModeController turns off MaximizeMode that on the -// next accelerometer update the rotation lock is cleared. -TEST_F(MaximizeModeControllerTest, ExitingMaximizeModeClearRotationLock) { - // Trigger maximize mode by opening to 270. - OpenLidToAngle(270.0f); - ASSERT_TRUE(IsMaximizeModeStarted()); - - maximize_mode_controller()->SetRotationLocked(true); - - OpenLidToAngle(90.0f); - EXPECT_FALSE(IsMaximizeModeStarted()); - - // Send an update that would not relaunch MaximizeMode. - OpenLidToAngle(90.0f); - EXPECT_FALSE(maximize_mode_controller()->rotation_locked()); -} - -// The TrayDisplay 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(MaximizeModeControllerTest, BlockRotationNotifications) { - test::TestSystemTrayDelegate* tray_delegate = - static_cast( - Shell::GetInstance()->system_tray_delegate()); - tray_delegate->set_should_show_display_notification(true); - - message_center::MessageCenter* message_center = - message_center::MessageCenter::Get(); - - // Make sure notifications are still displayed when - // adjusting the screen rotation directly when not in maximize mode - ASSERT_FALSE(IsMaximizeModeStarted()); - ASSERT_NE(gfx::Display::ROTATE_180, GetInternalDisplayRotation()); - ASSERT_EQ(0u, message_center->NotificationCount()); - ASSERT_FALSE(message_center->HasPopupNotifications()); - SetInternalDisplayRotation(gfx::Display::ROTATE_180); - EXPECT_EQ(gfx::Display::ROTATE_180, GetInternalDisplayRotation()); - EXPECT_EQ(1u, message_center->NotificationCount()); - EXPECT_TRUE(message_center->HasPopupNotifications()); - - // Reset the screen rotation. - SetInternalDisplayRotation(gfx::Display::ROTATE_0); - // Clear all notifications - message_center->RemoveAllNotifications(false); - // Trigger maximize mode by opening to 270. - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravity), - gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f)); - EXPECT_TRUE(IsMaximizeModeStarted()); - 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 maximize mode - ASSERT_NE(gfx::Display::ROTATE_270, GetInternalDisplayRotation()); - SetInternalDisplayRotation(gfx::Display::ROTATE_270); - maximize_mode_controller()->SetRotationLocked(false); - EXPECT_EQ(gfx::Display::ROTATE_270, GetInternalDisplayRotation()); - EXPECT_EQ(1u, message_center->NotificationCount()); - EXPECT_TRUE(message_center->HasPopupNotifications()); - - // Clear all notifications - message_center->RemoveAllNotifications(false); - 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 maximize mode - // Rotate the screen 90 degrees - ASSERT_NE(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); - TriggerBaseAndLidUpdate(gfx::Vector3dF(-kMeanGravity, 0.0f, 0.0f), - gfx::Vector3dF(-kMeanGravity, 0.0f, 0.0f)); - ASSERT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); - EXPECT_EQ(0u, message_center->NotificationCount()); - EXPECT_FALSE(message_center->HasPopupNotifications()); -} - -// Tests that if a user has set a display rotation that it is restored upon -// exiting maximize mode. -TEST_F(MaximizeModeControllerTest, ResetUserRotationUponExit) { - DisplayManager* display_manager = Shell::GetInstance()->display_manager(); - display_manager->SetDisplayRotation(gfx::Display::InternalDisplayId(), - gfx::Display::ROTATE_90); - - // Trigger maximize mode - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravity), - gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f)); - ASSERT_TRUE(IsMaximizeModeStarted()); - - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f), - gfx::Vector3dF(0.0f, kMeanGravity, 0.0f)); - EXPECT_EQ(gfx::Display::ROTATE_180, GetInternalDisplayRotation()); - - // Exit maximize mode - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, -kMeanGravity), - gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f)); - EXPECT_FALSE(IsMaximizeModeStarted()); - EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); -} - -// Tests that if a user sets a display rotation that accelerometer rotation -// becomes locked. -TEST_F(MaximizeModeControllerTest, - NonAccelerometerRotationChangesLockRotation) { - // Trigger maximize mode by opening to 270. - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravity), - gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f)); - ASSERT_FALSE(maximize_mode_controller()->rotation_locked()); - SetInternalDisplayRotation(gfx::Display::ROTATE_270); - EXPECT_TRUE(maximize_mode_controller()->rotation_locked()); -} - -// Tests that if a user changes the display rotation, while rotation is locked, -// that the updates are recorded. Upon exiting maximize mode the latest user -// rotation should be applied. -TEST_F(MaximizeModeControllerTest, UpdateUserRotationWhileRotationLocked) { - // Trigger maximize mode by opening to 270. - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, kMeanGravity), - gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f)); - SetInternalDisplayRotation(gfx::Display::ROTATE_270); - // User sets rotation to the same rotation that the display was at when - // maximize mode was activated. - SetInternalDisplayRotation(gfx::Display::ROTATE_0); - // Exit maximize mode - TriggerBaseAndLidUpdate(gfx::Vector3dF(0.0f, 0.0f, -kMeanGravity), - gfx::Vector3dF(0.0f, -kMeanGravity, 0.0f)); - EXPECT_EQ(gfx::Display::ROTATE_0, GetInternalDisplayRotation()); -} - class MaximizeModeControllerSwitchesTest : public MaximizeModeControllerTest { public: MaximizeModeControllerSwitchesTest() {} @@ -637,18 +360,13 @@ class MaximizeModeControllerSwitchesTest : public MaximizeModeControllerTest { }; // Tests that when the command line switch for testing maximize mode is on, that -// accelerometer updates which would normally cause it to exit do not, and that -// screen rotations still occur. +// accelerometer updates which would normally cause it to exit do not. TEST_F(MaximizeModeControllerSwitchesTest, IgnoreHingeAngles) { maximize_mode_controller()->EnableMaximizeModeWindowManager(true); // Would normally trigger an exit from maximize mode. OpenLidToAngle(90.0f); EXPECT_TRUE(IsMaximizeModeStarted()); - - TriggerBaseAndLidUpdate(gfx::Vector3dF(-kMeanGravity, 0.0f, 0.0f), - gfx::Vector3dF(-kMeanGravity, 0.0f, 0.0f)); - EXPECT_EQ(gfx::Display::ROTATE_90, GetInternalDisplayRotation()); } } // namespace ash diff --git a/chrome/browser/chromeos/display/display_preferences_unittest.cc b/chrome/browser/chromeos/display/display_preferences_unittest.cc index 0ff09cc9839a..572256aee12b 100644 --- a/chrome/browser/chromeos/display/display_preferences_unittest.cc +++ b/chrome/browser/chromeos/display/display_preferences_unittest.cc @@ -7,6 +7,7 @@ #include #include +#include "ash/content/display/screen_orientation_controller_chromeos.h" #include "ash/display/display_controller.h" #include "ash/display/display_layout_store.h" #include "ash/display/display_manager.h" @@ -41,6 +42,12 @@ const char kOffsetKey[] = "offset"; // The mean acceleration due to gravity on Earth in m/s^2. const float kMeanGravity = 9.80665f; +bool IsRotationLocked() { + return ash::Shell::GetInstance() + ->screen_orientation_controller() + ->rotation_locked(); +} + class DisplayPreferencesTest : public ash::test::AshTestBase { protected: DisplayPreferencesTest() @@ -655,7 +662,6 @@ TEST_F(DisplayPreferencesTest, DontSaveAndRestoreAllOff) { // are not saved. TEST_F(DisplayPreferencesTest, DontSaveMaximizeModeControllerRotations) { ash::Shell* shell = ash::Shell::GetInstance(); - ash::MaximizeModeController* controller = shell->maximize_mode_controller(); gfx::Display::SetInternalDisplayId( gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().id()); ash::DisplayManager* display_manager = shell->display_manager(); @@ -673,6 +679,7 @@ TEST_F(DisplayPreferencesTest, DontSaveMaximizeModeControllerRotations) { 0.0f, 0.0f, kMeanGravity); update.Set(ui::ACCELEROMETER_SOURCE_SCREEN, 0.0f, -kMeanGravity, 0.0f); + ash::MaximizeModeController* controller = shell->maximize_mode_controller(); controller->OnAccelerometerUpdated(update); EXPECT_TRUE(controller->IsMaximizeModeWindowManagerEnabled()); @@ -682,6 +689,7 @@ TEST_F(DisplayPreferencesTest, DontSaveMaximizeModeControllerRotations) { update.Set(ui::ACCELEROMETER_SOURCE_SCREEN, -kMeanGravity, 0.0f, 0.0f); controller->OnAccelerometerUpdated(update); + shell->screen_orientation_controller()->OnAccelerometerUpdated(update); EXPECT_EQ(gfx::Display::ROTATE_90, display_manager-> GetDisplayInfo(gfx::Display::InternalDisplayId()).rotation()); @@ -701,8 +709,7 @@ TEST_F(DisplayPreferencesTest, StoreRotationStateNoLogin) { gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().id()); EXPECT_FALSE(local_state()->HasPrefPath(prefs::kDisplayRotationLock)); - bool current_rotation_lock = - ash::Shell::GetInstance()->maximize_mode_controller()->rotation_locked(); + bool current_rotation_lock = IsRotationLocked(); StoreDisplayRotationPrefs(current_rotation_lock); EXPECT_TRUE(local_state()->HasPrefPath(prefs::kDisplayRotationLock)); @@ -727,8 +734,7 @@ TEST_F(DisplayPreferencesTest, StoreRotationStateGuest) { EXPECT_FALSE(local_state()->HasPrefPath(prefs::kDisplayRotationLock)); LoggedInAsGuest(); - bool current_rotation_lock = - ash::Shell::GetInstance()->maximize_mode_controller()->rotation_locked(); + bool current_rotation_lock = IsRotationLocked(); StoreDisplayRotationPrefs(current_rotation_lock); EXPECT_TRUE(local_state()->HasPrefPath(prefs::kDisplayRotationLock)); @@ -753,8 +759,7 @@ TEST_F(DisplayPreferencesTest, StoreRotationStateNormalUser) { EXPECT_FALSE(local_state()->HasPrefPath(prefs::kDisplayRotationLock)); LoggedInAsGuest(); - bool current_rotation_lock = - ash::Shell::GetInstance()->maximize_mode_controller()->rotation_locked(); + bool current_rotation_lock = IsRotationLocked(); StoreDisplayRotationPrefs(current_rotation_lock); EXPECT_TRUE(local_state()->HasPrefPath(prefs::kDisplayRotationLock)); @@ -780,9 +785,7 @@ TEST_F(DisplayPreferencesTest, LoadRotationNoLogin) { ASSERT_FALSE(local_state()->HasPrefPath(prefs::kDisplayRotationLock)); ash::Shell* shell = ash::Shell::GetInstance(); - ash::MaximizeModeController* maximize_mode_controller = - shell->maximize_mode_controller(); - bool initial_rotation_lock = maximize_mode_controller->rotation_locked(); + bool initial_rotation_lock = IsRotationLocked(); ASSERT_FALSE(initial_rotation_lock); ash::DisplayManager* display_manager = shell->display_manager(); gfx::Display::Rotation initial_rotation = display_manager-> @@ -802,7 +805,7 @@ TEST_F(DisplayPreferencesTest, LoadRotationNoLogin) { EXPECT_TRUE(display_rotation_lock); EXPECT_EQ(gfx::Display::ROTATE_90, display_rotation); - bool rotation_lock = maximize_mode_controller->rotation_locked(); + bool rotation_lock = IsRotationLocked(); gfx::Display::Rotation before_maximize_mode_rotation = display_manager-> GetDisplayInfo(gfx::Display::InternalDisplayId()).rotation(); @@ -816,59 +819,25 @@ TEST_F(DisplayPreferencesTest, LoadRotationNoLogin) { 0.0f, 0.0f, kMeanGravity); update.Set(ui::ACCELEROMETER_SOURCE_SCREEN, 0.0f, -kMeanGravity, 0.0f); + ash::MaximizeModeController* maximize_mode_controller = + shell->maximize_mode_controller(); maximize_mode_controller->OnAccelerometerUpdated(update); EXPECT_TRUE(maximize_mode_controller->IsMaximizeModeWindowManagerEnabled()); - bool maximize_mode_rotation_lock = - maximize_mode_controller->rotation_locked(); + bool screen_orientation_rotation_lock = IsRotationLocked(); gfx::Display::Rotation maximize_mode_rotation = display_manager-> GetDisplayInfo(gfx::Display::InternalDisplayId()).rotation(); - EXPECT_TRUE(maximize_mode_rotation_lock); + EXPECT_TRUE(screen_orientation_rotation_lock); EXPECT_EQ(gfx::Display::ROTATE_90, maximize_mode_rotation); } -// Tests that loaded rotation state is ignored if the device starts in normal -// mode, and that they are not applied upon first entering maximize mode. -TEST_F(DisplayPreferencesTest, LoadRotationIgnoredInNormalMode) { - gfx::Display::SetInternalDisplayId( - gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().id()); - ASSERT_FALSE(local_state()->HasPrefPath(prefs::kDisplayRotationLock)); - StoreDisplayRotationPrefs(false /* rotation_lock*/); - ASSERT_TRUE(local_state()->HasPrefPath(prefs::kDisplayRotationLock)); - - StoreDisplayRotationPrefsForTest(true, gfx::Display::ROTATE_90); - LoadDisplayPreferences(false); - - ash::MaximizeModeController* maximize_mode_controller = - ash::Shell::GetInstance()->maximize_mode_controller(); - // Lid open to 90 degrees - ui::AccelerometerUpdate update; - update.Set(ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD, - -kMeanGravity, 0.0f, 0.0f); - update.Set(ui::ACCELEROMETER_SOURCE_SCREEN, - -kMeanGravity, 0.0f, 0.0f); - maximize_mode_controller->OnAccelerometerUpdated(update); - EXPECT_FALSE(maximize_mode_controller->IsMaximizeModeWindowManagerEnabled()); - EXPECT_FALSE(maximize_mode_controller->rotation_locked()); - - // Open up 270 degrees to trigger maximize mode - update.Set(ui::ACCELEROMETER_SOURCE_ATTACHED_KEYBOARD, - 0.0f, 0.0f, kMeanGravity); - update.Set(ui::ACCELEROMETER_SOURCE_SCREEN, - 0.0f, -kMeanGravity, 0.0f); - maximize_mode_controller->OnAccelerometerUpdated(update); - EXPECT_TRUE(maximize_mode_controller->IsMaximizeModeWindowManagerEnabled()); - EXPECT_FALSE(maximize_mode_controller->rotation_locked()); -} - // Tests that rotation lock being set causes the rotation state to be saved. TEST_F(DisplayPreferencesTest, RotationLockTriggersStore) { gfx::Display::SetInternalDisplayId( gfx::Screen::GetNativeScreen()->GetPrimaryDisplay().id()); ASSERT_FALSE(local_state()->HasPrefPath(prefs::kDisplayRotationLock)); - ash::MaximizeModeController* maximize_mode_controller = - ash::Shell::GetInstance()->maximize_mode_controller(); - maximize_mode_controller->SetRotationLocked(true); + ash::Shell::GetInstance()->screen_orientation_controller()->SetRotationLocked( + true); EXPECT_TRUE(local_state()->HasPrefPath(prefs::kDisplayRotationLock)); diff --git a/chromeos/accelerometer/accelerometer_reader.cc b/chromeos/accelerometer/accelerometer_reader.cc index 340ba18856cd..9a91383a88d6 100644 --- a/chromeos/accelerometer/accelerometer_reader.cc +++ b/chromeos/accelerometer/accelerometer_reader.cc @@ -58,6 +58,10 @@ const int kDelayBetweenReadsMs = 100; // The mean acceleration due to gravity on Earth in m/s^2. const float kMeanGravity = 9.80665f; +// The maximum deviation from the acceleration expected due to gravity under +// which to detect hinge angle and screen rotation in m/s^2 +const float kDeviationFromGravityThreshold = 1.0f; + // Reads |path| to the unsigned int pointed to by |value|. Returns true on // success or false on failure. bool ReadFileToInt(const base::FilePath& path, int* value) { @@ -206,12 +210,20 @@ void AccelerometerReader::Initialize( void AccelerometerReader::AddObserver(Observer* observer) { observers_.AddObserver(observer); + observer->OnAccelerometerUpdated(update_); } void AccelerometerReader::RemoveObserver(Observer* observer) { observers_.RemoveObserver(observer); } +bool AccelerometerReader::IsReadingStable(const ui::AccelerometerUpdate& update, + ui::AccelerometerSource source) { + return update.has(source) && + std::abs(update.get(source).Length() - kMeanGravity) <= + kDeviationFromGravityThreshold; +} + void AccelerometerReader::OnInitialized( scoped_refptr configuration, bool success) { diff --git a/chromeos/accelerometer/accelerometer_reader.h b/chromeos/accelerometer/accelerometer_reader.h index ff1e5adc1b55..1d0c6ecebb3e 100644 --- a/chromeos/accelerometer/accelerometer_reader.h +++ b/chromeos/accelerometer/accelerometer_reader.h @@ -63,6 +63,12 @@ class CHROMEOS_EXPORT AccelerometerReader { void AddObserver(Observer* observer); void RemoveObserver(Observer* observer); + // A reading is considered stable if its deviation from gravity is small. This + // returns false if the deviation is too higher, or if |source| is not present + // in the update. + static bool IsReadingStable(const ui::AccelerometerUpdate& update, + ui::AccelerometerSource source); + private: // Dispatched when initialization is complete. If |success|, |configuration| // provides the details of the detected accelerometer. diff --git a/ui/gfx/geometry/vector3d_f.cc b/ui/gfx/geometry/vector3d_f.cc index a30f9db5cbe3..87223880669e 100644 --- a/ui/gfx/geometry/vector3d_f.cc +++ b/ui/gfx/geometry/vector3d_f.cc @@ -8,6 +8,10 @@ #include "base/strings/stringprintf.h" +namespace { +const float kRadiansToDegrees = 180.0f / 3.14159265f; +} + namespace gfx { Vector3dF::Vector3dF() @@ -85,4 +89,25 @@ Vector3dF ScaleVector3d(const Vector3dF& v, return scaled_v; } +float AngleBetweenVectorsInDegrees(const gfx::Vector3dF& base, + const gfx::Vector3dF& other) { + return acos(gfx::DotProduct(base, other) / base.Length() / other.Length()) * + kRadiansToDegrees; +} + +float ClockwiseAngleBetweenVectorsInDegrees(const gfx::Vector3dF& base, + const gfx::Vector3dF& other, + const gfx::Vector3dF& normal) { + float angle = AngleBetweenVectorsInDegrees(base, other); + gfx::Vector3dF cross(base); + cross.Cross(other); + + // If the dot product of this cross product is normal, it means that the + // shortest angle between |base| and |other| was counterclockwise with respect + // to the surface represented by |normal| and this angle must be reversed. + if (gfx::DotProduct(cross, normal) > 0.0f) + angle = 360.0f - angle; + return angle; +} + } // namespace gfx diff --git a/ui/gfx/geometry/vector3d_f.h b/ui/gfx/geometry/vector3d_f.h index 637b6d918072..4e0eaa412492 100644 --- a/ui/gfx/geometry/vector3d_f.h +++ b/ui/gfx/geometry/vector3d_f.h @@ -120,6 +120,17 @@ inline Vector3dF ScaleVector3d(const Vector3dF& v, float scale) { return ScaleVector3d(v, scale, scale, scale); } +// Returns the angle between |base| and |other| in degrees. +GFX_EXPORT float AngleBetweenVectorsInDegrees(const gfx::Vector3dF& base, + const gfx::Vector3dF& other); + +// Returns the clockwise angle between |base| and |other| where |normal| is the +// normal of the virtual surface to measure clockwise according to. +GFX_EXPORT float ClockwiseAngleBetweenVectorsInDegrees( + const gfx::Vector3dF& base, + const gfx::Vector3dF& other, + const gfx::Vector3dF& normal); + // This is declared here for use in gtest-based unit tests but is defined in // the gfx_test_support target. Depend on that to use this in your unit test. // This should not be used in production code - call ToString() instead. diff --git a/ui/gfx/geometry/vector3d_unittest.cc b/ui/gfx/geometry/vector3d_unittest.cc index d058ad12224d..2a3a1a0c5d83 100644 --- a/ui/gfx/geometry/vector3d_unittest.cc +++ b/ui/gfx/geometry/vector3d_unittest.cc @@ -261,4 +261,57 @@ TEST(Vector3dFTest, ClampVector3dF) { EXPECT_EQ(Vector3dF(3.5f, 5.5f, 7.5f).ToString(), a.ToString()); } +TEST(Vector3dTest, AngleBetweenVectorsInDegress) { + const struct { + float expected; + gfx::Vector3dF input1; + gfx::Vector3dF input2; + } tests[] = { + {0, gfx::Vector3dF(0, 1, 0), gfx::Vector3dF(0, 1, 0)}, + {90, gfx::Vector3dF(0, 1, 0), gfx::Vector3dF(0, 0, 1)}, + {45, + gfx::Vector3dF(0, 1, 0), + gfx::Vector3dF(0, 0.70710678188f, 0.70710678188f)}, + {180, gfx::Vector3dF(0, 1, 0), gfx::Vector3dF(0, -1, 0)}, + }; + + for (size_t i = 0; i < arraysize(tests); ++i) { + float actual = + gfx::AngleBetweenVectorsInDegrees(tests[i].input1, tests[i].input2); + EXPECT_FLOAT_EQ(tests[i].expected, actual); + actual = + gfx::AngleBetweenVectorsInDegrees(tests[i].input2, tests[i].input1); + EXPECT_FLOAT_EQ(tests[i].expected, actual); + } +} + +TEST(Vector3dTest, ClockwiseAngleBetweenVectorsInDegress) { + const struct { + float expected; + gfx::Vector3dF input1; + gfx::Vector3dF input2; + } tests[] = { + {0, gfx::Vector3dF(0, 1, 0), gfx::Vector3dF(0, 1, 0)}, + {90, gfx::Vector3dF(0, 1, 0), gfx::Vector3dF(0, 0, -1)}, + {45, + gfx::Vector3dF(0, -1, 0), + gfx::Vector3dF(0, -0.70710678188f, 0.70710678188f)}, + {180, gfx::Vector3dF(0, -1, 0), gfx::Vector3dF(0, 1, 0)}, + {270, gfx::Vector3dF(0, 1, 0), gfx::Vector3dF(0, 0, 1)}, + }; + + const gfx::Vector3dF normal_vector(1.0f, 0.0f, 0.0f); + + for (size_t i = 0; i < arraysize(tests); ++i) { + float actual = gfx::ClockwiseAngleBetweenVectorsInDegrees( + tests[i].input1, tests[i].input2, normal_vector); + EXPECT_FLOAT_EQ(tests[i].expected, actual); + actual = -gfx::ClockwiseAngleBetweenVectorsInDegrees( + tests[i].input2, tests[i].input1, normal_vector); + if (actual < 0.0f) + actual += 360.0f; + EXPECT_FLOAT_EQ(tests[i].expected, actual); + } +} + } // namespace gfx -- 2.11.4.GIT