OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "ash/wm/power_button_controller.h" | 5 #include "ash/wm/power_button_controller.h" |
6 | 6 |
7 #include "ash/ash_switches.h" | 7 #include "ash/ash_switches.h" |
8 #include "ash/shell.h" | 8 #include "ash/shell.h" |
9 #include "ash/shell_delegate.h" | 9 #include "ash/shell_delegate.h" |
10 #include "ash/shell_window_ids.h" | 10 #include "ash/shell_window_ids.h" |
11 #include "ash/wm/session_state_animator.h" | 11 #include "ash/wm/session_state_animator.h" |
12 #include "ash/wm/session_state_controller.h" | |
13 #include "base/command_line.h" | 12 #include "base/command_line.h" |
14 #include "ui/aura/root_window.h" | 13 #include "ui/aura/root_window.h" |
15 #include "ui/aura/shared/compound_event_filter.h" | 14 #include "ui/aura/shared/compound_event_filter.h" |
16 | 15 |
| 16 #if defined(OS_CHROMEOS) |
| 17 #include "base/chromeos/chromeos_version.h" |
| 18 #endif |
| 19 |
17 namespace ash { | 20 namespace ash { |
18 | 21 |
19 PowerButtonController::PowerButtonController(SessionStateController* controller) | 22 namespace { |
20 : power_button_down_(false), | 23 |
| 24 // Amount of time that the power button needs to be held before we lock the |
| 25 // screen. |
| 26 const int kLockTimeoutMs = 400; |
| 27 |
| 28 // Amount of time that the power button needs to be held before we shut down. |
| 29 const int kShutdownTimeoutMs = 400; |
| 30 |
| 31 // Amount of time to wait for our lock requests to be honored before giving up. |
| 32 const int kLockFailTimeoutMs = 4000; |
| 33 |
| 34 // When the button has been held continuously from the unlocked state, amount of |
| 35 // time that we wait after the screen locker window is shown before starting the |
| 36 // pre-shutdown animation. |
| 37 const int kLockToShutdownTimeoutMs = 150; |
| 38 |
| 39 // Amount of time taken to scale the snapshot of the screen down to a |
| 40 // slightly-smaller size once the user starts holding the power button. Used |
| 41 // for both the pre-lock and pre-shutdown animations. |
| 42 const int kSlowCloseAnimMs = 400; |
| 43 |
| 44 // Amount of time taken to scale the snapshot of the screen back to its original |
| 45 // size when the button is released. |
| 46 const int kUndoSlowCloseAnimMs = 100; |
| 47 |
| 48 // Amount of time taken to scale the snapshot down to a point in the center of |
| 49 // the screen once the screen has been locked or we've been notified that the |
| 50 // system is shutting down. |
| 51 const int kFastCloseAnimMs = 150; |
| 52 |
| 53 // Additional time (beyond kFastCloseAnimMs) to wait after starting the |
| 54 // fast-close shutdown animation before actually requesting shutdown, to give |
| 55 // the animation time to finish. |
| 56 const int kShutdownRequestDelayMs = 50; |
| 57 |
| 58 } // namespace |
| 59 |
| 60 PowerButtonController::TestApi::TestApi(PowerButtonController* controller) |
| 61 : controller_(controller), |
| 62 animator_api_(new internal::SessionStateAnimator::TestApi( |
| 63 controller->animator_.get())) { |
| 64 } |
| 65 |
| 66 PowerButtonController::TestApi::~TestApi() { |
| 67 } |
| 68 |
| 69 PowerButtonController::PowerButtonController() |
| 70 : login_status_(user::LOGGED_IN_NONE), |
| 71 unlocked_login_status_(user::LOGGED_IN_NONE), |
| 72 power_button_down_(false), |
21 lock_button_down_(false), | 73 lock_button_down_(false), |
22 screen_is_off_(false), | 74 screen_is_off_(false), |
| 75 shutting_down_(false), |
23 has_legacy_power_button_( | 76 has_legacy_power_button_( |
24 CommandLine::ForCurrentProcess()->HasSwitch( | 77 CommandLine::ForCurrentProcess()->HasSwitch( |
25 switches::kAuraLegacyPowerButton)), | 78 switches::kAuraLegacyPowerButton)), |
26 controller_(controller) { | 79 animator_(new internal::SessionStateAnimator()) { |
| 80 Shell::GetPrimaryRootWindow()->AddRootWindowObserver(this); |
27 } | 81 } |
28 | 82 |
29 PowerButtonController::~PowerButtonController() { | 83 PowerButtonController::~PowerButtonController() { |
| 84 Shell::GetPrimaryRootWindow()->RemoveRootWindowObserver(this); |
| 85 } |
| 86 |
| 87 void PowerButtonController::OnLoginStateChanged(user::LoginStatus status) { |
| 88 login_status_ = status; |
| 89 unlocked_login_status_ = user::LOGGED_IN_NONE; |
| 90 } |
| 91 |
| 92 void PowerButtonController::OnAppTerminating() { |
| 93 // If we hear that Chrome is exiting but didn't request it ourselves, all we |
| 94 // can really hope for is that we'll have time to clear the screen. |
| 95 if (!shutting_down_) { |
| 96 shutting_down_ = true; |
| 97 Shell* shell = ash::Shell::GetInstance(); |
| 98 shell->env_filter()->set_cursor_hidden_by_filter(false); |
| 99 shell->cursor_manager()->ShowCursor(false); |
| 100 animator_->ShowBlackLayer(); |
| 101 animator_->StartAnimation( |
| 102 internal::SessionStateAnimator::kAllContainersMask, |
| 103 internal::SessionStateAnimator::ANIMATION_HIDE); |
| 104 } |
| 105 } |
| 106 |
| 107 void PowerButtonController::OnLockStateChanged(bool locked) { |
| 108 if (shutting_down_ || (login_status_ == user::LOGGED_IN_LOCKED) == locked) |
| 109 return; |
| 110 |
| 111 if (!locked && login_status_ == user::LOGGED_IN_LOCKED) { |
| 112 login_status_ = unlocked_login_status_; |
| 113 unlocked_login_status_ = user::LOGGED_IN_NONE; |
| 114 } else { |
| 115 unlocked_login_status_ = login_status_; |
| 116 login_status_ = user::LOGGED_IN_LOCKED; |
| 117 } |
| 118 |
| 119 if (locked) { |
| 120 animator_->StartAnimation( |
| 121 internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS, |
| 122 internal::SessionStateAnimator::ANIMATION_FADE_IN); |
| 123 lock_timer_.Stop(); |
| 124 lock_fail_timer_.Stop(); |
| 125 |
| 126 if (!has_legacy_power_button_ && power_button_down_) { |
| 127 lock_to_shutdown_timer_.Stop(); |
| 128 lock_to_shutdown_timer_.Start( |
| 129 FROM_HERE, |
| 130 base::TimeDelta::FromMilliseconds(kLockToShutdownTimeoutMs), |
| 131 this, &PowerButtonController::OnLockToShutdownTimeout); |
| 132 } |
| 133 } else { |
| 134 animator_->StartAnimation( |
| 135 internal::SessionStateAnimator::DESKTOP_BACKGROUND | |
| 136 internal::SessionStateAnimator::LAUNCHER | |
| 137 internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS, |
| 138 internal::SessionStateAnimator::ANIMATION_RESTORE); |
| 139 animator_->DropBlackLayer(); |
| 140 } |
30 } | 141 } |
31 | 142 |
32 void PowerButtonController::OnScreenBrightnessChanged(double percent) { | 143 void PowerButtonController::OnScreenBrightnessChanged(double percent) { |
33 screen_is_off_ = percent <= 0.001; | 144 screen_is_off_ = percent <= 0.001; |
34 } | 145 } |
35 | 146 |
| 147 void PowerButtonController::OnStartingLock() { |
| 148 if (shutting_down_ || login_status_ == user::LOGGED_IN_LOCKED) |
| 149 return; |
| 150 |
| 151 // Ensure that the black layer is visible -- if the screen was locked via |
| 152 // the wrench menu, we won't have already shown the black background |
| 153 // as part of the slow-close animation. |
| 154 animator_->ShowBlackLayer(); |
| 155 |
| 156 animator_->StartAnimation( |
| 157 internal::SessionStateAnimator::LAUNCHER, |
| 158 internal::SessionStateAnimator::ANIMATION_HIDE); |
| 159 |
| 160 animator_->StartAnimation( |
| 161 internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS, |
| 162 internal::SessionStateAnimator::ANIMATION_FAST_CLOSE); |
| 163 |
| 164 // Hide the screen locker containers so we can make them fade in later. |
| 165 animator_->StartAnimation( |
| 166 internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS, |
| 167 internal::SessionStateAnimator::ANIMATION_HIDE); |
| 168 } |
| 169 |
36 void PowerButtonController::OnPowerButtonEvent( | 170 void PowerButtonController::OnPowerButtonEvent( |
37 bool down, const base::TimeTicks& timestamp) { | 171 bool down, const base::TimeTicks& timestamp) { |
38 power_button_down_ = down; | 172 power_button_down_ = down; |
39 | 173 |
40 if (controller_->ShutdownRequested()) | 174 if (shutting_down_) |
41 return; | 175 return; |
42 | 176 |
43 // Avoid starting the lock/shutdown sequence if the power button is pressed | 177 // Avoid starting the lock/shutdown sequence if the power button is pressed |
44 // while the screen is off (http://crbug.com/128451). | 178 // while the screen is off (http://crbug.com/128451). |
45 if (screen_is_off_) | 179 if (screen_is_off_) |
46 return; | 180 return; |
47 | 181 |
48 if (has_legacy_power_button_) { | 182 if (has_legacy_power_button_) { |
49 // If power button releases won't get reported correctly because we're not | 183 // If power button releases won't get reported correctly because we're not |
50 // running on official hardware, just lock the screen or shut down | 184 // running on official hardware, just lock the screen or shut down |
51 // immediately. | 185 // immediately. |
52 if (down) { | 186 if (down) { |
53 if (controller_->IsEligibleForLock()) | 187 animator_->ShowBlackLayer(); |
54 controller_->StartLockAnimationAndLockImmediately(); | 188 if (LoggedInAsNonGuest() && login_status_ != user::LOGGED_IN_LOCKED) { |
55 else | 189 animator_->StartAnimation( |
56 controller_->RequestShutdown(); | 190 internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS, |
| 191 internal::SessionStateAnimator::ANIMATION_SLOW_CLOSE); |
| 192 OnLockTimeout(); |
| 193 } else { |
| 194 OnShutdownTimeout(); |
| 195 } |
57 } | 196 } |
58 } else { // !has_legacy_power_button_ | 197 } else { // !has_legacy_power_button_ |
59 if (down) { | 198 if (down) { |
60 // If we already have a pending request to lock the screen, wait. | 199 // If we already have a pending request to lock the screen, wait. |
61 if (controller_->LockRequested()) | 200 if (lock_fail_timer_.IsRunning()) |
62 return; | 201 return; |
63 | 202 |
64 if (controller_->IsEligibleForLock()) | 203 if (LoggedInAsNonGuest() && login_status_ != user::LOGGED_IN_LOCKED) |
65 controller_->StartLockAnimation(); | 204 StartLockTimer(); |
66 else | 205 else |
67 controller_->StartShutdownAnimation(); | 206 StartShutdownTimer(); |
68 } else { // Button is up. | 207 } else { // Button is up. |
69 if (controller_->CanCancelLockAnimation()) | 208 if (lock_timer_.IsRunning() || shutdown_timer_.IsRunning()) { |
70 controller_->CancelLockAnimation(); | 209 if (login_status_ == user::LOGGED_IN_LOCKED) { |
71 else if (controller_->CanCancelShutdownAnimation()) | 210 // If we've already started shutdown transition at lock screen |
72 controller_->CancelShutdownAnimation(); | 211 // desktop background needs to be restored immediately. |
| 212 animator_->StartAnimation( |
| 213 internal::SessionStateAnimator::DESKTOP_BACKGROUND, |
| 214 internal::SessionStateAnimator::ANIMATION_RESTORE); |
| 215 } |
| 216 animator_->StartAnimation( |
| 217 (login_status_ == user::LOGGED_IN_LOCKED) ? |
| 218 internal::SessionStateAnimator::kAllLockScreenContainersMask : |
| 219 internal::SessionStateAnimator::kAllContainersMask, |
| 220 internal::SessionStateAnimator::ANIMATION_UNDO_SLOW_CLOSE); |
| 221 } |
| 222 |
| 223 // Drop the black layer after the undo animation finishes. |
| 224 if (lock_timer_.IsRunning() || |
| 225 (shutdown_timer_.IsRunning() && !LoggedInAsNonGuest())) { |
| 226 animator_->ScheduleDropBlackLayer(); |
| 227 } |
| 228 |
| 229 lock_timer_.Stop(); |
| 230 shutdown_timer_.Stop(); |
| 231 lock_to_shutdown_timer_.Stop(); |
73 } | 232 } |
74 } | 233 } |
75 } | 234 } |
76 | 235 |
77 void PowerButtonController::OnLockButtonEvent( | 236 void PowerButtonController::OnLockButtonEvent( |
78 bool down, const base::TimeTicks& timestamp) { | 237 bool down, const base::TimeTicks& timestamp) { |
79 lock_button_down_ = down; | 238 lock_button_down_ = down; |
80 | 239 |
81 if (controller_->ShutdownRequested() || !controller_->IsEligibleForLock()) | 240 if (shutting_down_ || !LoggedInAsNonGuest()) |
82 return; | 241 return; |
83 | 242 |
84 // Bail if we're already locked or are in the process of locking. Also give | 243 // Bail if we're already locked or are in the process of locking. Also give |
85 // the power button precedence over the lock button (we don't expect both | 244 // the power button precedence over the lock button (we don't expect both |
86 // buttons to be present, so this is just making sure that we don't do | 245 // buttons to be present, so this is just making sure that we don't do |
87 // something completely stupid if that assumption changes later). | 246 // something completely stupid if that assumption changes later). |
88 if (power_button_down_) | 247 if (login_status_ == user::LOGGED_IN_LOCKED || |
| 248 lock_fail_timer_.IsRunning() || power_button_down_) |
89 return; | 249 return; |
90 | 250 |
91 if (controller_->IsLocked() || controller_->LockRequested()) | 251 if (down) { |
92 return; | 252 StartLockTimer(); |
| 253 } else { |
| 254 if (lock_timer_.IsRunning()) { |
| 255 animator_->StartAnimation( |
| 256 internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS, |
| 257 internal::SessionStateAnimator::ANIMATION_UNDO_SLOW_CLOSE); |
| 258 animator_->ScheduleDropBlackLayer(); |
| 259 lock_timer_.Stop(); |
| 260 } |
| 261 } |
| 262 } |
93 | 263 |
94 if (down) | 264 void PowerButtonController::RequestShutdown() { |
95 controller_->StartLockAnimation(); | 265 if (!shutting_down_) |
96 else | 266 StartShutdownAnimationAndRequestShutdown(); |
97 controller_->CancelLockWithOtherAnimation(); | 267 } |
| 268 |
| 269 void PowerButtonController::OnRootWindowHostCloseRequested( |
| 270 const aura::RootWindow*) { |
| 271 if(Shell::GetInstance() && Shell::GetInstance()->delegate()) |
| 272 Shell::GetInstance()->delegate()->Exit(); |
| 273 } |
| 274 |
| 275 bool PowerButtonController::LoggedInAsNonGuest() const { |
| 276 if (login_status_ == user::LOGGED_IN_NONE) |
| 277 return false; |
| 278 if (login_status_ == user::LOGGED_IN_GUEST) |
| 279 return false; |
| 280 // TODO(mukai): think about kiosk mode. |
| 281 return true; |
| 282 } |
| 283 |
| 284 void PowerButtonController::OnLockTimeout() { |
| 285 delegate_->RequestLockScreen(); |
| 286 lock_fail_timer_.Start( |
| 287 FROM_HERE, |
| 288 base::TimeDelta::FromMilliseconds(kLockFailTimeoutMs), |
| 289 this, &PowerButtonController::OnLockFailTimeout); |
| 290 } |
| 291 |
| 292 void PowerButtonController::OnLockFailTimeout() { |
| 293 DCHECK_NE(login_status_, user::LOGGED_IN_LOCKED); |
| 294 LOG(ERROR) << "Screen lock request timed out"; |
| 295 animator_->StartAnimation( |
| 296 internal::SessionStateAnimator::LAUNCHER | |
| 297 internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS, |
| 298 internal::SessionStateAnimator::ANIMATION_RESTORE); |
| 299 animator_->DropBlackLayer(); |
| 300 } |
| 301 |
| 302 void PowerButtonController::OnLockToShutdownTimeout() { |
| 303 DCHECK_EQ(login_status_, user::LOGGED_IN_LOCKED); |
| 304 StartShutdownTimer(); |
| 305 } |
| 306 |
| 307 void PowerButtonController::OnShutdownTimeout() { |
| 308 if (!shutting_down_) |
| 309 StartShutdownAnimationAndRequestShutdown(); |
| 310 } |
| 311 |
| 312 void PowerButtonController::OnRealShutdownTimeout() { |
| 313 DCHECK(shutting_down_); |
| 314 #if defined(OS_CHROMEOS) |
| 315 if (!base::chromeos::IsRunningOnChromeOS()) { |
| 316 ShellDelegate* delegate = Shell::GetInstance()->delegate(); |
| 317 if (delegate) |
| 318 delegate->Exit(); |
| 319 } |
| 320 #endif |
| 321 delegate_->RequestShutdown(); |
| 322 } |
| 323 |
| 324 void PowerButtonController::StartLockTimer() { |
| 325 animator_->ShowBlackLayer(); |
| 326 animator_->StartAnimation( |
| 327 internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS, |
| 328 internal::SessionStateAnimator::ANIMATION_SLOW_CLOSE); |
| 329 lock_timer_.Stop(); |
| 330 lock_timer_.Start(FROM_HERE, |
| 331 base::TimeDelta::FromMilliseconds(kSlowCloseAnimMs), |
| 332 this, &PowerButtonController::OnLockTimeout); |
| 333 } |
| 334 |
| 335 void PowerButtonController::StartShutdownTimer() { |
| 336 animator_->ShowBlackLayer(); |
| 337 animator_->StartAnimation( |
| 338 internal::SessionStateAnimator::kAllContainersMask, |
| 339 internal::SessionStateAnimator::ANIMATION_SLOW_CLOSE); |
| 340 shutdown_timer_.Stop(); |
| 341 shutdown_timer_.Start( |
| 342 FROM_HERE, |
| 343 base::TimeDelta::FromMilliseconds(kShutdownTimeoutMs), |
| 344 this, &PowerButtonController::OnShutdownTimeout); |
| 345 } |
| 346 |
| 347 void PowerButtonController::StartShutdownAnimationAndRequestShutdown() { |
| 348 DCHECK(!shutting_down_); |
| 349 shutting_down_ = true; |
| 350 |
| 351 Shell* shell = ash::Shell::GetInstance(); |
| 352 shell->env_filter()->set_cursor_hidden_by_filter(false); |
| 353 shell->cursor_manager()->ShowCursor(false); |
| 354 |
| 355 animator_->ShowBlackLayer(); |
| 356 if (login_status_ != user::LOGGED_IN_NONE) { |
| 357 // Hide the other containers before starting the animation. |
| 358 // ANIMATION_FAST_CLOSE will make the screen locker windows partially |
| 359 // transparent, and we don't want the other windows to show through. |
| 360 animator_->StartAnimation( |
| 361 internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS | |
| 362 internal::SessionStateAnimator::LAUNCHER, |
| 363 internal::SessionStateAnimator::ANIMATION_HIDE); |
| 364 animator_->StartAnimation( |
| 365 internal::SessionStateAnimator::kAllLockScreenContainersMask, |
| 366 internal::SessionStateAnimator::ANIMATION_FAST_CLOSE); |
| 367 } else { |
| 368 animator_->StartAnimation( |
| 369 internal::SessionStateAnimator::kAllContainersMask, |
| 370 internal::SessionStateAnimator::ANIMATION_FAST_CLOSE); |
| 371 } |
| 372 |
| 373 real_shutdown_timer_.Start( |
| 374 FROM_HERE, |
| 375 base::TimeDelta::FromMilliseconds( |
| 376 kFastCloseAnimMs + kShutdownRequestDelayMs), |
| 377 this, &PowerButtonController::OnRealShutdownTimeout); |
98 } | 378 } |
99 | 379 |
100 } // namespace ash | 380 } // namespace ash |
OLD | NEW |