Chromium Code Reviews| 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 |