diff options
author | skuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-07 19:18:27 +0000 |
---|---|---|
committer | skuhne@chromium.org <skuhne@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-07 19:18:27 +0000 |
commit | 9dd7a656fa210d93c62a7356f902f3e3e002222f (patch) | |
tree | 94150c179225cddefabfd3d9a9b8610fc357904b /chrome/browser/ui/ash | |
parent | 885e7200515b1e211188db537190f6ccc955971f (diff) | |
download | chromium_src-9dd7a656fa210d93c62a7356f902f3e3e002222f.zip chromium_src-9dd7a656fa210d93c62a7356f902f3e3e002222f.tar.gz chromium_src-9dd7a656fa210d93c62a7356f902f3e3e002222f.tar.bz2 |
Improving the user transition to add special cases for maximized windows and make the transition "more fluid"
There were user complaints with the old animation which said that the animation looked janky. This was in part because various transitions (e.g. maximized->maximized) were showing the background animating in the middle of the animation which looked like "flashing") and because there are some real causes where the CPU gets 100% hogged and the UI thread has not time processing the animation.
This is addressing the first part: Improving the animation by removing the "flash" in the middle and instead transitioning the desktop cleanly from one state to another.
The shelf animation stays the same, but the windows will cross dissolve. When switching from maximize to maximize, the desktop will not shine through and when switching from / or to maximize, the desktop background will only show the desktop background from the user which is not maximized. This way the intermediate frames do not show "some state".
BUG=357852
TEST=unittest & visual checking
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=261944
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=262024
Review URL: https://codereview.chromium.org/223823004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@262174 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ui/ash')
5 files changed, 702 insertions, 233 deletions
diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc index 2bedb08..180588e 100644 --- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc +++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.cc @@ -7,17 +7,13 @@ #include "apps/app_window.h" #include "apps/app_window_registry.h" #include "ash/ash_switches.h" -#include "ash/desktop_background/user_wallpaper_delegate.h" #include "ash/multi_profile_uma.h" #include "ash/root_window_controller.h" #include "ash/session_state_delegate.h" #include "ash/shelf/shelf.h" -#include "ash/shelf/shelf_layout_manager.h" #include "ash/shell.h" #include "ash/shell_delegate.h" #include "ash/shell_window_ids.h" -#include "ash/wm/mru_window_tracker.h" -#include "ash/wm/window_positioner.h" #include "ash/wm/window_state.h" #include "base/auto_reset.h" #include "base/message_loop/message_loop.h" @@ -25,12 +21,11 @@ #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/login/user_manager.h" -#include "chrome/browser/chromeos/login/wallpaper_manager.h" #include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile_manager.h" -#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" #include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h" #include "chrome/browser/ui/ash/multi_user/multi_user_util.h" +#include "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_finder.h" #include "chrome/browser/ui/browser_list.h" @@ -46,23 +41,18 @@ #include "ui/wm/core/transient_window_manager.h" #include "ui/wm/core/window_animations.h" #include "ui/wm/core/window_util.h" -#include "ui/wm/public/activation_client.h" namespace { // The animation time in milliseconds for a single window which is fading // in / out. -static int kAnimationTimeMS = 100; - -// The animation time in millseconds for the fade in and / or out when switching -// users. -static int kUserFadeTimeMS = 110; +const int kAnimationTimeMS = 100; // The animation time in ms for a window which get teleported to another screen. -static int kTeleportAnimationTimeMS = 300; +const int kTeleportAnimationTimeMS = 300; // Checks if a given event is a user event. -bool IsUserEvent(ui::Event* e) { +bool IsUserEvent(const ui::Event* e) { if (e) { ui::EventType type = e->type(); if (type != ui::ET_CANCEL_MODE && @@ -187,24 +177,6 @@ class AnimationSetter { DISALLOW_COPY_AND_ASSIGN(AnimationSetter); }; -// logic while the user gets switched. -class UserChangeActionDisabler { - public: - UserChangeActionDisabler() { - ash::WindowPositioner::DisableAutoPositioning(true); - ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(true); - } - - ~UserChangeActionDisabler() { - ash::WindowPositioner::DisableAutoPositioning(false); - ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations( - false); - } - private: - - DISALLOW_COPY_AND_ASSIGN(UserChangeActionDisabler); -}; - // This class keeps track of all applications which were started for a user. // When an app gets created, the window will be tagged for that user. Note // that the destruction does not need to be tracked here since the universal @@ -411,30 +383,11 @@ void MultiUserWindowManagerChromeOS::RemoveObserver(Observer* observer) { void MultiUserWindowManagerChromeOS::ActiveUserChanged( const std::string& user_id) { DCHECK(user_id != current_user_id_); + // This needs to be set before the animation starts. current_user_id_ = user_id; - // If there is an animation in progress finish the pending switch which also - // kills the timer (if there is one). - if (user_changed_animation_timer_.get()) - TransitionUser(SHOW_NEW_USER); - - // Start the animation by hiding the old user. - TransitionUser(HIDE_OLD_USER); - - // If animations are disabled we immediately switch to the new user, otherwise - // we create a timer which will fade in the new user once the other user has - // been faded away. - if (animations_disabled_) { - TransitionUser(SHOW_NEW_USER); - } else { - user_changed_animation_timer_.reset(new base::Timer( - FROM_HERE, - base::TimeDelta::FromMilliseconds(kUserFadeTimeMS), - base::Bind(&MultiUserWindowManagerChromeOS::TransitionUser, - base::Unretained(this), - SHOW_NEW_USER), - false)); - user_changed_animation_timer_->Reset(); - } + + animation_.reset( + new UserSwichAnimatorChromeOS(this, user_id, animations_disabled_)); } void MultiUserWindowManagerChromeOS::OnWindowDestroyed(aura::Window* window) { @@ -531,7 +484,7 @@ void MultiUserWindowManagerChromeOS::SetAnimationsForTest(bool disable) { } bool MultiUserWindowManagerChromeOS::IsAnimationRunningForTest() { - return user_changed_animation_timer_.get() != NULL; + return animation_.get() != NULL && !animation_->IsAnimationFinished(); } const std::string& MultiUserWindowManagerChromeOS::GetCurrentUserForTest() { @@ -580,149 +533,6 @@ bool MultiUserWindowManagerChromeOS::ShowWindowForUserIntern( return true; } -void MultiUserWindowManagerChromeOS::TransitionUser( - MultiUserWindowManagerChromeOS::AnimationStep animation_step) { - TransitionWallpaper(animation_step); - TransitionUserShelf(animation_step); - - // Disable the window position manager and the MRU window tracker temporarily. - scoped_ptr<UserChangeActionDisabler> disabler(new UserChangeActionDisabler); - - // We need to show/hide the windows in the same order as they were created in - // their parent window(s) to keep the layer / window hierarchy in sync. To - // achieve that we first collect all parent windows and then enumerate all - // windows in those parent windows and show or hide them accordingly. - - // Create a list of all parent windows we have to check and their parents. - std::set<aura::Window*> parent_list; - for (WindowToEntryMap::iterator it = window_to_entry_.begin(); - it != window_to_entry_.end(); ++it) { - aura::Window* parent = it->first->parent(); - if (parent_list.find(parent) == parent_list.end()) - parent_list.insert(parent); - } - - // Traverse the found parent windows to handle their child windows in order of - // their appearance. - for (std::set<aura::Window*>::iterator it_parents = parent_list.begin(); - it_parents != parent_list.end(); ++it_parents) { - const aura::Window::Windows window_list = (*it_parents)->children(); - for (aura::Window::Windows::const_iterator it_window = window_list.begin(); - it_window != window_list.end(); ++it_window) { - aura::Window* window = *it_window; - WindowToEntryMap::iterator it_map = window_to_entry_.find(window); - if (it_map != window_to_entry_.end()) { - bool should_be_visible = - it_map->second->show_for_user() == current_user_id_ && - it_map->second->show(); - bool is_visible = window->IsVisible(); - ash::wm::WindowState* window_state = ash::wm::GetWindowState(window); - if (animation_step == SHOW_NEW_USER && - it_map->second->owner() == current_user_id_ && - it_map->second->show_for_user() != current_user_id_ && - window_state->IsMinimized()) { - // Pull back minimized visiting windows to the owners desktop. - ShowWindowForUserIntern(window, current_user_id_); - window_state->Unminimize(); - } else if (should_be_visible != is_visible && - should_be_visible == (animation_step == SHOW_NEW_USER)) { - SetWindowVisibility(window, should_be_visible, kUserFadeTimeMS); - } - } - } - } - - // Activation and real switch are happening after the other user gets shown. - if (animation_step == SHOW_NEW_USER) { - // Finally we need to restore the previously active window. - ash::MruWindowTracker::WindowList mru_list = - ash::Shell::GetInstance()->mru_window_tracker()->BuildMruWindowList(); - if (mru_list.size()) { - aura::Window* window = mru_list[0]; - ash::wm::WindowState* window_state = ash::wm::GetWindowState(window); - if (IsWindowOnDesktopOfUser(window, current_user_id_) && - !window_state->IsMinimized()) { - aura::client::ActivationClient* client = - aura::client::GetActivationClient(window->GetRootWindow()); - // Several unit tests come here without an activation client. - if (client) - client->ActivateWindow(window); - } - } - - // This is called directly here to make sure notification_blocker will see - // the new window status. - notification_blocker_->ActiveUserChanged(current_user_id_); - - // We can reset the timer at this point. - // Note: The timer can be destroyed while it is performing its task. - user_changed_animation_timer_.reset(); - } -} - -void MultiUserWindowManagerChromeOS::TransitionWallpaper( - MultiUserWindowManagerChromeOS::AnimationStep animation_step) { - // Handle the wallpaper switch. - ash::UserWallpaperDelegate* wallpaper_delegate = - ash::Shell::GetInstance()->user_wallpaper_delegate(); - if (animation_step == HIDE_OLD_USER) { - // Set the wallpaper cross dissolve animation duration to our complete - // animation cycle for a fade in and fade out. - wallpaper_delegate->SetAnimationDurationOverride(2 * kUserFadeTimeMS); - chromeos::WallpaperManager::Get()->SetUserWallpaperDelayed( - current_user_id_); - } else { - // Revert the wallpaper cross dissolve animation duration back to the - // default. - wallpaper_delegate->SetAnimationDurationOverride(0); - } -} - -void MultiUserWindowManagerChromeOS::TransitionUserShelf( - MultiUserWindowManagerChromeOS::AnimationStep animation_step) { - // The shelf animation duration override. - int duration_override = kUserFadeTimeMS; - // Handle the shelf order of items. This is done once the old user is hidden. - if (animation_step == SHOW_NEW_USER) { - // Some unit tests have no ChromeLauncherController. - if (ChromeLauncherController::instance()) - ChromeLauncherController::instance()->ActiveUserChanged(current_user_id_); - // We kicked off the shelf animation in the command above. As such we can - // disable the override now again. - duration_override = 0; - } - - if (animations_disabled_) - return; - - ash::Shell::RootWindowControllerList controller = - ash::Shell::GetInstance()->GetAllRootWindowControllers(); - for (ash::Shell::RootWindowControllerList::iterator it1 = controller.begin(); - it1 != controller.end(); ++it1) { - (*it1)->GetShelfLayoutManager()->SetAnimationDurationOverride( - duration_override); - } - - // For each root window hide the shelf. - if (animation_step == HIDE_OLD_USER) { - aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows(); - for (aura::Window::Windows::const_iterator iter = root_windows.begin(); - iter != root_windows.end(); ++iter) { - ash::Shell::GetInstance()->SetShelfAutoHideBehavior( - ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN, *iter); - } - } -} - -void MultiUserWindowManagerChromeOS::AddBrowserWindow(Browser* browser) { - // A unit test (e.g. CrashRestoreComplexTest.RestoreSessionForThreeUsers) can - // come here with no valid window. - if (!browser->window() || !browser->window()->GetNativeWindow()) - return; - SetWindowOwner(browser->window()->GetNativeWindow(), - multi_user_util::GetUserIDFromProfile(browser->profile())); -} - void MultiUserWindowManagerChromeOS::SetWindowVisibility( aura::Window* window, bool visible, int animation_time_in_ms) { if (window->IsVisible() == visible) @@ -766,6 +576,15 @@ void MultiUserWindowManagerChromeOS::SetWindowVisibility( } } +void MultiUserWindowManagerChromeOS::AddBrowserWindow(Browser* browser) { + // A unit test (e.g. CrashRestoreComplexTest.RestoreSessionForThreeUsers) can + // come here with no valid window. + if (!browser->window() || !browser->window()->GetNativeWindow()) + return; + SetWindowOwner(browser->window()->GetNativeWindow(), + multi_user_util::GetUserIDFromProfile(browser->profile())); +} + void MultiUserWindowManagerChromeOS::ShowWithTransientChildrenRecursive( aura::Window* window, int animation_time_in_ms) { aura::Window::Windows::const_iterator it = diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h index 644d5ca..1cc1a0c 100644 --- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h +++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h @@ -12,7 +12,6 @@ #include "base/compiler_specific.h" #include "base/memory/scoped_ptr.h" #include "base/observer_list.h" -#include "base/timer/timer.h" #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h" #include "content/public/browser/notification_observer.h" #include "content/public/browser/notification_registrar.h" @@ -40,6 +39,7 @@ class MultiUserWindowManagerChromeOSTest; namespace chrome { class AppObserver; +class UserSwichAnimatorChromeOS; // This ChromeOS implementation of the MultiUserWindowManager interface is // detecting app and browser creations, tagging their windows automatically and @@ -112,9 +112,8 @@ class MultiUserWindowManagerChromeOS // Returns the current user for unit tests. const std::string& GetCurrentUserForTest(); - private: - friend class ::MultiUserNotificationBlockerChromeOSTest; - friend class ash::test::MultiUserWindowManagerChromeOSTest; + protected: + friend class UserSwichAnimatorChromeOS; class WindowEntry { public: @@ -156,35 +155,12 @@ class MultiUserWindowManagerChromeOS }; typedef std::map<aura::Window*, WindowEntry*> WindowToEntryMap; - typedef std::map<std::string, AppObserver*> UserIDToAppWindowObserver; - typedef std::map<aura::Window*, bool> TransientWindowToVisibility; - - // The animation step for the user change animation. First the old user gets - // hidden and then the new one gets presented. - enum AnimationStep { - HIDE_OLD_USER, - SHOW_NEW_USER - }; // Show a window for a user without switching the user. // Returns true when the window moved to a new desktop. bool ShowWindowForUserIntern(aura::Window* window, const std::string& user_id); - // Start the user change animation required for |animation_step|. - // Note that a call with SHOW_NEW_USER will finalize the animation and kill - // the timer (if there is one). - void TransitionUser(AnimationStep animtion_step); - - // Start the user wallpaper animations. - void TransitionWallpaper(AnimationStep animtion_step); - - // Start the user shelf animations. - void TransitionUserShelf(AnimationStep animtion_step); - - // Add a browser window to the system so that the owner can be remembered. - void AddBrowserWindow(Browser* browser); - // Show / hide the given window. Note: By not doing this within the functions, // this allows to either switching to different ways to show/hide and / or to // distinguish state changes performed by this class vs. state changes @@ -196,6 +172,21 @@ class MultiUserWindowManagerChromeOS bool visible, int animation_time_in_ms); + const WindowToEntryMap& window_to_entry() { return window_to_entry_; } + MultiUserNotificationBlockerChromeOS* notification_blocker() { + return notification_blocker_.get(); + } + + private: + friend class ::MultiUserNotificationBlockerChromeOSTest; + friend class ash::test::MultiUserWindowManagerChromeOSTest; + + typedef std::map<std::string, AppObserver*> UserIDToAppWindowObserver; + typedef std::map<aura::Window*, bool> TransientWindowToVisibility; + + // Add a browser window to the system so that the owner can be remembered. + void AddBrowserWindow(Browser* browser); + // Show the window and its transient children. However - if a transient child // was turned invisible by some other operation, it will stay invisible. // Use the given |animation_time_in_ms| for transitioning. @@ -253,13 +244,12 @@ class MultiUserWindowManagerChromeOS // used is quite expensive. static MultiProfileMode multi_user_mode_; - // A timer which watches to executes the second part of a "user changed" - // animation. Note that this timer exists only during such an animation. - scoped_ptr<base::Timer> user_changed_animation_timer_; - // If true, all animations will be suppressed. bool animations_disabled_; + // The animation between users. + scoped_ptr<UserSwichAnimatorChromeOS> animation_; + DISALLOW_COPY_AND_ASSIGN(MultiUserWindowManagerChromeOS); }; diff --git a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc index 1dae3a9..a8bfa50 100644 --- a/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc +++ b/chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos_unittest.cc @@ -8,6 +8,7 @@ #include "ash/test/test_session_state_delegate.h" #include "ash/test/test_shell_delegate.h" #include "ash/wm/window_state.h" +#include "ash/wm/wm_event.h" #include "base/command_line.h" #include "base/compiler_specific.h" #include "base/logging.h" @@ -15,6 +16,7 @@ #include "base/time/time.h" #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager.h" #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h" +#include "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h" #include "chrome/common/chrome_switches.h" #include "chrome/test/base/testing_profile.h" #include "ui/aura/client/aura_constants.h" @@ -106,6 +108,27 @@ class MultiUserWindowManagerChromeOSTest : public AshTestBase { return user; } + // Initiate a user transition. + void StartUserTransitionAnimation(const std::string& user_id) { + multi_user_window_manager_->ActiveUserChanged(user_id); + } + + // Call next animation step. + void AdvanceUserTransitionAnimation() { + multi_user_window_manager_->animation_->AdvanceUserTransitionAnimation(); + } + + // Return the user id of the wallpaper which is currently set. + const std::string& GetWallaperUserIdForTest() { + return multi_user_window_manager_->animation_->wallpaper_user_id_for_test(); + } + + // Returns true if the given window covers the screen. + bool CoversScreen(aura::Window* window) { + return chrome::UserSwichAnimatorChromeOS::CoversScreen( + window); + } + private: // These get created for each session. std::vector<aura::Window*> window_; @@ -698,14 +721,214 @@ TEST_F(MultiUserWindowManagerChromeOSTest, FullUserSwitchAnimationTests) { // Switch the user quickly to another user and before the animation is done // switch back and see that this works. multi_user_window_manager()->ActiveUserChanged("B"); - // Check that at this time we have nothing visible (in the middle of the - // animation). - EXPECT_EQ("H[A], H[B], H[C]", GetStatus()); + // With the start of the animation B should become visible. + EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); // Check that after switching to C, C is fully visible. SwitchUserAndWaitForAnimation("C"); EXPECT_EQ("H[A], H[B], S[C]", GetStatus()); } +// Test that using the full user switch, the animations are transitioning as +// we expect them to in all animation steps. +TEST_F(MultiUserWindowManagerChromeOSTest, AnimationSteps) { + SetUpForThisManyWindows(3); + // Turn the use of delays and animation on. + multi_user_window_manager()->SetAnimationsForTest(false); + // Set some owners and make sure we got what we asked for. + multi_user_window_manager()->SetWindowOwner(window(0), "A"); + multi_user_window_manager()->SetWindowOwner(window(1), "B"); + multi_user_window_manager()->SetWindowOwner(window(2), "C"); + EXPECT_FALSE(CoversScreen(window(0))); + EXPECT_FALSE(CoversScreen(window(1))); + EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); + EXPECT_EQ("A", GetOwnersOfVisibleWindowsAsString()); + EXPECT_NE(ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN, + ash::Shell::GetInstance()->GetShelfAutoHideBehavior( + window(0)->GetRootWindow())); + EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); + + // Start the animation and see that the old window is becoming invisible, the + // new one visible, the background starts transitionining and the shelf hides. + StartUserTransitionAnimation("B"); + EXPECT_EQ("->B", GetWallaperUserIdForTest()); + EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); + EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); + EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); + EXPECT_EQ(ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN, + ash::Shell::GetInstance()->GetShelfAutoHideBehavior( + window(0)->GetRootWindow())); + + // Staring the next step should show the shelf again, but there are many + // subsystems missing (preferences system, ChromeLauncherController, ...) + // which should set the shelf to its users state. Since that isn't there we + // can only make sure that it stays where it is. + AdvanceUserTransitionAnimation(); + EXPECT_EQ("->B", GetWallaperUserIdForTest()); + EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); + EXPECT_EQ(ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN, + ash::Shell::GetInstance()->GetShelfAutoHideBehavior( + window(0)->GetRootWindow())); + + // After the finalize the animation of the wallpaper should be finished. + AdvanceUserTransitionAnimation(); + EXPECT_EQ("B", GetWallaperUserIdForTest()); +} + +// Test that the screen coverage is properly determined. +TEST_F(MultiUserWindowManagerChromeOSTest, AnimationStepsScreenCoverage) { + SetUpForThisManyWindows(3); + // Maximizing, fully covering the screen by bounds or fullscreen mode should + // make CoversScreen return true. + wm::GetWindowState(window(0))->Maximize(); + window(1)->SetBounds(gfx::Rect(0, 0, 3000, 3000)); + + EXPECT_TRUE(CoversScreen(window(0))); + EXPECT_TRUE(CoversScreen(window(1))); + EXPECT_FALSE(CoversScreen(window(2))); + + ash::wm::WMEvent event(ash::wm::WM_EVENT_FULLSCREEN); + wm::GetWindowState(window(2))->OnWMEvent(&event); + EXPECT_TRUE(CoversScreen(window(2))); +} + +// Test that switching from a desktop which has a maximized window to a desktop +// which has no maximized window will produce the proper animation. +TEST_F(MultiUserWindowManagerChromeOSTest, AnimationStepsMaximizeToNormal) { + SetUpForThisManyWindows(3); + // Turn the use of delays and animation on. + multi_user_window_manager()->SetAnimationsForTest(false); + // Set some owners and make sure we got what we asked for. + multi_user_window_manager()->SetWindowOwner(window(0), "A"); + wm::GetWindowState(window(0))->Maximize(); + multi_user_window_manager()->SetWindowOwner(window(1), "B"); + multi_user_window_manager()->SetWindowOwner(window(2), "C"); + EXPECT_TRUE(CoversScreen(window(0))); + EXPECT_FALSE(CoversScreen(window(1))); + EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); + EXPECT_EQ("A", GetOwnersOfVisibleWindowsAsString()); + EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); + + // Start the animation and see that the new background is immediately set. + StartUserTransitionAnimation("B"); + EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); + EXPECT_EQ("B", GetWallaperUserIdForTest()); + EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); + EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); + + // The next step will not change anything. + AdvanceUserTransitionAnimation(); + EXPECT_EQ("B", GetWallaperUserIdForTest()); + EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); + EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); + + // The final step will also not have any visible impact. + AdvanceUserTransitionAnimation(); + EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); + EXPECT_EQ("B", GetWallaperUserIdForTest()); + EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); + EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); +} + +// Test that switching from a desktop which has a normal window to a desktop +// which has a maximized window will produce the proper animation. +TEST_F(MultiUserWindowManagerChromeOSTest, AnimationStepsNormalToMaximized) { + SetUpForThisManyWindows(3); + // Turn the use of delays and animation on. + multi_user_window_manager()->SetAnimationsForTest(false); + // Set some owners and make sure we got what we asked for. + multi_user_window_manager()->SetWindowOwner(window(0), "A"); + multi_user_window_manager()->SetWindowOwner(window(1), "B"); + wm::GetWindowState(window(1))->Maximize(); + multi_user_window_manager()->SetWindowOwner(window(2), "C"); + EXPECT_FALSE(CoversScreen(window(0))); + EXPECT_TRUE(CoversScreen(window(1))); + EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); + EXPECT_EQ("A", GetOwnersOfVisibleWindowsAsString()); + EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); + + // Start the animation and see that the old window is becoming invisible, the + // new one visible and the background remains as is. + StartUserTransitionAnimation("B"); + EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); + EXPECT_EQ("", GetWallaperUserIdForTest()); + EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); + EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); + + // The next step will not change anything. + AdvanceUserTransitionAnimation(); + EXPECT_EQ("", GetWallaperUserIdForTest()); + EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); + EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); + + // The final step however will switch the background. + AdvanceUserTransitionAnimation(); + EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); + EXPECT_EQ("B", GetWallaperUserIdForTest()); + EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); + EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); +} + +// Test that switching from a desktop which has a maximized window to a desktop +// which has a maximized window will produce the proper animation. +TEST_F(MultiUserWindowManagerChromeOSTest, AnimationStepsMaximizedToMaximized) { + SetUpForThisManyWindows(3); + // Turn the use of delays and animation on. + multi_user_window_manager()->SetAnimationsForTest(false); + // Set some owners and make sure we got what we asked for. + multi_user_window_manager()->SetWindowOwner(window(0), "A"); + wm::GetWindowState(window(0))->Maximize(); + multi_user_window_manager()->SetWindowOwner(window(1), "B"); + wm::GetWindowState(window(1))->Maximize(); + multi_user_window_manager()->SetWindowOwner(window(2), "C"); + EXPECT_TRUE(CoversScreen(window(0))); + EXPECT_TRUE(CoversScreen(window(1))); + EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); + EXPECT_EQ("A", GetOwnersOfVisibleWindowsAsString()); + EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); + + // Start the animation and see that the old window is staying visible, the + // new one slowly visible and the background changes immediately. + StartUserTransitionAnimation("B"); + EXPECT_EQ("S[A], S[B], H[C]", GetStatus()); + EXPECT_EQ("B", GetWallaperUserIdForTest()); + EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); + EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); + + // The next step will not change anything. + AdvanceUserTransitionAnimation(); + EXPECT_EQ("B", GetWallaperUserIdForTest()); + EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); + EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); + + // The final step however will hide the old window. + AdvanceUserTransitionAnimation(); + EXPECT_EQ("H[A], S[B], H[C]", GetStatus()); + EXPECT_EQ("B", GetWallaperUserIdForTest()); + EXPECT_EQ(0.0f, window(0)->layer()->GetTargetOpacity()); + EXPECT_EQ(1.0f, window(1)->layer()->GetTargetOpacity()); + + // Switching back will preserve the z-order by instantly showing the new + // window, hiding the layer above it and switching instantly the wallpaper. + StartUserTransitionAnimation("A"); + EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); + EXPECT_EQ("A", GetWallaperUserIdForTest()); + EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); + EXPECT_EQ(0.0f, window(1)->layer()->GetTargetOpacity()); + + // The next step will not change anything. + AdvanceUserTransitionAnimation(); + EXPECT_EQ("A", GetWallaperUserIdForTest()); + EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); + EXPECT_EQ(0.0f, window(1)->layer()->GetTargetOpacity()); + + // The final step is also not changing anything to the status. + AdvanceUserTransitionAnimation(); + EXPECT_EQ("S[A], H[B], H[C]", GetStatus()); + EXPECT_EQ("A", GetWallaperUserIdForTest()); + EXPECT_EQ(1.0f, window(0)->layer()->GetTargetOpacity()); + EXPECT_EQ(0.0f, window(1)->layer()->GetTargetOpacity()); +} + // Test that showing a window for another user also switches the desktop. TEST_F(MultiUserWindowManagerChromeOSTest, ShowForUserSwitchesDesktop) { SetUpForThisManyWindows(3); diff --git a/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.cc b/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.cc new file mode 100644 index 0000000..398c9b0 --- /dev/null +++ b/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.cc @@ -0,0 +1,324 @@ +// Copyright 2014 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 "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h" + +#include "ash/desktop_background/user_wallpaper_delegate.h" +#include "ash/root_window_controller.h" +#include "ash/shelf/shelf_layout_manager.h" +#include "ash/shell.h" +#include "ash/wm/mru_window_tracker.h" +#include "ash/wm/window_positioner.h" +#include "ash/wm/window_state.h" +#include "chrome/browser/chromeos/login/wallpaper_manager.h" +#include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h" +#include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h" +#include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h" +#include "ui/wm/public/activation_client.h" + +namespace chrome { + +namespace { + +// The animation time in milliseconds for the fade in and / or out when +// switching users. +const int kUserFadeTimeMS = 110; + +// The minimal possible animation time for animations which should happen +// "instantly". +const int kMinimalAnimationTimeMS = 1; + +// logic while the user gets switched. +class UserChangeActionDisabler { + public: + UserChangeActionDisabler() { + ash::WindowPositioner::DisableAutoPositioning(true); + ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(true); + } + + ~UserChangeActionDisabler() { + ash::WindowPositioner::DisableAutoPositioning(false); + ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations( + false); + } + private: + + DISALLOW_COPY_AND_ASSIGN(UserChangeActionDisabler); +}; + +} // namespace + +UserSwichAnimatorChromeOS::UserSwichAnimatorChromeOS( + MultiUserWindowManagerChromeOS* owner, + const std::string& new_user_id, + bool animation_disabled) + : owner_(owner), + new_user_id_(new_user_id), + animation_disabled_(animation_disabled), + animation_step_(ANIMATION_STEP_HIDE_OLD_USER), + screen_cover_(GetScreenCover()) { + AdvanceUserTransitionAnimation(); + + if (animation_disabled_) { + FinalizeAnimation(); + } else { + user_changed_animation_timer_.reset(new base::Timer( + FROM_HERE, + base::TimeDelta::FromMilliseconds(kUserFadeTimeMS), + base::Bind( + &UserSwichAnimatorChromeOS::AdvanceUserTransitionAnimation, + base::Unretained(this)), + true)); + user_changed_animation_timer_->Reset(); + } +} + +UserSwichAnimatorChromeOS::~UserSwichAnimatorChromeOS() { + FinalizeAnimation(); +} + +// static +bool UserSwichAnimatorChromeOS::CoversScreen(aura::Window* window) { + // Full screen covers the screen naturally. Since a normal window can have the + // same size as the work area, we only compare the bounds against the work + // area. + if (ash::wm::GetWindowState(window)->IsFullscreen()) + return true; + gfx::Rect bounds = window->GetBoundsInRootWindow(); + gfx::Rect work_area = gfx::Screen::GetScreenFor(window)-> + GetDisplayNearestWindow(window).work_area(); + bounds.Intersect(work_area); + return work_area == bounds; +} + +void UserSwichAnimatorChromeOS::AdvanceUserTransitionAnimation() { + DCHECK_NE(animation_step_, ANIMATION_STEP_ENDED); + + TransitionWallpaper(animation_step_); + TransitionUserShelf(animation_step_); + TransitionWindows(animation_step_); + + // Advance to the next step. + switch (animation_step_) { + case ANIMATION_STEP_HIDE_OLD_USER: + animation_step_ = ANIMATION_STEP_SHOW_NEW_USER; + break; + case ANIMATION_STEP_SHOW_NEW_USER: + animation_step_ = ANIMATION_STEP_FINALIZE; + break; + case ANIMATION_STEP_FINALIZE: + user_changed_animation_timer_.reset(); + animation_step_ = ANIMATION_STEP_ENDED; + break; + case ANIMATION_STEP_ENDED: + NOTREACHED(); + break; + } +} + +void UserSwichAnimatorChromeOS::FinalizeAnimation() { + user_changed_animation_timer_.reset(); + while (ANIMATION_STEP_ENDED != animation_step_) + AdvanceUserTransitionAnimation(); +} + +void UserSwichAnimatorChromeOS::TransitionWallpaper( + AnimationStep animation_step) { + // Handle the wallpaper switch. + ash::UserWallpaperDelegate* wallpaper_delegate = + ash::Shell::GetInstance()->user_wallpaper_delegate(); + if (animation_step == ANIMATION_STEP_HIDE_OLD_USER) { + // Set the wallpaper cross dissolve animation duration to our complete + // animation cycle for a fade in and fade out. + wallpaper_delegate->SetAnimationDurationOverride( + NO_USER_COVERS_SCREEN == screen_cover_ ? (2 * kUserFadeTimeMS) : + kMinimalAnimationTimeMS); + if (screen_cover_ != NEW_USER_COVERS_SCREEN) { + chromeos::WallpaperManager::Get()->SetUserWallpaperNow(new_user_id_); + wallpaper_user_id_ = + (NO_USER_COVERS_SCREEN == screen_cover_ ? "->" : "") + + new_user_id_; + } + } else if (animation_step == ANIMATION_STEP_FINALIZE) { + // Revert the wallpaper cross dissolve animation duration back to the + // default. + if (screen_cover_ == NEW_USER_COVERS_SCREEN) + chromeos::WallpaperManager::Get()->SetUserWallpaperNow(new_user_id_); + + // Coming here the wallpaper user id is the final result. No matter how we + // got here. + wallpaper_user_id_ = new_user_id_; + wallpaper_delegate->SetAnimationDurationOverride(0); + } +} + +void UserSwichAnimatorChromeOS::TransitionUserShelf( + AnimationStep animation_step) { + // The shelf animation duration override. + int duration_override = kUserFadeTimeMS; + // Handle the shelf order of items. This is done once the old user is hidden. + if (animation_step == ANIMATION_STEP_SHOW_NEW_USER) { + // Some unit tests have no ChromeLauncherController. + if (ChromeLauncherController::instance()) + ChromeLauncherController::instance()->ActiveUserChanged(new_user_id_); + // We kicked off the shelf animation in the command above. As such we can + // disable the override now again. + duration_override = 0; + } + + if (animation_disabled_ || animation_step == ANIMATION_STEP_FINALIZE) + return; + + ash::Shell::RootWindowControllerList controller = + ash::Shell::GetInstance()->GetAllRootWindowControllers(); + for (ash::Shell::RootWindowControllerList::iterator iter = controller.begin(); + iter != controller.end(); ++iter) { + (*iter)->GetShelfLayoutManager()->SetAnimationDurationOverride( + duration_override); + } + + // For each root window hide the shelf. + if (animation_step == ANIMATION_STEP_HIDE_OLD_USER) { + aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows(); + for (aura::Window::Windows::const_iterator iter = root_windows.begin(); + iter != root_windows.end(); ++iter) { + // This shelf change is only part of the animation and will be updated by + // ChromeLauncherController::ActiveUserChanged() to the new users value. + // Note that the user perference will not be changed. + ash::Shell::GetInstance()->SetShelfAutoHideBehavior( + ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN, *iter); + } + } +} + +void UserSwichAnimatorChromeOS::TransitionWindows( + AnimationStep animation_step) { + // Disable the window position manager and the MRU window tracker temporarily. + UserChangeActionDisabler disabler; + + if (animation_step == ANIMATION_STEP_HIDE_OLD_USER || + (animation_step == ANIMATION_STEP_FINALIZE && + screen_cover_ == BOTH_USERS_COVER_SCREEN)) { + // We need to show/hide the windows in the same order as they were created + // in their parent window(s) to keep the layer / window hierarchy in sync. + // To achieve that we first collect all parent windows and then enumerate + // all windows in those parent windows and show or hide them accordingly. + + // Create a list of all parent windows we have to check. + std::set<aura::Window*> parent_list; + for (MultiUserWindowManagerChromeOS::WindowToEntryMap::const_iterator it = + owner_->window_to_entry().begin(); + it != owner_->window_to_entry().end(); ++it) { + aura::Window* parent = it->first->parent(); + if (parent_list.find(parent) == parent_list.end()) + parent_list.insert(parent); + } + + for (std::set<aura::Window*>::iterator it_parents = parent_list.begin(); + it_parents != parent_list.end(); ++it_parents) { + const aura::Window::Windows window_list = (*it_parents)->children(); + // In case of |BOTH_USERS_COVER_SCREEN| the desktop might shine through + // if all windows fade (in or out). To avoid this we only fade the topmost + // covering window (in / out) and make / keep all other covering windows + // visible while animating. |foreground_window_found| will get set when + // the top fading window was found. + bool foreground_window_found = false; + // Covering windows which follow the fade direction will also fade - all + // others will get immediately shown / kept shown until the animation is + // finished. + bool foreground_becomes_visible = false; + for (aura::Window::Windows::const_reverse_iterator it_window = + window_list.rbegin(); + it_window != window_list.rend(); ++it_window) { + aura::Window* window = *it_window; + MultiUserWindowManagerChromeOS::WindowToEntryMap::const_iterator + it_map = owner_->window_to_entry().find(window); + if (it_map != owner_->window_to_entry().end()) { + bool should_be_visible = + it_map->second->show_for_user() == new_user_id_ && + it_map->second->show(); + bool is_visible = window->IsVisible(); + ash::wm::WindowState* window_state = ash::wm::GetWindowState(window); + if (it_map->second->owner() == new_user_id_ && + it_map->second->show_for_user() != new_user_id_ && + window_state->IsMinimized()) { + // Pull back minimized visiting windows to the owners desktop. + owner_->ShowWindowForUserIntern(window, new_user_id_); + window_state->Unminimize(); + } else if (should_be_visible != is_visible) { + bool animate = true; + int duration = animation_step == ANIMATION_STEP_FINALIZE ? + kMinimalAnimationTimeMS : (2 * kUserFadeTimeMS); + if (animation_step != ANIMATION_STEP_FINALIZE && + screen_cover_ == BOTH_USERS_COVER_SCREEN && + CoversScreen(window)) { + if (!foreground_window_found) { + foreground_window_found = true; + foreground_becomes_visible = should_be_visible; + } else if (should_be_visible != foreground_becomes_visible) { + // Covering windows behind the foreground window which are + // inverting their visibility should immediately become visible + // or stay visible until the animation is finished. + duration = kMinimalAnimationTimeMS; + if (!should_be_visible) + animate = false; + } + } + if (animate) + owner_->SetWindowVisibility(window, should_be_visible, duration); + } + } + } + } + } + + // Activation and real switch are happening after the other user gets shown. + if (animation_step == ANIMATION_STEP_SHOW_NEW_USER) { + // Finally we need to restore the previously active window. + ash::MruWindowTracker::WindowList mru_list = + ash::Shell::GetInstance()->mru_window_tracker()->BuildMruWindowList(); + if (!mru_list.empty()) { + aura::Window* window = mru_list[0]; + ash::wm::WindowState* window_state = ash::wm::GetWindowState(window); + if (owner_->IsWindowOnDesktopOfUser(window, new_user_id_) && + !window_state->IsMinimized()) { + aura::client::ActivationClient* client = + aura::client::GetActivationClient(window->GetRootWindow()); + // Several unit tests come here without an activation client. + if (client) + client->ActivateWindow(window); + } + } + + // This is called directly here to make sure notification_blocker will see + // the new window status. + owner_->notification_blocker()->ActiveUserChanged(new_user_id_); + } +} + +UserSwichAnimatorChromeOS::TransitioningScreenCover +UserSwichAnimatorChromeOS::GetScreenCover() { + TransitioningScreenCover cover = NO_USER_COVERS_SCREEN; + for (MultiUserWindowManagerChromeOS::WindowToEntryMap::const_iterator it_map = + owner_->window_to_entry().begin(); + it_map != owner_->window_to_entry().end(); + ++it_map) { + aura::Window* window = it_map->first; + if (window->IsVisible() && CoversScreen(window)) { + if (cover == NEW_USER_COVERS_SCREEN) + return BOTH_USERS_COVER_SCREEN; + else + cover = OLD_USER_COVERS_SCREEN; + } else if (owner_->IsWindowOnDesktopOfUser(window, new_user_id_) && + CoversScreen(window)) { + if (cover == OLD_USER_COVERS_SCREEN) + return BOTH_USERS_COVER_SCREEN; + else + cover = NEW_USER_COVERS_SCREEN; + } + } + return cover; +} + +} // namespace chrome diff --git a/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h b/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h new file mode 100644 index 0000000..25b945c --- /dev/null +++ b/chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h @@ -0,0 +1,113 @@ +// Copyright 2014 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. + +#ifndef CHROME_BROWSER_UI_ASH_MULTI_USER_USER_SWITCH_ANIMATOR_CHROMEOS_H_ +#define CHROME_BROWSER_UI_ASH_MULTI_USER_USER_SWITCH_ANIMATOR_CHROMEOS_H_ + +#include <string> + +#include "base/memory/scoped_ptr.h" +#include "base/timer/timer.h" + +namespace aura { +class Window; +} // namespace aura + +namespace chrome { + +class MultiUserWindowManagerChromeOS; + +// A class which performs transitions animations between users. Upon creation, +// the animation gets started and upon destruction the animation gets finished +// if not done yet. +// Specifying |animation_disabled| upon creation will perform the transition +// without visible animations. +class UserSwichAnimatorChromeOS { + public: + // The animation step for the user change animation. + enum AnimationStep { + ANIMATION_STEP_HIDE_OLD_USER, // Hiding the old user (and shelf). + ANIMATION_STEP_SHOW_NEW_USER, // Show the shelf of the new user. + ANIMATION_STEP_FINALIZE, // All animations are done - final cleanup. + ANIMATION_STEP_ENDED // The animation has ended. + }; + + UserSwichAnimatorChromeOS(MultiUserWindowManagerChromeOS* owner, + const std::string& new_user_id, + bool animation_disabled); + ~UserSwichAnimatorChromeOS(); + + // Check if a window is covering the entire work area of the screen it is on. + static bool CoversScreen(aura::Window* window); + + bool IsAnimationFinished() { + return animation_step_ == ANIMATION_STEP_ENDED; + } + + // Returns the user id for which the wallpaper is currently shown. + // If a wallpaper is transitioning to B it will be returned as "->B". + const std::string& wallpaper_user_id_for_test() { return wallpaper_user_id_; } + + // Advances the user switch animation to the next step. It reads the current + // step from |animation_step_| and increments it thereafter. When + // |ANIMATION_STEP_FINALIZE| gets executed, the animation is finished and the + // timer (if one exists) will get destroyed. + void AdvanceUserTransitionAnimation(); + + private: + // The window configuration of screen covering windows before an animation. + enum TransitioningScreenCover { + NO_USER_COVERS_SCREEN, // No window covers the entire screen. + OLD_USER_COVERS_SCREEN, // The current user has at least one window + // covering the entire screen. + NEW_USER_COVERS_SCREEN, // The user which becomes active has at least one + // window covering the entire screen. + BOTH_USERS_COVER_SCREEN // Both users have at least one window each + // covering the entire screen. + }; + + // Finalizes the animation and ends the timer (if there is one). + void FinalizeAnimation(); + + // Execute the user wallpaper animations for |animation_step|. + void TransitionWallpaper(AnimationStep animtion_step); + + // Execute the user shelf animations for |animation_step|. + void TransitionUserShelf(AnimationStep animtion_step); + + // Execute the window animations for |animation_step|. + void TransitionWindows(AnimationStep animation_step); + + // Check if a window is maximized / fullscreen / covering the entire screen. + // TODO(skuhne): We might want to do this on a per screen basis. + TransitioningScreenCover GetScreenCover(); + + // The owning window manager. + MultiUserWindowManagerChromeOS* owner_; + + // The new user to set. + std::string new_user_id_; + + // If true, all animations will be suppressed. + bool animation_disabled_; + + // The next animation step for AdvanceUserTransitionAnimation(). + AnimationStep animation_step_; + + // The screen cover status before the animation has started. + TransitioningScreenCover screen_cover_; + + // A timer which watches to executes the second part of a "user changed" + // animation. Note that this timer exists only during such an animation. + scoped_ptr<base::Timer> user_changed_animation_timer_; + + // For unit tests: Check which wallpaper was set. + std::string wallpaper_user_id_; + + DISALLOW_COPY_AND_ASSIGN(UserSwichAnimatorChromeOS); +}; + +} // namespace chrome + +#endif // CHROME_BROWSER_UI_ASH_MULTI_USER_USER_SWITCH_ANIMATOR_CHROMEOS_H_ |