Index: ash/wm/power_button_controller.cc |
diff --git a/ash/wm/power_button_controller.cc b/ash/wm/power_button_controller.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..330ef6d86867e529684ea7bec36268e7f72be018 |
--- /dev/null |
+++ b/ash/wm/power_button_controller.cc |
@@ -0,0 +1,495 @@ |
+// Copyright (c) 2012 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/wm/power_button_controller.h" |
+ |
+#include "ash/shell.h" |
+#include "ash/shell_window_ids.h" |
+#include "base/logging.h" |
+#include "base/time.h" |
+#include "third_party/skia/include/core/SkColor.h" |
+#include "ui/aura/root_window.h" |
+#include "ui/aura/window.h" |
+#include "ui/gfx/canvas.h" |
+#include "ui/gfx/compositor/layer.h" |
+#include "ui/gfx/compositor/layer_animation_element.h" |
+#include "ui/gfx/compositor/layer_animation_sequence.h" |
+#include "ui/gfx/compositor/layer_animator.h" |
+#include "ui/gfx/rect.h" |
+#include "ui/gfx/size.h" |
+#include "ui/gfx/transform.h" |
+ |
+namespace ash { |
+ |
+namespace { |
+ |
+// Amount of time that the power button needs to be held before we lock the |
+// screen. |
+const int kLockTimeoutMs = 400; |
+ |
+// Amount of time that the power button needs to be held before we shut down. |
+const int kShutdownTimeoutMs = 400; |
+ |
+// Amount of time to wait for our lock requests to be honored before giving up. |
+const int kLockFailTimeoutMs = 4000; |
+ |
+// When the button has been held continuously from the unlocked state, amount of |
+// time that we wait after the screen locker window is shown before starting the |
+// pre-shutdown animation. |
+const int kLockToShutdownTimeoutMs = 150; |
+ |
+// Amount of time taken to scale the snapshot of the screen down to a |
+// slightly-smaller size once the user starts holding the power button. Used |
+// for both the pre-lock and pre-shutdown animations. |
+const int kSlowCloseAnimMs = 400; |
+ |
+// Amount of time taken to scale the snapshot of the screen back to its original |
+// size when the button is released. |
+const int kUndoSlowCloseAnimMs = 100; |
+ |
+// Amount of time taken to scale the snapshot down to a point in the center of |
+// the screen once the screen has been locked or we've been notified that the |
+// system is shutting down. |
+const int kFastCloseAnimMs = 150; |
+ |
+// Amount of time taken to make the lock window fade in when the screen is |
+// locked. |
+const int kLockFadeInAnimMs = 500; |
+ |
+// Slightly-smaller size that we scale the screen down to for the pre-lock and |
+// pre-shutdown states. |
+const float kSlowCloseSizeRatio = 0.95f; |
+ |
+// Containers holding screen locker windows. |
+const int kScreenLockerContainerIds[] = { |
+ internal::kShellWindowId_LockScreenContainer, |
+ internal::kShellWindowId_LockModalContainer, |
+}; |
+ |
+// Containers holding additional windows that should be shown while the screen |
+// is locked. |
+const int kRelatedContainerIds[] = { |
+ internal::kShellWindowId_StatusContainer, |
+ internal::kShellWindowId_MenusAndTooltipsContainer, |
+}; |
+ |
+// Is |window| a container that holds screen locker windows? |
+bool IsScreenLockerContainer(aura::Window* window) { |
+ for (size_t i = 0; i < arraysize(kScreenLockerContainerIds); ++i) |
+ if (window->id() == kScreenLockerContainerIds[i]) |
+ return true; |
+ return false; |
+} |
+ |
+// Is |window| a container that holds other windows that should be shown while |
+// the screen is locked? |
+bool IsRelatedContainer(aura::Window* window) { |
+ for (size_t i = 0; i < arraysize(kRelatedContainerIds); ++i) |
+ if (window->id() == kRelatedContainerIds[i]) |
+ return true; |
+ return false; |
+} |
+ |
+// Returns the transform that should be applied to containers for the slow-close |
+// animation. |
+ui::Transform GetSlowCloseTransform() { |
+ gfx::Size root_size = aura::RootWindow::GetInstance()->bounds().size(); |
+ ui::Transform transform; |
+ transform.SetScale(kSlowCloseSizeRatio, kSlowCloseSizeRatio); |
+ transform.ConcatTranslate( |
+ floor(0.5 * (1.0 - kSlowCloseSizeRatio) * root_size.width() + 0.5), |
+ floor(0.5 * (1.0 - kSlowCloseSizeRatio) * root_size.height() + 0.5)); |
+ return transform; |
+} |
+ |
+// Returns the transform that should be applied to containers for the fast-close |
+// animation. |
+ui::Transform GetFastCloseTransform() { |
+ gfx::Size root_size = aura::RootWindow::GetInstance()->bounds().size(); |
+ ui::Transform transform; |
+ transform.SetScale(0.0, 0.0); |
+ transform.ConcatTranslate(floor(0.5 * root_size.width() + 0.5), |
+ floor(0.5 * root_size.height() + 0.5)); |
+ return transform; |
+} |
+ |
+// Slowly shrinks |window| to a slightly-smaller size. |
+void StartSlowCloseAnimationForWindow(aura::Window* window) { |
+ ui::LayerAnimator* animator = window->layer()->GetAnimator(); |
+ animator->set_preemption_strategy( |
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
+ animator->StartAnimation( |
+ new ui::LayerAnimationSequence( |
+ ui::LayerAnimationElement::CreateTransformElement( |
+ GetSlowCloseTransform(), |
+ base::TimeDelta::FromMilliseconds(kSlowCloseAnimMs)))); |
+} |
+ |
+// Quickly undoes the effects of the slow-close animation on |window|. |
+void StartUndoSlowCloseAnimationForWindow(aura::Window* window) { |
+ ui::LayerAnimator* animator = window->layer()->GetAnimator(); |
+ animator->set_preemption_strategy( |
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
+ animator->StartAnimation( |
+ new ui::LayerAnimationSequence( |
+ ui::LayerAnimationElement::CreateTransformElement( |
+ ui::Transform(), |
+ base::TimeDelta::FromMilliseconds(kUndoSlowCloseAnimMs)))); |
+} |
+ |
+// Quickly shrinks |window| down to a point in the center of the screen and |
+// fades it out to 0 opacity. |
+void StartFastCloseAnimationForWindow(aura::Window* window) { |
+ ui::LayerAnimator* animator = window->layer()->GetAnimator(); |
+ animator->set_preemption_strategy( |
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
+ animator->StartAnimation( |
+ new ui::LayerAnimationSequence( |
+ ui::LayerAnimationElement::CreateTransformElement( |
+ GetFastCloseTransform(), |
+ base::TimeDelta::FromMilliseconds(kFastCloseAnimMs)))); |
+ animator->StartAnimation( |
+ new ui::LayerAnimationSequence( |
+ ui::LayerAnimationElement::CreateOpacityElement( |
+ 0.0, base::TimeDelta::FromMilliseconds(kFastCloseAnimMs)))); |
+} |
+ |
+// Fades |window| in to full opacity. |
+void FadeInWindow(aura::Window* window) { |
+ ui::LayerAnimator* animator = window->layer()->GetAnimator(); |
+ animator->set_preemption_strategy( |
+ ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET); |
+ animator->StartAnimation( |
+ new ui::LayerAnimationSequence( |
+ ui::LayerAnimationElement::CreateOpacityElement( |
+ 1.0, base::TimeDelta::FromMilliseconds(kLockFadeInAnimMs)))); |
+} |
+ |
+// Makes |window| fully transparent instantaneously. |
+void HideWindow(aura::Window* window) { |
+ window->layer()->SetOpacity(0.0); |
+} |
+ |
+// Restores |window| to its original position and scale and full opacity |
+// instantaneously. |
+void RestoreWindow(aura::Window* window) { |
+ window->layer()->SetTransform(ui::Transform()); |
+ window->layer()->SetOpacity(1.0); |
+} |
+ |
+// Fills |containers| with the containers described by |group|. |
+void GetContainers(PowerButtonController::ContainerGroup group, |
+ aura::Window::Windows* containers) { |
+ containers->clear(); |
+ |
+ aura::Window* root = aura::RootWindow::GetInstance(); |
+ for (aura::Window::Windows::const_iterator it = root->children().begin(); |
+ it != root->children().end(); ++it) { |
+ aura::Window* window = *it; |
+ |
+ bool matched = true; |
+ if (group != PowerButtonController::ALL_CONTAINERS) { |
+ bool is_screen_locker = IsScreenLockerContainer(window); |
+ bool is_related = IsRelatedContainer(window); |
+ |
+ switch (group) { |
+ case PowerButtonController::SCREEN_LOCKER_CONTAINERS: |
+ matched = is_screen_locker; |
+ break; |
+ case PowerButtonController::SCREEN_LOCKER_AND_RELATED_CONTAINERS: |
+ matched = is_screen_locker || is_related; |
+ break; |
+ case PowerButtonController:: |
+ ALL_BUT_SCREEN_LOCKER_AND_RELATED_CONTAINERS: |
+ matched = !is_screen_locker && !is_related; |
+ break; |
+ default: |
+ NOTREACHED() << "Unhandled container group " << group; |
+ } |
+ } |
+ |
+ if (matched) |
+ containers->push_back(window); |
+ } |
+} |
+ |
+// Apply animation |type| to all containers described by |group|. |
+void StartAnimation(PowerButtonController::ContainerGroup group, |
+ PowerButtonController::AnimationType type) { |
+ aura::Window::Windows containers; |
+ GetContainers(group, &containers); |
+ |
+ for (aura::Window::Windows::const_iterator it = containers.begin(); |
+ it != containers.end(); ++it) { |
+ aura::Window* window = *it; |
+ switch (type) { |
+ case PowerButtonController::ANIMATION_SLOW_CLOSE: |
+ StartSlowCloseAnimationForWindow(window); |
+ break; |
+ case PowerButtonController::ANIMATION_UNDO_SLOW_CLOSE: |
+ StartUndoSlowCloseAnimationForWindow(window); |
+ break; |
+ case PowerButtonController::ANIMATION_FAST_CLOSE: |
+ StartFastCloseAnimationForWindow(window); |
+ break; |
+ case PowerButtonController::ANIMATION_FADE_IN: |
+ FadeInWindow(window); |
+ break; |
+ case PowerButtonController::ANIMATION_HIDE: |
+ HideWindow(window); |
+ break; |
+ case PowerButtonController::ANIMATION_RESTORE: |
+ RestoreWindow(window); |
+ break; |
+ default: |
+ NOTREACHED() << "Unhandled animation type " << type; |
+ } |
+ } |
+} |
+ |
+} // namespace |
+ |
+bool PowerButtonController::TestApi::ContainerGroupIsAnimated( |
+ ContainerGroup group, AnimationType type) const { |
+ aura::Window::Windows containers; |
+ GetContainers(group, &containers); |
+ for (aura::Window::Windows::const_iterator it = containers.begin(); |
+ it != containers.end(); ++it) { |
+ aura::Window* window = *it; |
+ ui::Layer* layer = window->layer(); |
+ |
+ switch (type) { |
+ case PowerButtonController::ANIMATION_SLOW_CLOSE: |
+ if (layer->GetTargetTransform() != GetSlowCloseTransform()) |
+ return false; |
+ break; |
+ case PowerButtonController::ANIMATION_UNDO_SLOW_CLOSE: |
+ if (layer->GetTargetTransform() != ui::Transform()) |
+ return false; |
+ break; |
+ case PowerButtonController::ANIMATION_FAST_CLOSE: |
+ if (layer->GetTargetTransform() != GetFastCloseTransform() || |
+ layer->GetTargetOpacity() > 0.0001) |
+ return false; |
+ break; |
+ case PowerButtonController::ANIMATION_FADE_IN: |
+ if (layer->GetTargetOpacity() < 0.9999) |
+ return false; |
+ break; |
+ case PowerButtonController::ANIMATION_HIDE: |
+ if (layer->GetTargetOpacity() > 0.0001) |
+ return false; |
+ break; |
+ case PowerButtonController::ANIMATION_RESTORE: |
+ if (layer->opacity() < 0.9999 || layer->transform() != ui::Transform()) |
+ return false; |
+ break; |
+ default: |
+ NOTREACHED() << "Unhandled animation type " << type; |
+ return false; |
+ } |
+ } |
+ return true; |
+} |
+ |
+bool PowerButtonController::TestApi::BackgroundLayerIsVisible() const { |
+ return controller_->background_layer_.get() && |
+ controller_->background_layer_->visible(); |
+} |
+ |
+// Simple class that fills |background_layer_| with black. |
+class PowerButtonController::BackgroundLayerDelegate |
+ : public ui::LayerDelegate { |
+ public: |
+ BackgroundLayerDelegate() {} |
+ virtual ~BackgroundLayerDelegate() {} |
+ |
+ // ui::LayerDelegate implementation: |
+ virtual void OnPaintLayer(gfx::Canvas* canvas) OVERRIDE { |
+ canvas->FillRect(SK_ColorBLACK, aura::RootWindow::GetInstance()->bounds()); |
+ } |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(BackgroundLayerDelegate); |
+}; |
+ |
+PowerButtonController::PowerButtonController() |
+ : logged_in_as_non_guest_(false), |
+ locked_(false), |
+ power_button_down_(false), |
+ lock_button_down_(false), |
+ shutting_down_(false) { |
+} |
+ |
+PowerButtonController::~PowerButtonController() { |
+} |
+ |
+void PowerButtonController::OnLoginStateChange(bool logged_in, bool is_guest) { |
+ logged_in_as_non_guest_ = logged_in && !is_guest; |
+} |
+ |
+void PowerButtonController::OnLockStateChange(bool locked) { |
+ if (shutting_down_ || locked_ == locked) |
+ return; |
+ |
+ locked_ = locked; |
+ if (locked) { |
+ StartAnimation(SCREEN_LOCKER_CONTAINERS, ANIMATION_FADE_IN); |
+ lock_timer_.Stop(); |
+ lock_fail_timer_.Stop(); |
+ |
+ if (power_button_down_) { |
+ lock_to_shutdown_timer_.Stop(); |
+ lock_to_shutdown_timer_.Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(kLockToShutdownTimeoutMs), |
+ this, &PowerButtonController::OnLockToShutdownTimeout); |
+ } |
+ } else { |
+ StartAnimation(ALL_BUT_SCREEN_LOCKER_AND_RELATED_CONTAINERS, |
+ ANIMATION_RESTORE); |
+ HideBackgroundLayer(); |
+ } |
+} |
+ |
+void PowerButtonController::OnStartingLock() { |
+ if (shutting_down_ || locked_) |
+ return; |
+ |
+ StartAnimation(ALL_BUT_SCREEN_LOCKER_AND_RELATED_CONTAINERS, |
+ ANIMATION_FAST_CLOSE); |
+ |
+ // Hide the screen locker containers so we can make them fade in later. |
+ StartAnimation(SCREEN_LOCKER_CONTAINERS, ANIMATION_HIDE); |
+} |
+ |
+void PowerButtonController::OnPowerButtonEvent( |
+ bool down, const base::TimeTicks& timestamp) { |
+ power_button_down_ = down; |
+ |
+ if (shutting_down_) |
+ return; |
+ |
+ if (down) { |
+ // If we already have a pending request to lock the screen, wait. |
+ if (lock_fail_timer_.IsRunning()) |
+ return; |
+ |
+ if (logged_in_as_non_guest_ && !locked_) { |
+ ShowBackgroundLayer(); |
+ StartAnimation(ALL_BUT_SCREEN_LOCKER_AND_RELATED_CONTAINERS, |
+ ANIMATION_SLOW_CLOSE); |
+ lock_timer_.Stop(); |
+ lock_timer_.Start(FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(kSlowCloseAnimMs), |
+ this, &PowerButtonController::OnLockTimeout); |
+ } else { |
+ StartShutdownTimer(); |
+ } |
+ } else { // Button is up. |
+ if (lock_timer_.IsRunning() || shutdown_timer_.IsRunning()) |
+ StartAnimation( |
+ locked_ ? SCREEN_LOCKER_AND_RELATED_CONTAINERS : ALL_CONTAINERS, |
+ ANIMATION_UNDO_SLOW_CLOSE); |
+ |
+ // Drop the background layer after the undo animation finishes. |
+ if (lock_timer_.IsRunning() || |
+ (shutdown_timer_.IsRunning() && !logged_in_as_non_guest_)) { |
+ hide_background_layer_timer_.Stop(); |
+ hide_background_layer_timer_.Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(kUndoSlowCloseAnimMs), |
+ this, &PowerButtonController::HideBackgroundLayer); |
+ } |
+ |
+ lock_timer_.Stop(); |
+ shutdown_timer_.Stop(); |
+ lock_to_shutdown_timer_.Stop(); |
+ } |
+} |
+ |
+void PowerButtonController::OnLockButtonEvent( |
+ bool down, const base::TimeTicks& timestamp) { |
+ lock_button_down_ = down; |
+ |
+ if (shutting_down_) |
+ return; |
+ |
+ NOTIMPLEMENTED(); |
+} |
+ |
+void PowerButtonController::OnRootWindowResized(const gfx::Size& new_size) { |
+ if (background_layer_.get()) |
+ background_layer_->SetBounds(gfx::Rect(new_size)); |
+} |
+ |
+void PowerButtonController::OnLockTimeout() { |
+ delegate_->RequestLockScreen(); |
+ lock_fail_timer_.Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(kLockFailTimeoutMs), |
+ this, &PowerButtonController::OnLockFailTimeout); |
+} |
+ |
+void PowerButtonController::OnLockFailTimeout() { |
+ DCHECK(!locked_); |
+ LOG(ERROR) << "Screen lock request timed out"; |
+ StartAnimation(ALL_BUT_SCREEN_LOCKER_AND_RELATED_CONTAINERS, |
+ ANIMATION_RESTORE); |
+ HideBackgroundLayer(); |
+} |
+ |
+void PowerButtonController::OnLockToShutdownTimeout() { |
+ DCHECK(locked_); |
+ StartShutdownTimer(); |
+} |
+ |
+void PowerButtonController::OnShutdownTimeout() { |
+ DCHECK(!shutting_down_); |
+ shutting_down_ = true; |
+ aura::RootWindow::GetInstance()->ShowCursor(false); |
+ StartAnimation(ALL_CONTAINERS, ANIMATION_FAST_CLOSE); |
+ real_shutdown_timer_.Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(kFastCloseAnimMs), |
+ this, &PowerButtonController::OnRealShutdownTimeout); |
+} |
+ |
+void PowerButtonController::OnRealShutdownTimeout() { |
+ DCHECK(shutting_down_); |
+ delegate_->RequestShutdown(); |
+} |
+ |
+void PowerButtonController::StartShutdownTimer() { |
+ ShowBackgroundLayer(); |
+ StartAnimation(ALL_CONTAINERS, ANIMATION_SLOW_CLOSE); |
+ shutdown_timer_.Stop(); |
+ shutdown_timer_.Start( |
+ FROM_HERE, |
+ base::TimeDelta::FromMilliseconds(kShutdownTimeoutMs), |
+ this, &PowerButtonController::OnShutdownTimeout); |
+} |
+ |
+void PowerButtonController::ShowBackgroundLayer() { |
+ if (hide_background_layer_timer_.IsRunning()) |
+ hide_background_layer_timer_.Stop(); |
+ |
+ if (!background_layer_.get()) { |
+ background_layer_delegate_.reset(new BackgroundLayerDelegate); |
+ background_layer_.reset(new ui::Layer(ui::Layer::LAYER_HAS_TEXTURE)); |
+ background_layer_->set_delegate(background_layer_delegate_.get()); |
+ |
+ ui::Layer* root_layer = aura::RootWindow::GetInstance()->layer(); |
+ background_layer_->SetBounds(root_layer->bounds()); |
+ root_layer->Add(background_layer_.get()); |
+ root_layer->StackAtBottom(background_layer_.get()); |
+ } |
+ background_layer_->SetVisible(true); |
+} |
+ |
+void PowerButtonController::HideBackgroundLayer() { |
+ background_layer_.reset(); |
+} |
+ |
+} // namespace ash |