blob: c5301b7409986fedc22c1a84d6a6d04dee8fbc07 [file] [log] [blame]
// Copyright 2013 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/lock_state_controller_impl2.h"
#include "ash/ash_switches.h"
#include "ash/cancel_mode.h"
#include "ash/shell.h"
#include "ash/shell_delegate.h"
#include "ash/shell_window_ids.h"
#include "ash/wm/session_state_animator.h"
#include "base/bind_helpers.h"
#include "base/command_line.h"
#include "base/timer/timer.h"
#include "ui/aura/root_window.h"
#include "ui/compositor/layer_animation_sequence.h"
#include "ui/compositor/scoped_layer_animation_settings.h"
#include "ui/views/corewm/compound_event_filter.h"
#if defined(OS_CHROMEOS)
#include "base/chromeos/chromeos_version.h"
#endif
namespace ash {
namespace {
aura::Window* GetBackground() {
aura::RootWindow* root_window = Shell::GetPrimaryRootWindow();
return Shell::GetContainer(root_window,
internal::kShellWindowId_DesktopBackgroundContainer);
}
bool IsBackgroundHidden() {
return !GetBackground()->IsVisible();
}
void ShowBackground() {
ui::ScopedLayerAnimationSettings settings(
GetBackground()->layer()->GetAnimator());
settings.SetTransitionDuration(base::TimeDelta());
GetBackground()->Show();
}
void HideBackground() {
ui::ScopedLayerAnimationSettings settings(
GetBackground()->layer()->GetAnimator());
settings.SetTransitionDuration(base::TimeDelta());
GetBackground()->Hide();
}
// This observer is intended to use in cases when some action has to be taken
// once some animation successfully completes (i.e. it was not aborted).
// Observer will count a number of sequences it is attached to, and a number of
// finished sequences (either Ended or Aborted). Once these two numbers are
// equal, observer will delete itself, calling callback passed to constructor if
// there were no aborted animations.
// This way it can be either used to wait for some animation to be finished in
// multiple layers, to wait once a sequence of animations is finished in one
// layer or the mixture of both.
class AnimationFinishedObserver : public ui::LayerAnimationObserver {
public:
explicit AnimationFinishedObserver(base::Closure &callback)
: callback_(callback),
sequences_attached_(0),
sequences_completed_(0),
paused_(false) {
}
// Pauses observer: no checks will be made while paused. It can be used when
// a sequence has some immediate animations in the beginning, and for
// animations that can be tested with flag that makes all animations
// immediate.
void Pause() {
paused_ = true;
}
// Unpauses observer. It does a check and calls callback if conditions are
// met.
void Unpause() {
if (!paused_)
return;
paused_ = false;
if (sequences_completed_ == sequences_attached_) {
callback_.Run();
delete this;
}
}
private:
virtual ~AnimationFinishedObserver() {
}
// LayerAnimationObserver implementation
virtual void OnLayerAnimationEnded(
ui::LayerAnimationSequence* sequence) OVERRIDE {
sequences_completed_++;
if ((sequences_completed_ == sequences_attached_) && !paused_) {
callback_.Run();
delete this;
}
}
virtual void OnLayerAnimationAborted(
ui::LayerAnimationSequence* sequence) OVERRIDE {
sequences_completed_++;
if ((sequences_completed_ == sequences_attached_) && !paused_)
delete this;
}
virtual void OnLayerAnimationScheduled(
ui::LayerAnimationSequence* sequence) OVERRIDE {
}
virtual void OnAttachedToSequence(
ui::LayerAnimationSequence* sequence) OVERRIDE {
LayerAnimationObserver::OnAttachedToSequence(sequence);
sequences_attached_++;
}
// Callback to be called.
base::Closure callback_;
// Number of sequences this observer was attached to.
int sequences_attached_;
// Number of sequences either ended or aborted.
int sequences_completed_;
bool paused_;
DISALLOW_COPY_AND_ASSIGN(AnimationFinishedObserver);
};
} // namespace
LockStateControllerImpl2::TestApi::TestApi(
LockStateControllerImpl2* controller)
: controller_(controller) {
}
LockStateControllerImpl2::TestApi::~TestApi() {
}
LockStateControllerImpl2::LockStateControllerImpl2()
: login_status_(user::LOGGED_IN_NONE),
system_is_locked_(false),
shutting_down_(false),
shutdown_after_lock_(false),
animating_lock_(false),
can_cancel_lock_animation_(false) {
Shell::GetPrimaryRootWindow()->AddRootWindowObserver(this);
}
LockStateControllerImpl2::~LockStateControllerImpl2() {
Shell::GetPrimaryRootWindow()->RemoveRootWindowObserver(this);
}
void LockStateControllerImpl2::OnLoginStateChanged(
user::LoginStatus status) {
if (status != user::LOGGED_IN_LOCKED)
login_status_ = status;
system_is_locked_ = (status == user::LOGGED_IN_LOCKED);
}
void LockStateControllerImpl2::OnAppTerminating() {
// If we hear that Chrome is exiting but didn't request it ourselves, all we
// can really hope for is that we'll have time to clear the screen.
// This is also the case when the user signs off.
if (!shutting_down_) {
shutting_down_ = true;
Shell* shell = ash::Shell::GetInstance();
shell->env_filter()->set_cursor_hidden_by_filter(false);
shell->cursor_manager()->HideCursor();
animator_->StartAnimation(
internal::SessionStateAnimator::kAllContainersMask,
internal::SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
internal::SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
}
}
void LockStateControllerImpl2::OnLockStateChanged(bool locked) {
if (shutting_down_ || (system_is_locked_ == locked))
return;
system_is_locked_ = locked;
if (locked) {
StartPostLockAnimation();
lock_fail_timer_.Stop();
} else {
StartUnlockAnimationAfterUIDestroyed();
}
}
void LockStateControllerImpl2::SetLockScreenDisplayedCallback(
base::Closure& callback) {
lock_screen_displayed_callback_ = callback;
}
void LockStateControllerImpl2::OnStartingLock() {
if (shutting_down_ || system_is_locked_)
return;
if (animating_lock_)
return;
StartImmediatePreLockAnimation(false /* request_lock_on_completion */);
}
void LockStateControllerImpl2::StartLockAnimationAndLockImmediately() {
if (animating_lock_)
return;
StartImmediatePreLockAnimation(true /* request_lock_on_completion */);
}
void LockStateControllerImpl2::StartLockAnimation(
bool shutdown_after_lock) {
if (animating_lock_)
return;
shutdown_after_lock_ = shutdown_after_lock;
can_cancel_lock_animation_ = true;
StartCancellablePreLockAnimation();
}
bool LockStateControllerImpl2::LockRequested() {
return lock_fail_timer_.IsRunning();
}
bool LockStateControllerImpl2::ShutdownRequested() {
return shutting_down_;
}
bool LockStateControllerImpl2::CanCancelLockAnimation() {
return can_cancel_lock_animation_;
}
void LockStateControllerImpl2::CancelLockAnimation() {
if (!CanCancelLockAnimation())
return;
shutdown_after_lock_ = false;
animating_lock_ = false;
CancelPreLockAnimation();
}
bool LockStateControllerImpl2::CanCancelShutdownAnimation() {
return pre_shutdown_timer_.IsRunning() ||
shutdown_after_lock_ ||
lock_to_shutdown_timer_.IsRunning();
}
void LockStateControllerImpl2::StartShutdownAnimation() {
StartCancellableShutdownAnimation();
}
void LockStateControllerImpl2::CancelShutdownAnimation() {
if (!CanCancelShutdownAnimation())
return;
if (lock_to_shutdown_timer_.IsRunning()) {
lock_to_shutdown_timer_.Stop();
return;
}
if (shutdown_after_lock_) {
shutdown_after_lock_ = false;
return;
}
animator_->StartGlobalAnimation(
internal::SessionStateAnimator::ANIMATION_UNDO_GRAYSCALE_BRIGHTNESS,
internal::SessionStateAnimator::ANIMATION_SPEED_REVERT_SHUTDOWN);
pre_shutdown_timer_.Stop();
}
void LockStateControllerImpl2::RequestShutdown() {
if (!shutting_down_)
RequestShutdownImpl();
}
void LockStateControllerImpl2::RequestShutdownImpl() {
DCHECK(!shutting_down_);
shutting_down_ = true;
Shell* shell = ash::Shell::GetInstance();
shell->env_filter()->set_cursor_hidden_by_filter(false);
shell->cursor_manager()->HideCursor();
StartShutdownAnimationImpl();
}
void LockStateControllerImpl2::OnRootWindowHostCloseRequested(
const aura::RootWindow*) {
Shell::GetInstance()->delegate()->Exit();
}
void LockStateControllerImpl2::OnLockFailTimeout() {
DCHECK(!system_is_locked_);
// Undo lock animation.
StartUnlockAnimationAfterUIDestroyed();
}
void LockStateControllerImpl2::StartLockToShutdownTimer() {
shutdown_after_lock_ = false;
lock_to_shutdown_timer_.Stop();
lock_to_shutdown_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kLockToShutdownTimeoutMs),
this, &LockStateControllerImpl2::OnLockToShutdownTimeout);
}
void LockStateControllerImpl2::OnLockToShutdownTimeout() {
DCHECK(system_is_locked_);
StartCancellableShutdownAnimation();
}
void LockStateControllerImpl2::StartCancellableShutdownAnimation() {
Shell* shell = ash::Shell::GetInstance();
// Hide cursor, but let it reappear if the mouse moves.
shell->env_filter()->set_cursor_hidden_by_filter(true);
shell->cursor_manager()->HideCursor();
animator_->StartGlobalAnimation(
internal::SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS,
internal::SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
StartPreShutdownAnimationTimer();
}
void LockStateControllerImpl2::StartShutdownAnimationImpl() {
animator_->StartGlobalAnimation(
internal::SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS,
internal::SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
StartRealShutdownTimer(true);
}
void LockStateControllerImpl2::StartPreShutdownAnimationTimer() {
pre_shutdown_timer_.Stop();
pre_shutdown_timer_.Start(
FROM_HERE,
animator_->
GetDuration(internal::SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN),
this,
&LockStateControllerImpl2::OnPreShutdownAnimationTimeout);
}
void LockStateControllerImpl2::OnPreShutdownAnimationTimeout() {
shutting_down_ = true;
Shell* shell = ash::Shell::GetInstance();
shell->env_filter()->set_cursor_hidden_by_filter(false);
shell->cursor_manager()->HideCursor();
StartRealShutdownTimer(false);
}
void LockStateControllerImpl2::StartRealShutdownTimer(
bool with_animation_time) {
base::TimeDelta duration =
base::TimeDelta::FromMilliseconds(kShutdownRequestDelayMs);
if (with_animation_time) {
duration += animator_->GetDuration(
internal::SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
}
real_shutdown_timer_.Start(
FROM_HERE,
duration,
this,
&LockStateControllerImpl2::OnRealShutdownTimeout);
}
void LockStateControllerImpl2::OnRealShutdownTimeout() {
DCHECK(shutting_down_);
#if defined(OS_CHROMEOS)
if (!base::chromeos::IsRunningOnChromeOS()) {
ShellDelegate* delegate = Shell::GetInstance()->delegate();
if (delegate) {
delegate->Exit();
return;
}
}
#endif
Shell::GetInstance()->delegate()->RecordUserMetricsAction(
UMA_ACCEL_SHUT_DOWN_POWER_BUTTON);
delegate_->RequestShutdown();
}
void LockStateControllerImpl2::OnLockScreenHide(
base::Callback<void(void)>& callback) {
StartUnlockAnimationBeforeUIDestroyed(callback);
}
void LockStateControllerImpl2::LockAnimationCancelled() {
can_cancel_lock_animation_ = false;
RestoreUnlockedProperties();
}
void LockStateControllerImpl2::PreLockAnimationFinished(
bool request_lock) {
can_cancel_lock_animation_ = false;
if (request_lock) {
Shell::GetInstance()->delegate()->RecordUserMetricsAction(
shutdown_after_lock_ ?
UMA_ACCEL_LOCK_SCREEN_POWER_BUTTON :
UMA_ACCEL_LOCK_SCREEN_LOCK_BUTTON);
delegate_->RequestLockScreen();
}
lock_fail_timer_.Start(
FROM_HERE,
base::TimeDelta::FromMilliseconds(kLockFailTimeoutMs),
this,
&LockStateControllerImpl2::OnLockFailTimeout);
}
void LockStateControllerImpl2::PostLockAnimationFinished() {
animating_lock_ = false;
FOR_EACH_OBSERVER(LockStateObserver, observers_,
OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_FINISHED));
if (!lock_screen_displayed_callback_.is_null()) {
lock_screen_displayed_callback_.Run();
lock_screen_displayed_callback_.Reset();
}
if (shutdown_after_lock_) {
shutdown_after_lock_ = false;
StartLockToShutdownTimer();
}
}
void LockStateControllerImpl2::
UnlockAnimationAfterUIDestroyedFinished() {
RestoreUnlockedProperties();
}
void LockStateControllerImpl2::StartImmediatePreLockAnimation(
bool request_lock_on_completion) {
animating_lock_ = true;
StoreUnlockedProperties();
base::Closure next_animation_starter =
base::Bind(&LockStateControllerImpl2::PreLockAnimationFinished,
base::Unretained(this), request_lock_on_completion);
AnimationFinishedObserver* observer =
new AnimationFinishedObserver(next_animation_starter);
observer->Pause();
animator_->StartAnimationWithObserver(
internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
internal::SessionStateAnimator::ANIMATION_LIFT,
internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
observer);
animator_->StartAnimationWithObserver(
internal::SessionStateAnimator::LAUNCHER,
internal::SessionStateAnimator::ANIMATION_FADE_OUT,
internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
observer);
// Hide the screen locker containers so we can raise them later.
animator_->StartAnimation(
internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
internal::SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
internal::SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
AnimateBackgroundAppearanceIfNecessary(
internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
observer);
observer->Unpause();
DispatchCancelMode();
FOR_EACH_OBSERVER(LockStateObserver, observers_,
OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_STARTED));
}
void LockStateControllerImpl2::StartCancellablePreLockAnimation() {
animating_lock_ = true;
StoreUnlockedProperties();
base::Closure next_animation_starter =
base::Bind(&LockStateControllerImpl2::PreLockAnimationFinished,
base::Unretained(this), true /* request_lock */);
AnimationFinishedObserver* observer =
new AnimationFinishedObserver(next_animation_starter);
observer->Pause();
animator_->StartAnimationWithObserver(
internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
internal::SessionStateAnimator::ANIMATION_LIFT,
internal::SessionStateAnimator::ANIMATION_SPEED_UNDOABLE,
observer);
animator_->StartAnimationWithObserver(
internal::SessionStateAnimator::LAUNCHER,
internal::SessionStateAnimator::ANIMATION_FADE_OUT,
internal::SessionStateAnimator::ANIMATION_SPEED_UNDOABLE,
observer);
// Hide the screen locker containers so we can raise them later.
animator_->StartAnimation(
internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
internal::SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
internal::SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
AnimateBackgroundAppearanceIfNecessary(
internal::SessionStateAnimator::ANIMATION_SPEED_UNDOABLE,
observer);
DispatchCancelMode();
FOR_EACH_OBSERVER(LockStateObserver, observers_,
OnLockStateEvent(LockStateObserver::EVENT_PRELOCK_ANIMATION_STARTED));
observer->Unpause();
}
void LockStateControllerImpl2::CancelPreLockAnimation() {
base::Closure next_animation_starter =
base::Bind(&LockStateControllerImpl2::LockAnimationCancelled,
base::Unretained(this));
AnimationFinishedObserver* observer =
new AnimationFinishedObserver(next_animation_starter);
observer->Pause();
animator_->StartAnimationWithObserver(
internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
internal::SessionStateAnimator::ANIMATION_UNDO_LIFT,
internal::SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS,
observer);
animator_->StartAnimationWithObserver(
internal::SessionStateAnimator::LAUNCHER,
internal::SessionStateAnimator::ANIMATION_FADE_IN,
internal::SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS,
observer);
AnimateBackgroundHidingIfNecessary(
internal::SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS,
observer);
observer->Unpause();
}
void LockStateControllerImpl2::StartPostLockAnimation() {
base::Closure next_animation_starter =
base::Bind(&LockStateControllerImpl2::PostLockAnimationFinished,
base::Unretained(this));
AnimationFinishedObserver* observer =
new AnimationFinishedObserver(next_animation_starter);
observer->Pause();
animator_->StartAnimationWithObserver(
internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
internal::SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN,
internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
observer);
observer->Unpause();
}
void LockStateControllerImpl2::StartUnlockAnimationBeforeUIDestroyed(
base::Closure& callback) {
animator_->StartAnimationWithCallback(
internal::SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
internal::SessionStateAnimator::ANIMATION_LIFT,
internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
callback);
}
void LockStateControllerImpl2::StartUnlockAnimationAfterUIDestroyed() {
base::Closure next_animation_starter =
base::Bind(
&LockStateControllerImpl2::
UnlockAnimationAfterUIDestroyedFinished,
base::Unretained(this));
AnimationFinishedObserver* observer =
new AnimationFinishedObserver(next_animation_starter);
observer->Pause();
animator_->StartAnimationWithObserver(
internal::SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
internal::SessionStateAnimator::ANIMATION_DROP,
internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
observer);
animator_->StartAnimationWithObserver(
internal::SessionStateAnimator::LAUNCHER,
internal::SessionStateAnimator::ANIMATION_FADE_IN,
internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
observer);
AnimateBackgroundHidingIfNecessary(
internal::SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
observer);
observer->Unpause();
}
void LockStateControllerImpl2::StoreUnlockedProperties() {
if (!unlocked_properties_) {
unlocked_properties_.reset(new UnlockedStateProperties());
unlocked_properties_->background_is_hidden = IsBackgroundHidden();
}
if (unlocked_properties_->background_is_hidden) {
// Hide background so that it can be animated later.
animator_->StartAnimation(
internal::SessionStateAnimator::DESKTOP_BACKGROUND,
internal::SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
internal::SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
ShowBackground();
}
}
void LockStateControllerImpl2::RestoreUnlockedProperties() {
if (!unlocked_properties_)
return;
if (unlocked_properties_->background_is_hidden) {
HideBackground();
// Restore background visibility.
animator_->StartAnimation(
internal::SessionStateAnimator::DESKTOP_BACKGROUND,
internal::SessionStateAnimator::ANIMATION_FADE_IN,
internal::SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
}
unlocked_properties_.reset();
}
void LockStateControllerImpl2::AnimateBackgroundAppearanceIfNecessary(
internal::SessionStateAnimator::AnimationSpeed speed,
ui::LayerAnimationObserver* observer) {
if (unlocked_properties_.get() &&
unlocked_properties_->background_is_hidden) {
animator_->StartAnimationWithObserver(
internal::SessionStateAnimator::DESKTOP_BACKGROUND,
internal::SessionStateAnimator::ANIMATION_FADE_IN,
speed,
observer);
}
}
void LockStateControllerImpl2::AnimateBackgroundHidingIfNecessary(
internal::SessionStateAnimator::AnimationSpeed speed,
ui::LayerAnimationObserver* observer) {
if (unlocked_properties_.get() &&
unlocked_properties_->background_is_hidden) {
animator_->StartAnimationWithObserver(
internal::SessionStateAnimator::DESKTOP_BACKGROUND,
internal::SessionStateAnimator::ANIMATION_FADE_OUT,
speed,
observer);
}
}
} // namespace ash