Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / ash / multi_user / user_switch_animator_chromeos.cc
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.
4
5 #include "chrome/browser/ui/ash/multi_user/user_switch_animator_chromeos.h"
6
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"
20
21 namespace chrome {
22
23 namespace {
24
25 // The minimal possible animation time for animations which should happen
26 // "instantly".
27 const int kMinimalAnimationTimeMS = 1;
28
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);
35   }
36
37   ~UserChangeActionDisabler() {
38     ash::WindowPositioner::DisableAutoPositioning(false);
39     ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(
40         false);
41   }
42  private:
43
44   DISALLOW_COPY_AND_ASSIGN(UserChangeActionDisabler);
45 };
46
47 }  // namespace
48
49 UserSwichAnimatorChromeOS::UserSwichAnimatorChromeOS(
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();
59
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             &UserSwichAnimatorChromeOS::AdvanceUserTransitionAnimation,
68             base::Unretained(this)),
69         true));
70     user_changed_animation_timer_->Reset();
71   }
72 }
73
74 UserSwichAnimatorChromeOS::~UserSwichAnimatorChromeOS() {
75   FinalizeAnimation();
76 }
77
78 // static
79 bool UserSwichAnimatorChromeOS::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;
90 }
91
92 void UserSwichAnimatorChromeOS::AdvanceUserTransitionAnimation() {
93   DCHECK_NE(animation_step_, ANIMATION_STEP_ENDED);
94
95   TransitionWallpaper(animation_step_);
96   TransitionUserShelf(animation_step_);
97   TransitionWindows(animation_step_);
98
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;
114   }
115 }
116
117 void UserSwichAnimatorChromeOS::CancelAnimation() {
118   animation_step_ = ANIMATION_STEP_ENDED;
119 }
120
121 void UserSwichAnimatorChromeOS::FinalizeAnimation() {
122   user_changed_animation_timer_.reset();
123   while (ANIMATION_STEP_ENDED != animation_step_)
124     AdvanceUserTransitionAnimation();
125 }
126
127 void UserSwichAnimatorChromeOS::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_;
144     }
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_);
150
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);
155   }
156 }
157
158 void UserSwichAnimatorChromeOS::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);
176     }
177     // We kicked off the shelf animation above and the override can be
178     // removed.
179     duration_override = 0;
180   }
181
182   if (!animation_speed_ms_ || animation_step == ANIMATION_STEP_FINALIZE)
183     return;
184
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);
193   }
194
195   if (animation_step != ANIMATION_STEP_HIDE_OLD_USER)
196     return;
197
198   // For each root window hide the shelf.
199   aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
200
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);
222     }
223   }
224 }
225
226 void UserSwichAnimatorChromeOS::TransitionWindows(
227     AnimationStep animation_step) {
228   // Disable the window position manager and the MRU window tracker temporarily.
229   UserChangeActionDisabler disabler;
230
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.
238
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);
247     }
248
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;
298               }
299             }
300             if (animate)
301               owner_->SetWindowVisibility(window, should_be_visible, duration);
302           }
303         }
304       }
305     }
306   }
307
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);
323       }
324     }
325
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_);
329   }
330 }
331
332 UserSwichAnimatorChromeOS::TransitioningScreenCover
333 UserSwichAnimatorChromeOS::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;
353     }
354   }
355   return cover;
356 }
357
358 }  // namespace chrome