Upstream version 7.36.149.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/shell.h"
11 #include "ash/wm/mru_window_tracker.h"
12 #include "ash/wm/window_positioner.h"
13 #include "ash/wm/window_state.h"
14 #include "chrome/browser/chromeos/login/wallpaper_manager.h"
15 #include "chrome/browser/ui/ash/launcher/chrome_launcher_controller.h"
16 #include "chrome/browser/ui/ash/multi_user/multi_user_notification_blocker_chromeos.h"
17 #include "chrome/browser/ui/ash/multi_user/multi_user_window_manager_chromeos.h"
18 #include "ui/wm/public/activation_client.h"
19
20 namespace chrome {
21
22 namespace {
23
24 // The minimal possible animation time for animations which should happen
25 // "instantly".
26 const int kMinimalAnimationTimeMS = 1;
27
28 // logic while the user gets switched.
29 class UserChangeActionDisabler {
30  public:
31   UserChangeActionDisabler() {
32     ash::WindowPositioner::DisableAutoPositioning(true);
33     ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(true);
34   }
35
36   ~UserChangeActionDisabler() {
37     ash::WindowPositioner::DisableAutoPositioning(false);
38     ash::Shell::GetInstance()->mru_window_tracker()->SetIgnoreActivations(
39         false);
40   }
41  private:
42
43   DISALLOW_COPY_AND_ASSIGN(UserChangeActionDisabler);
44 };
45
46 }  // namespace
47
48 UserSwichAnimatorChromeOS::UserSwichAnimatorChromeOS(
49     MultiUserWindowManagerChromeOS* owner,
50     const std::string& new_user_id,
51     int animation_speed_ms)
52     : owner_(owner),
53       new_user_id_(new_user_id),
54       animation_speed_ms_(animation_speed_ms),
55       animation_step_(ANIMATION_STEP_HIDE_OLD_USER),
56       screen_cover_(GetScreenCover()) {
57   AdvanceUserTransitionAnimation();
58
59   if (!animation_speed_ms_) {
60     FinalizeAnimation();
61   } else {
62     user_changed_animation_timer_.reset(new base::Timer(
63         FROM_HERE,
64         base::TimeDelta::FromMilliseconds(animation_speed_ms_),
65         base::Bind(
66             &UserSwichAnimatorChromeOS::AdvanceUserTransitionAnimation,
67             base::Unretained(this)),
68         true));
69     user_changed_animation_timer_->Reset();
70   }
71 }
72
73 UserSwichAnimatorChromeOS::~UserSwichAnimatorChromeOS() {
74   FinalizeAnimation();
75 }
76
77 // static
78 bool UserSwichAnimatorChromeOS::CoversScreen(aura::Window* window) {
79   // Full screen covers the screen naturally. Since a normal window can have the
80   // same size as the work area, we only compare the bounds against the work
81   // area.
82   if (ash::wm::GetWindowState(window)->IsFullscreen())
83     return true;
84   gfx::Rect bounds = window->GetBoundsInRootWindow();
85   gfx::Rect work_area = gfx::Screen::GetScreenFor(window)->
86       GetDisplayNearestWindow(window).work_area();
87   bounds.Intersect(work_area);
88   return work_area == bounds;
89 }
90
91 void UserSwichAnimatorChromeOS::AdvanceUserTransitionAnimation() {
92   DCHECK_NE(animation_step_, ANIMATION_STEP_ENDED);
93
94   TransitionWallpaper(animation_step_);
95   TransitionUserShelf(animation_step_);
96   TransitionWindows(animation_step_);
97
98   // Advance to the next step.
99   switch (animation_step_) {
100     case ANIMATION_STEP_HIDE_OLD_USER:
101       animation_step_ = ANIMATION_STEP_SHOW_NEW_USER;
102       break;
103     case ANIMATION_STEP_SHOW_NEW_USER:
104       animation_step_ = ANIMATION_STEP_FINALIZE;
105       break;
106     case ANIMATION_STEP_FINALIZE:
107       user_changed_animation_timer_.reset();
108       animation_step_ = ANIMATION_STEP_ENDED;
109       break;
110     case ANIMATION_STEP_ENDED:
111       NOTREACHED();
112       break;
113   }
114 }
115
116 void UserSwichAnimatorChromeOS::CancelAnimation() {
117   animation_step_ = ANIMATION_STEP_ENDED;
118 }
119
120 void UserSwichAnimatorChromeOS::FinalizeAnimation() {
121   user_changed_animation_timer_.reset();
122   while (ANIMATION_STEP_ENDED != animation_step_)
123     AdvanceUserTransitionAnimation();
124 }
125
126 void UserSwichAnimatorChromeOS::TransitionWallpaper(
127     AnimationStep animation_step) {
128   // Handle the wallpaper switch.
129   ash::UserWallpaperDelegate* wallpaper_delegate =
130       ash::Shell::GetInstance()->user_wallpaper_delegate();
131   if (animation_step == ANIMATION_STEP_HIDE_OLD_USER) {
132     // Set the wallpaper cross dissolve animation duration to our complete
133     // animation cycle for a fade in and fade out.
134     int duration =
135         NO_USER_COVERS_SCREEN == screen_cover_ ? (2 * animation_speed_ms_) : 0;
136     wallpaper_delegate->SetAnimationDurationOverride(
137         std::max(duration, kMinimalAnimationTimeMS));
138     if (screen_cover_ != NEW_USER_COVERS_SCREEN) {
139       chromeos::WallpaperManager::Get()->SetUserWallpaperNow(new_user_id_);
140       wallpaper_user_id_ =
141           (NO_USER_COVERS_SCREEN == screen_cover_ ? "->" : "") +
142           new_user_id_;
143     }
144   } else if (animation_step == ANIMATION_STEP_FINALIZE) {
145     // Revert the wallpaper cross dissolve animation duration back to the
146     // default.
147     if (screen_cover_ == NEW_USER_COVERS_SCREEN)
148       chromeos::WallpaperManager::Get()->SetUserWallpaperNow(new_user_id_);
149
150     // Coming here the wallpaper user id is the final result. No matter how we
151     // got here.
152     wallpaper_user_id_ = new_user_id_;
153     wallpaper_delegate->SetAnimationDurationOverride(0);
154   }
155 }
156
157 void UserSwichAnimatorChromeOS::TransitionUserShelf(
158     AnimationStep animation_step) {
159   // The shelf animation duration override.
160   int duration_override = animation_speed_ms_;
161   // Handle the shelf order of items. This is done once the old user is hidden.
162   if (animation_step == ANIMATION_STEP_SHOW_NEW_USER) {
163     // Some unit tests have no ChromeLauncherController.
164     if (ChromeLauncherController::instance())
165       ChromeLauncherController::instance()->ActiveUserChanged(new_user_id_);
166     // We kicked off the shelf animation in the command above. As such we can
167     // disable the override now again.
168     duration_override = 0;
169   }
170
171   if (!animation_speed_ms_ || animation_step == ANIMATION_STEP_FINALIZE)
172     return;
173
174   ash::Shell::RootWindowControllerList controller =
175       ash::Shell::GetInstance()->GetAllRootWindowControllers();
176   for (ash::Shell::RootWindowControllerList::iterator iter = controller.begin();
177        iter != controller.end(); ++iter) {
178     (*iter)->GetShelfLayoutManager()->SetAnimationDurationOverride(
179         duration_override);
180   }
181
182   // For each root window hide the shelf.
183   if (animation_step == ANIMATION_STEP_HIDE_OLD_USER) {
184     aura::Window::Windows root_windows = ash::Shell::GetAllRootWindows();
185     for (aura::Window::Windows::const_iterator iter = root_windows.begin();
186          iter != root_windows.end(); ++iter) {
187       // This shelf change is only part of the animation and will be updated by
188       // ChromeLauncherController::ActiveUserChanged() to the new users value.
189       // Note that the user perference will not be changed.
190       ash::Shell::GetInstance()->SetShelfAutoHideBehavior(
191           ash::SHELF_AUTO_HIDE_ALWAYS_HIDDEN, *iter);
192     }
193   }
194 }
195
196 void UserSwichAnimatorChromeOS::TransitionWindows(
197     AnimationStep animation_step) {
198   // Disable the window position manager and the MRU window tracker temporarily.
199   UserChangeActionDisabler disabler;
200
201   if (animation_step == ANIMATION_STEP_HIDE_OLD_USER ||
202       (animation_step == ANIMATION_STEP_FINALIZE &&
203        screen_cover_ == BOTH_USERS_COVER_SCREEN)) {
204     // We need to show/hide the windows in the same order as they were created
205     // in their parent window(s) to keep the layer / window hierarchy in sync.
206     // To achieve that we first collect all parent windows and then enumerate
207     // all windows in those parent windows and show or hide them accordingly.
208
209     // Create a list of all parent windows we have to check.
210     std::set<aura::Window*> parent_list;
211     for (MultiUserWindowManagerChromeOS::WindowToEntryMap::const_iterator it =
212              owner_->window_to_entry().begin();
213          it != owner_->window_to_entry().end(); ++it) {
214       aura::Window* parent = it->first->parent();
215       if (parent_list.find(parent) == parent_list.end())
216         parent_list.insert(parent);
217     }
218
219     for (std::set<aura::Window*>::iterator it_parents = parent_list.begin();
220          it_parents != parent_list.end(); ++it_parents) {
221       const aura::Window::Windows window_list = (*it_parents)->children();
222       // In case of |BOTH_USERS_COVER_SCREEN| the desktop might shine through
223       // if all windows fade (in or out). To avoid this we only fade the topmost
224       // covering window (in / out) and make / keep all other covering windows
225       // visible while animating. |foreground_window_found| will get set when
226       // the top fading window was found.
227       bool foreground_window_found = false;
228       // Covering windows which follow the fade direction will also fade - all
229       // others will get immediately shown / kept shown until the animation is
230       // finished.
231       bool foreground_becomes_visible = false;
232       for (aura::Window::Windows::const_reverse_iterator it_window =
233                window_list.rbegin();
234            it_window != window_list.rend(); ++it_window) {
235         aura::Window* window = *it_window;
236         MultiUserWindowManagerChromeOS::WindowToEntryMap::const_iterator
237             it_map = owner_->window_to_entry().find(window);
238         if (it_map != owner_->window_to_entry().end()) {
239           bool should_be_visible =
240               it_map->second->show_for_user() == new_user_id_ &&
241               it_map->second->show();
242           bool is_visible = window->IsVisible();
243           ash::wm::WindowState* window_state = ash::wm::GetWindowState(window);
244           if (it_map->second->owner() == new_user_id_ &&
245               it_map->second->show_for_user() != new_user_id_ &&
246               window_state->IsMinimized()) {
247             // Pull back minimized visiting windows to the owners desktop.
248             owner_->ShowWindowForUserIntern(window, new_user_id_);
249             window_state->Unminimize();
250           } else if (should_be_visible != is_visible) {
251             bool animate = true;
252             int duration = animation_step == ANIMATION_STEP_FINALIZE ?
253                                0 : (2 * animation_speed_ms_);
254             duration = std::max(kMinimalAnimationTimeMS, duration);
255             if (animation_step != ANIMATION_STEP_FINALIZE &&
256                 screen_cover_ == BOTH_USERS_COVER_SCREEN &&
257                 CoversScreen(window)) {
258               if (!foreground_window_found) {
259                 foreground_window_found = true;
260                 foreground_becomes_visible = should_be_visible;
261               } else if (should_be_visible != foreground_becomes_visible) {
262                 // Covering windows behind the foreground window which are
263                 // inverting their visibility should immediately become visible
264                 // or stay visible until the animation is finished.
265                 duration = kMinimalAnimationTimeMS;
266                 if (!should_be_visible)
267                   animate = false;
268               }
269             }
270             if (animate)
271               owner_->SetWindowVisibility(window, should_be_visible, duration);
272           }
273         }
274       }
275     }
276   }
277
278   // Activation and real switch are happening after the other user gets shown.
279   if (animation_step == ANIMATION_STEP_SHOW_NEW_USER) {
280     // Finally we need to restore the previously active window.
281     ash::MruWindowTracker::WindowList mru_list =
282         ash::Shell::GetInstance()->mru_window_tracker()->BuildMruWindowList();
283     if (!mru_list.empty()) {
284       aura::Window* window = mru_list[0];
285       ash::wm::WindowState* window_state = ash::wm::GetWindowState(window);
286       if (owner_->IsWindowOnDesktopOfUser(window, new_user_id_) &&
287           !window_state->IsMinimized()) {
288         aura::client::ActivationClient* client =
289             aura::client::GetActivationClient(window->GetRootWindow());
290         // Several unit tests come here without an activation client.
291         if (client)
292           client->ActivateWindow(window);
293       }
294     }
295
296     // This is called directly here to make sure notification_blocker will see
297     // the new window status.
298     owner_->notification_blocker()->ActiveUserChanged(new_user_id_);
299   }
300 }
301
302 UserSwichAnimatorChromeOS::TransitioningScreenCover
303 UserSwichAnimatorChromeOS::GetScreenCover() {
304   TransitioningScreenCover cover = NO_USER_COVERS_SCREEN;
305   for (MultiUserWindowManagerChromeOS::WindowToEntryMap::const_iterator it_map =
306            owner_->window_to_entry().begin();
307        it_map != owner_->window_to_entry().end();
308        ++it_map) {
309     aura::Window* window = it_map->first;
310     if (window->IsVisible() && CoversScreen(window)) {
311       if (cover == NEW_USER_COVERS_SCREEN)
312         return BOTH_USERS_COVER_SCREEN;
313       else
314         cover = OLD_USER_COVERS_SCREEN;
315     } else if (owner_->IsWindowOnDesktopOfUser(window, new_user_id_) &&
316                CoversScreen(window)) {
317       if (cover == OLD_USER_COVERS_SCREEN)
318         return BOTH_USERS_COVER_SCREEN;
319       else
320         cover = NEW_USER_COVERS_SCREEN;
321     }
322   }
323   return cover;
324 }
325
326 }  // namespace chrome