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