| 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
|
|
|