This change renames ScreenOrientationDelegate to ScreenOrientationController to refle...
[chromium-blink-merge.git] / ash / content / display / screen_orientation_controller_chromeos.cc
blobf36f1931b9c0e3c37fbe85a2a24f58b1b13ec7e9
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "ash/content/display/screen_orientation_controller_chromeos.h"
7 #include "ash/ash_switches.h"
8 #include "ash/display/display_info.h"
9 #include "ash/display/display_manager.h"
10 #include "ash/shell.h"
11 #include "ash/wm/maximize_mode/maximize_mode_controller.h"
12 #include "base/auto_reset.h"
13 #include "base/command_line.h"
14 #include "chromeos/accelerometer/accelerometer_reader.h"
15 #include "content/public/browser/screen_orientation_provider.h"
16 #include "content/public/browser/web_contents.h"
17 #include "ui/aura/window.h"
18 #include "ui/gfx/display.h"
19 #include "ui/gfx/geometry/size.h"
21 namespace {
23 // The angle which the screen has to be rotated past before the display will
24 // rotate to match it (i.e. 45.0f is no stickiness).
25 const float kDisplayRotationStickyAngleDegrees = 60.0f;
27 // The minimum acceleration in m/s^2 in a direction required to trigger screen
28 // rotation. This prevents rapid toggling of rotation when the device is near
29 // flat and there is very little screen aligned force on it. The value is
30 // effectively the sine of the rise angle required times the acceleration due
31 // to gravity, with the current value requiring at least a 25 degree rise.
32 const float kMinimumAccelerationScreenRotation = 4.2f;
34 blink::WebScreenOrientationLockType GetDisplayNaturalOrientation() {
35 ash::DisplayManager* display_manager =
36 ash::Shell::GetInstance()->display_manager();
37 if (!display_manager->HasInternalDisplay())
38 return blink::WebScreenOrientationLockLandscape;
40 ash::DisplayInfo info =
41 display_manager->GetDisplayInfo(gfx::Display::InternalDisplayId());
42 gfx::Size size = info.size_in_pixel();
43 switch (info.rotation()) {
44 case gfx::Display::ROTATE_0:
45 case gfx::Display::ROTATE_180:
46 return size.height() >= size.width()
47 ? blink::WebScreenOrientationLockPortrait
48 : blink::WebScreenOrientationLockLandscape;
49 case gfx::Display::ROTATE_90:
50 case gfx::Display::ROTATE_270:
51 return size.height() < size.width()
52 ? blink::WebScreenOrientationLockPortrait
53 : blink::WebScreenOrientationLockLandscape;
55 NOTREACHED();
56 return blink::WebScreenOrientationLockLandscape;
59 } // namespace
61 namespace ash {
63 ScreenOrientationController::ScreenOrientationController()
64 : locking_window_(NULL),
65 natural_orientation_(GetDisplayNaturalOrientation()),
66 ignore_display_configuration_updates_(false),
67 rotation_locked_(false),
68 user_rotation_(gfx::Display::ROTATE_0),
69 current_rotation_(gfx::Display::ROTATE_0) {
70 content::ScreenOrientationProvider::SetDelegate(this);
71 Shell::GetInstance()->AddShellObserver(this);
74 ScreenOrientationController::~ScreenOrientationController() {
75 content::ScreenOrientationProvider::SetDelegate(NULL);
76 Shell::GetInstance()->RemoveShellObserver(this);
77 Shell::GetInstance()->accelerometer_reader()->RemoveObserver(this);
78 Shell::GetInstance()->display_controller()->RemoveObserver(this);
81 void ScreenOrientationController::AddObserver(Observer* observer) {
82 observers_.AddObserver(observer);
85 void ScreenOrientationController::RemoveObserver(Observer* observer) {
86 observers_.RemoveObserver(observer);
89 void ScreenOrientationController::SetRotationLocked(bool rotation_locked) {
90 if (rotation_locked_ == rotation_locked)
91 return;
92 rotation_locked_ = rotation_locked;
93 FOR_EACH_OBSERVER(Observer, observers_,
94 OnRotationLockChanged(rotation_locked_));
95 DisplayManager* display_manager = Shell::GetInstance()->display_manager();
96 if (!display_manager->HasInternalDisplay())
97 return;
98 base::AutoReset<bool> auto_ignore_display_configuration_updates(
99 &ignore_display_configuration_updates_, true);
100 display_manager->RegisterDisplayRotationProperties(rotation_locked_,
101 current_rotation_);
104 void ScreenOrientationController::SetDisplayRotation(
105 gfx::Display::Rotation rotation) {
106 DisplayManager* display_manager = Shell::GetInstance()->display_manager();
107 if (!display_manager->HasInternalDisplay())
108 return;
109 current_rotation_ = rotation;
110 base::AutoReset<bool> auto_ignore_display_configuration_updates(
111 &ignore_display_configuration_updates_, true);
112 display_manager->SetDisplayRotation(gfx::Display::InternalDisplayId(),
113 rotation);
116 void ScreenOrientationController::OnAccelerometerUpdated(
117 const ui::AccelerometerUpdate& update) {
118 if (rotation_locked_)
119 return;
120 if (!update.has(ui::ACCELEROMETER_SOURCE_SCREEN))
121 return;
122 // Ignore the reading if it appears unstable. The reading is considered
123 // unstable if it deviates too much from gravity
124 if (chromeos::AccelerometerReader::IsReadingStable(
125 update, ui::ACCELEROMETER_SOURCE_SCREEN)) {
126 HandleScreenRotation(update.get(ui::ACCELEROMETER_SOURCE_SCREEN));
130 bool ScreenOrientationController::FullScreenRequired(
131 content::WebContents* web_contents) {
132 return true;
135 void ScreenOrientationController::Lock(
136 content::WebContents* web_contents,
137 blink::WebScreenOrientationLockType lock_orientation) {
138 aura::Window* requesting_window = web_contents->GetNativeView();
139 // TODO(jonross): Track one rotation lock per window. When the active window
140 // changes apply any corresponding rotation lock.
141 if (!locking_window_)
142 locking_window_ = requesting_window;
143 else if (requesting_window != locking_window_)
144 return;
146 switch (lock_orientation) {
147 case blink::WebScreenOrientationLockAny:
148 SetRotationLocked(false);
149 locking_window_ = NULL;
150 break;
151 case blink::WebScreenOrientationLockDefault:
152 NOTREACHED();
153 break;
154 case blink::WebScreenOrientationLockPortraitPrimary:
155 LockRotationToPrimaryOrientation(blink::WebScreenOrientationLockPortrait);
156 break;
157 case blink::WebScreenOrientationLockLandscape:
158 case blink::WebScreenOrientationLockPortrait:
159 LockToRotationMatchingOrientation(lock_orientation);
160 break;
161 case blink::WebScreenOrientationLockPortraitSecondary:
162 LockRotationToSecondaryOrientation(
163 blink::WebScreenOrientationLockPortrait);
164 break;
165 case blink::WebScreenOrientationLockLandscapeSecondary:
166 LockRotationToSecondaryOrientation(
167 blink::WebScreenOrientationLockLandscape);
168 break;
169 case blink::WebScreenOrientationLockLandscapePrimary:
170 LockRotationToPrimaryOrientation(
171 blink::WebScreenOrientationLockLandscape);
172 break;
173 case blink::WebScreenOrientationLockNatural:
174 LockRotation(gfx::Display::ROTATE_0);
175 break;
176 default:
177 NOTREACHED();
178 break;
182 bool ScreenOrientationController::ScreenOrientationProviderSupported() {
183 return Shell::GetInstance()
184 ->maximize_mode_controller()
185 ->IsMaximizeModeWindowManagerEnabled() &&
186 base::CommandLine::ForCurrentProcess()->HasSwitch(
187 switches::kAshEnableTouchViewTesting);
190 void ScreenOrientationController::Unlock(content::WebContents* web_contents) {
191 aura::Window* requesting_window = web_contents->GetNativeView();
192 if (requesting_window != locking_window_)
193 return;
194 locking_window_ = NULL;
195 SetRotationLocked(false);
198 void ScreenOrientationController::OnDisplayConfigurationChanged() {
199 if (ignore_display_configuration_updates_)
200 return;
201 gfx::Display::Rotation user_rotation =
202 Shell::GetInstance()
203 ->display_manager()
204 ->GetDisplayInfo(gfx::Display::InternalDisplayId())
205 .rotation();
206 if (user_rotation != current_rotation_) {
207 // A user may change other display configuration settings. When the user
208 // does change the rotation setting, then lock rotation to prevent the
209 // accelerometer from erasing their change.
210 SetRotationLocked(true);
211 user_rotation_ = current_rotation_ = user_rotation;
215 void ScreenOrientationController::OnMaximizeModeStarted() {
216 DisplayManager* display_manager = Shell::GetInstance()->display_manager();
217 if (!display_manager->HasInternalDisplay())
218 return;
219 current_rotation_ = user_rotation_ =
220 display_manager->GetDisplayInfo(gfx::Display::InternalDisplayId())
221 .rotation();
222 LoadDisplayRotationProperties();
223 Shell::GetInstance()->accelerometer_reader()->AddObserver(this);
224 Shell::GetInstance()->display_controller()->AddObserver(this);
227 void ScreenOrientationController::OnMaximizeModeEnded() {
228 if (!Shell::GetInstance()->display_manager()->HasInternalDisplay())
229 return;
230 Shell::GetInstance()->accelerometer_reader()->RemoveObserver(this);
231 Shell::GetInstance()->display_controller()->RemoveObserver(this);
232 if (current_rotation_ != user_rotation_)
233 SetDisplayRotation(user_rotation_);
236 void ScreenOrientationController::LockRotation(
237 gfx::Display::Rotation rotation) {
238 SetRotationLocked(true);
239 SetDisplayRotation(rotation);
242 void ScreenOrientationController::LockRotationToPrimaryOrientation(
243 blink::WebScreenOrientationLockType lock_orientation) {
244 LockRotation(natural_orientation_ == lock_orientation
245 ? gfx::Display::ROTATE_0
246 : gfx::Display::ROTATE_90);
249 void ScreenOrientationController::LockRotationToSecondaryOrientation(
250 blink::WebScreenOrientationLockType lock_orientation) {
251 LockRotation(natural_orientation_ == lock_orientation
252 ? gfx::Display::ROTATE_180
253 : gfx::Display::ROTATE_270);
256 void ScreenOrientationController::LockToRotationMatchingOrientation(
257 blink::WebScreenOrientationLockType lock_orientation) {
258 // TODO(jonross): Update MaximizeModeController to allow rotation between
259 // two angles of an orientation (e.g. from ROTATE_0 to ROTATE_180, and from
260 // ROTATE_90 to ROTATE_270)
261 DisplayManager* display_manager = Shell::GetInstance()->display_manager();
262 if (!display_manager->HasInternalDisplay())
263 return;
265 gfx::Display::Rotation rotation =
266 display_manager->GetDisplayInfo(gfx::Display::InternalDisplayId())
267 .rotation();
268 if (natural_orientation_ == lock_orientation) {
269 if (rotation == gfx::Display::ROTATE_0 ||
270 rotation == gfx::Display::ROTATE_180) {
271 SetRotationLocked(true);
272 } else {
273 LockRotation(gfx::Display::ROTATE_0);
275 } else {
276 if (rotation == gfx::Display::ROTATE_90 ||
277 rotation == gfx::Display::ROTATE_270) {
278 SetRotationLocked(true);
279 } else {
280 LockRotation(gfx::Display::ROTATE_90);
285 void ScreenOrientationController::HandleScreenRotation(
286 const gfx::Vector3dF& lid) {
287 gfx::Vector3dF lid_flattened(lid.x(), lid.y(), 0.0f);
288 float lid_flattened_length = lid_flattened.Length();
289 // When the lid is close to being flat, don't change rotation as it is too
290 // sensitive to slight movements.
291 if (lid_flattened_length < kMinimumAccelerationScreenRotation)
292 return;
294 // The reference vector is the angle of gravity when the device is rotated
295 // clockwise by 45 degrees. Computing the angle between this vector and
296 // gravity we can easily determine the expected display rotation.
297 static const gfx::Vector3dF rotation_reference(-1.0f, -1.0f, 0.0f);
299 // Set the down vector to match the expected direction of gravity given the
300 // last configured rotation. This is used to enforce a stickiness that the
301 // user must overcome to rotate the display and prevents frequent rotations
302 // when holding the device near 45 degrees.
303 gfx::Vector3dF down(0.0f, 0.0f, 0.0f);
304 if (current_rotation_ == gfx::Display::ROTATE_0)
305 down.set_y(-1.0f);
306 else if (current_rotation_ == gfx::Display::ROTATE_90)
307 down.set_x(-1.0f);
308 else if (current_rotation_ == gfx::Display::ROTATE_180)
309 down.set_y(1.0f);
310 else
311 down.set_x(1.0f);
313 // Don't rotate if the screen has not passed the threshold.
314 if (gfx::AngleBetweenVectorsInDegrees(down, lid_flattened) <
315 kDisplayRotationStickyAngleDegrees) {
316 return;
319 float angle = gfx::ClockwiseAngleBetweenVectorsInDegrees(
320 rotation_reference, lid_flattened, gfx::Vector3dF(0.0f, 0.0f, -1.0f));
322 gfx::Display::Rotation new_rotation = gfx::Display::ROTATE_90;
323 if (angle < 90.0f)
324 new_rotation = gfx::Display::ROTATE_0;
325 else if (angle < 180.0f)
326 new_rotation = gfx::Display::ROTATE_270;
327 else if (angle < 270.0f)
328 new_rotation = gfx::Display::ROTATE_180;
330 if (new_rotation != current_rotation_)
331 SetDisplayRotation(new_rotation);
334 void ScreenOrientationController::LoadDisplayRotationProperties() {
335 DisplayManager* display_manager = Shell::GetInstance()->display_manager();
336 if (!display_manager->registered_internal_display_rotation_lock())
337 return;
338 SetDisplayRotation(display_manager->registered_internal_display_rotation());
339 SetRotationLocked(true);
342 } // namespace ash