Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / ui / ash / multi_user / user_switch_animator_chromeos.cc
blob32d353ec812a0831da6955b9c93a648376dbb817
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h"
7 #include "ash/desktop_background/user_wallpaper_delegate.h"
8 #include "ash/root_window_controller.h"
9 #include "ash/shelf/shelf_layout_manager.h"
10 #include "ash/shelf/shelf_widget.h"
11 #include "ash/shell.h"
12 #include "ash/wm/mru_window_tracker.h"
13 #include "ash/wm/window_positioner.h"
14 #include "ash/wm/window_state.h"
15 #include "chrome/browser/chromeos/login/users/wallpaper/wallpaper_manager.h"
16 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
17 #include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h"
18 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h"
19 #include "ui/wm/public/activation_client.h"
21 namespace chrome {
23 namespace {
25 // The minimal possible animation time for animations which should happen
26 // "instantly".
27 const int kMinimalAnimationTimeMS = 1;
29 // logic while the user gets switched.
30 class UserChangeActionDisabler {
31 public:
32 UserChangeActionDisabler() {
33 ash::WindowPositioner::DisableAutoPositioning(true);
34 ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(true);
37 ~UserChangeActionDisabler() {
38 ash::WindowPositioner::DisableAutoPositioning(false);
39 ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(
40 false);
42 private:
44 DISALLOW_COPY_AND_ASSIGN(UserChangeActionDisabler);
47 } // namespace
49 UserSwitchAnimatorChromeOS::UserSwitchAnimatorChromeOS(
50 MultiUserWindowManagerChromeOS* owner,
51 const std::string& new_user_id,
52 int animation_speed_ms)
53 : owner_(owner),
54 new_user_id_(new_user_id),
55 animation_speed_ms_(animation_speed_ms),
56 animation_step_(ANIMATION_STEP_HIDE_OLD_USER),
57 screen_cover_(GetScreenCover(NULL)) {
58 AdvanceUserTransitionAnimation();
60 if (!animation_speed_ms_) {
61 FinalizeAnimation();
62 } else {
63 user_changed_animation_timer_.reset(new base::Timer(
64 FROM_HERE,
65 base::TimeDelta::FromMilliseconds(animation_speed_ms_),
66 base::Bind(
67 &UserSwitchAnimatorChromeOS::AdvanceUserTransitionAnimation,
68 base::Unretained(this)),
69 true));
70 user_changed_animation_timer_->Reset();
74 UserSwitchAnimatorChromeOS::~UserSwitchAnimatorChromeOS() {
75 FinalizeAnimation();
78 // static
79 bool UserSwitchAnimatorChromeOS::CoversScreen(aura::Window* window) {
80 // Full screen covers the screen naturally. Since a normal window can have the
81 // same size as the work area, we only compare the bounds against the work
82 // area.
83 if (ash::wm::GetWindowState(window)->IsFullscreen())
84 return true;
85 gfx::Rect bounds = window->GetBoundsInRootWindow();
86 gfx::Rect work_area = gfx::Screen::GetScreenFor(window)->
87 GetDisplayNearestWindow(window).work_area();
88 bounds.Intersect(work_area);
89 return work_area == bounds;
92 void UserSwitchAnimatorChromeOS::AdvanceUserTransitionAnimation() {
93 DCHECK_NE(animation_step_, ANIMATION_STEP_ENDED);
95 TransitionWallpaper(animation_step_);
96 TransitionUserShelf(animation_step_);
97 TransitionWindows(animation_step_);
99 // Advance to the next step.
100 switch (animation_step_) {
101 case ANIMATION_STEP_HIDE_OLD_USER:
102 animation_step_ = ANIMATION_STEP_SHOW_NEW_USER;
103 break;
104 case ANIMATION_STEP_SHOW_NEW_USER:
105 animation_step_ = ANIMATION_STEP_FINALIZE;
106 break;
107 case ANIMATION_STEP_FINALIZE:
108 user_changed_animation_timer_.reset();
109 animation_step_ = ANIMATION_STEP_ENDED;
110 break;
111 case ANIMATION_STEP_ENDED:
112 NOTREACHED();
113 break;
117 void UserSwitchAnimatorChromeOS::CancelAnimation() {
118 animation_step_ = ANIMATION_STEP_ENDED;
121 void UserSwitchAnimatorChromeOS::FinalizeAnimation() {
122 user_changed_animation_timer_.reset();
123 while (ANIMATION_STEP_ENDED != animation_step_)
124 AdvanceUserTransitionAnimation();
127 void UserSwitchAnimatorChromeOS::TransitionWallpaper(
128 AnimationStep animation_step) {
129 // Handle the wallpaper switch.
130 ash::UserWallpaperDelegate* wallpaper_delegate =
131 ash::Shell::GetInstance()->user_wallpaper_delegate();
132 if (animation_step == ANIMATION_STEP_HIDE_OLD_USER) {
133 // Set the wallpaper cross dissolve animation duration to our complete
134 // animation cycle for a fade in and fade out.
135 int duration =
136 NO_USER_COVERS_SCREEN == screen_cover_ ? (2 * animation_speed_ms_) : 0;
137 wallpaper_delegate->SetAnimationDurationOverride(
138 std::max(duration, kMinimalAnimationTimeMS));
139 if (screen_cover_ != NEW_USER_COVERS_SCREEN) {
140 chromeos::WallpaperManager::Get()->SetUserWallpaperNow(new_user_id_);
141 wallpaper_user_id_ =
142 (NO_USER_COVERS_SCREEN == screen_cover_ ? "->" : "") +
143 new_user_id_;
145 } else if (animation_step == ANIMATION_STEP_FINALIZE) {
146 // Revert the wallpaper cross dissolve animation duration back to the
147 // default.
148 if (screen_cover_ == NEW_USER_COVERS_SCREEN)
149 chromeos::WallpaperManager::Get()->SetUserWallpaperNow(new_user_id_);
151 // Coming here the wallpaper user id is the final result. No matter how we
152 // got here.
153 wallpaper_user_id_ = new_user_id_;
154 wallpaper_delegate->SetAnimationDurationOverride(0);
158 void UserSwitchAnimatorChromeOS::TransitionUserShelf(
159 AnimationStep animation_step) {
160 ChromeLauncherController* chrome_launcher_controller =
161 ChromeLauncherController::instance();
162 // The shelf animation duration override.
163 int duration_override = animation_speed_ms_;
164 // Handle the shelf order of items. This is done once the old user is hidden.
165 if (animation_step == ANIMATION_STEP_SHOW_NEW_USER) {
166 // Some unit tests have no ChromeLauncherController.
167 if (chrome_launcher_controller)
168 chrome_launcher_controller->ActiveUserChanged(new_user_id_);
169 // Hide the black rectangle on top of each shelf again.
170 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
171 for (aura::Window::Windows::const_iterator iter = root_windows.begin();
172 iter != root_windows.end(); ++iter) {
173 ash::ShelfWidget* shelf =
174 ash::RootWindowController::ForWindow(*iter)->shelf();
175 shelf->HideShelfBehindBlackBar(false, duration_override);
177 // We kicked off the shelf animation above and the override can be
178 // removed.
179 duration_override = 0;
182 if (!animation_speed_ms_ || animation_step == ANIMATION_STEP_FINALIZE)
183 return;
185 // Note: The animation duration override will be set before the old user gets
186 // hidden and reset after the animations for the new user got kicked off.
187 ash::Shell::RootWindowControllerList controller =
188 ash::Shell::GetInstance()->GetAllRootWindowControllers();
189 for (ash::Shell::RootWindowControllerList::iterator iter = controller.begin();
190 iter != controller.end(); ++iter) {
191 (*iter)->GetShelfLayoutManager()->SetAnimationDurationOverride(
192 duration_override);
195 if (animation_step != ANIMATION_STEP_HIDE_OLD_USER)
196 return;
198 // For each root window hide the shelf.
199 aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
201 for (aura::Window::Windows::const_iterator iter = root_windows.begin();
202 iter != root_windows.end(); ++iter) {
203 // Hiding the shelf will cause a resize on a maximized window.
204 // If the shelf is then shown for the following user in the same location,
205 // the window gets resized again. Since each resize can cause a considerable
206 // CPU usage and therefore effect jank, we should avoid hiding the shelf if
207 // the start and end location are the same and cover the shelf instead with
208 // a black rectangle on top.
209 if (GetScreenCover(*iter) != NO_USER_COVERS_SCREEN &&
210 (!chrome_launcher_controller ||
211 !chrome_launcher_controller->ShelfBoundsChangesProbablyWithUser(
212 *iter, new_user_id_))) {
213 ash::ShelfWidget* shelf =
214 ash::RootWindowController::ForWindow(*iter)->shelf();
215 shelf->HideShelfBehindBlackBar(true, duration_override);
216 } else {
217 // This shelf change is only part of the animation and will be updated by
218 // ChromeLauncherController::ActiveUserChanged() to the new users value.
219 // Note that the user preference will not be changed.
220 ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
221 ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN, *iter);
226 void UserSwitchAnimatorChromeOS::TransitionWindows(
227 AnimationStep animation_step) {
228 // Disable the window position manager and the MRU window tracker temporarily.
229 UserChangeActionDisabler disabler;
231 if (animation_step == ANIMATION_STEP_HIDE_OLD_USER ||
232 (animation_step == ANIMATION_STEP_FINALIZE &&
233 screen_cover_ == BOTH_USERS_COVER_SCREEN)) {
234 // We need to show/hide the windows in the same order as they were created
235 // in their parent window(s) to keep the layer / window hierarchy in sync.
236 // To achieve that we first collect all parent windows and then enumerate
237 // all windows in those parent windows and show or hide them accordingly.
239 // Create a list of all parent windows we have to check.
240 std::set<aura::Window*> parent_list;
241 for (MultiUserWindowManagerChromeOS::WindowToEntryMap::const_iterator it =
242 owner_->window_to_entry().begin();
243 it != owner_->window_to_entry().end(); ++it) {
244 aura::Window* parent = it->first->parent();
245 if (parent_list.find(parent) == parent_list.end())
246 parent_list.insert(parent);
249 for (std::set<aura::Window*>::iterator it_parents = parent_list.begin();
250 it_parents != parent_list.end(); ++it_parents) {
251 const aura::Window::Windows window_list = (*it_parents)->children();
252 // In case of |BOTH_USERS_COVER_SCREEN| the desktop might shine through
253 // if all windows fade (in or out). To avoid this we only fade the topmost
254 // covering window (in / out) and make / keep all other covering windows
255 // visible while animating. |foreground_window_found| will get set when
256 // the top fading window was found.
257 bool foreground_window_found = false;
258 // Covering windows which follow the fade direction will also fade - all
259 // others will get immediately shown / kept shown until the animation is
260 // finished.
261 bool foreground_becomes_visible = false;
262 for (aura::Window::Windows::const_reverse_iterator it_window =
263 window_list.rbegin();
264 it_window != window_list.rend(); ++it_window) {
265 aura::Window* window = *it_window;
266 MultiUserWindowManagerChromeOS::WindowToEntryMap::const_iterator
267 it_map = owner_->window_to_entry().find(window);
268 if (it_map != owner_->window_to_entry().end()) {
269 bool should_be_visible =
270 it_map->second->show_for_user() == new_user_id_ &&
271 it_map->second->show();
272 bool is_visible = window->IsVisible();
273 ash::wm::WindowState* window_state = ash::wm::GetWindowState(window);
274 if (it_map->second->owner() == new_user_id_ &&
275 it_map->second->show_for_user() != new_user_id_ &&
276 window_state->IsMinimized()) {
277 // Pull back minimized visiting windows to the owners desktop.
278 owner_->ShowWindowForUserIntern(window, new_user_id_);
279 window_state->Unminimize();
280 } else if (should_be_visible != is_visible) {
281 bool animate = true;
282 int duration = animation_step == ANIMATION_STEP_FINALIZE ?
283 0 : (2 * animation_speed_ms_);
284 duration = std::max(kMinimalAnimationTimeMS, duration);
285 if (animation_step != ANIMATION_STEP_FINALIZE &&
286 screen_cover_ == BOTH_USERS_COVER_SCREEN &&
287 CoversScreen(window)) {
288 if (!foreground_window_found) {
289 foreground_window_found = true;
290 foreground_becomes_visible = should_be_visible;
291 } else if (should_be_visible != foreground_becomes_visible) {
292 // Covering windows behind the foreground window which are
293 // inverting their visibility should immediately become visible
294 // or stay visible until the animation is finished.
295 duration = kMinimalAnimationTimeMS;
296 if (!should_be_visible)
297 animate = false;
300 if (animate)
301 owner_->SetWindowVisibility(window, should_be_visible, duration);
308 // Activation and real switch are happening after the other user gets shown.
309 if (animation_step == ANIMATION_STEP_SHOW_NEW_USER) {
310 // Finally we need to restore the previously active window.
311 ash::MruWindowTracker::WindowList mru_list =
312 ash::Shell::GetInstance()->mru_window_tracker()->BuildMruWindowList();
313 if (!mru_list.empty()) {
314 aura::Window* window = mru_list[0];
315 ash::wm::WindowState* window_state = ash::wm::GetWindowState(window);
316 if (owner_->IsWindowOnDesktopOfUser(window, new_user_id_) &&
317 !window_state->IsMinimized()) {
318 aura::client::ActivationClient* client =
319 aura::client::GetActivationClient(window->GetRootWindow());
320 // Several unit tests come here without an activation client.
321 if (client)
322 client->ActivateWindow(window);
326 // This is called directly here to make sure notification_blocker will see
327 // the new window status.
328 owner_->notification_blocker()->ActiveUserChanged(new_user_id_);
332 UserSwitchAnimatorChromeOS::TransitioningScreenCover
333 UserSwitchAnimatorChromeOS::GetScreenCover(aura::Window* root_window) {
334 TransitioningScreenCover cover = NO_USER_COVERS_SCREEN;
335 for (MultiUserWindowManagerChromeOS::WindowToEntryMap::const_iterator it_map =
336 owner_->window_to_entry().begin();
337 it_map != owner_->window_to_entry().end();
338 ++it_map) {
339 aura::Window* window = it_map->first;
340 if (root_window && window->GetRootWindow() != root_window)
341 continue;
342 if (window->IsVisible() && CoversScreen(window)) {
343 if (cover == NEW_USER_COVERS_SCREEN)
344 return BOTH_USERS_COVER_SCREEN;
345 else
346 cover = OLD_USER_COVERS_SCREEN;
347 } else if (owner_->IsWindowOnDesktopOfUser(window, new_user_id_) &&
348 CoversScreen(window)) {
349 if (cover == OLD_USER_COVERS_SCREEN)
350 return BOTH_USERS_COVER_SCREEN;
351 else
352 cover = NEW_USER_COVERS_SCREEN;
355 return cover;
358 } // namespace chrome