user_switch_animator.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374
  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. #include "ash/multi_user/user_switch_animator.h"
  5. #include <memory>
  6. #include "ash/multi_user/multi_user_window_manager_impl.h"
  7. #include "ash/public/cpp/multi_user_window_manager_delegate.h"
  8. #include "ash/shell.h"
  9. #include "ash/wallpaper/wallpaper_controller_impl.h"
  10. #include "ash/wm/desks/desks_controller.h"
  11. #include "ash/wm/mru_window_tracker.h"
  12. #include "ash/wm/overview/overview_controller.h"
  13. #include "ash/wm/window_positioner.h"
  14. #include "base/bind.h"
  15. #include "ui/aura/client/aura_constants.h"
  16. #include "ui/compositor/layer.h"
  17. #include "ui/compositor/layer_animation_observer.h"
  18. #include "ui/compositor/layer_tree_owner.h"
  19. #include "ui/compositor/scoped_layer_animation_settings.h"
  20. #include "ui/display/display.h"
  21. #include "ui/wm/core/window_util.h"
  22. #include "ui/wm/public/activation_client.h"
  23. namespace ash {
  24. namespace {
  25. // The minimal possible animation time for animations which should happen
  26. // "instantly".
  27. constexpr base::TimeDelta kMinimalAnimationTime = base::Milliseconds(1);
  28. // logic while the user gets switched.
  29. class UserChangeActionDisabler {
  30. public:
  31. UserChangeActionDisabler() {
  32. WindowPositioner::DisableAutoPositioning(true);
  33. Shell::Get()->mru_window_tracker()->SetIgnoreActivations(true);
  34. }
  35. UserChangeActionDisabler(const UserChangeActionDisabler&) = delete;
  36. UserChangeActionDisabler& operator=(const UserChangeActionDisabler&) = delete;
  37. ~UserChangeActionDisabler() {
  38. WindowPositioner::DisableAutoPositioning(false);
  39. Shell::Get()->mru_window_tracker()->SetIgnoreActivations(false);
  40. }
  41. };
  42. // Defines an animation watcher for the 'hide' animation of the first maximized
  43. // window we encounter while looping through the old user's windows. This is
  44. // to observe the end of the animation so that we can destruct the old detached
  45. // layer of the window.
  46. class MaximizedWindowAnimationWatcher : public ui::ImplicitAnimationObserver {
  47. public:
  48. explicit MaximizedWindowAnimationWatcher(
  49. std::unique_ptr<ui::LayerTreeOwner> old_layer)
  50. : old_layer_(std::move(old_layer)) {}
  51. MaximizedWindowAnimationWatcher(const MaximizedWindowAnimationWatcher&) =
  52. delete;
  53. MaximizedWindowAnimationWatcher& operator=(
  54. const MaximizedWindowAnimationWatcher&) = delete;
  55. // ui::ImplicitAnimationObserver:
  56. void OnImplicitAnimationsCompleted() override { delete this; }
  57. private:
  58. std::unique_ptr<ui::LayerTreeOwner> old_layer_;
  59. };
  60. // Modifies the given |window_list| such that the most-recently used window (if
  61. // any, and if it exists in |window_list|) will be the last window in the list.
  62. void PutMruWindowLast(std::vector<aura::Window*>* window_list) {
  63. DCHECK(window_list);
  64. auto it = std::find_if(
  65. window_list->begin(), window_list->end(),
  66. [](aura::Window* window) { return wm::IsActiveWindow(window); });
  67. if (it == window_list->end())
  68. return;
  69. // Move the active window to the end of the list.
  70. aura::Window* active_window = *it;
  71. window_list->erase(it);
  72. window_list->push_back(active_window);
  73. }
  74. } // namespace
  75. UserSwitchAnimator::UserSwitchAnimator(MultiUserWindowManagerImpl* owner,
  76. const AccountId& new_account_id,
  77. base::TimeDelta animation_speed)
  78. : owner_(owner),
  79. new_account_id_(new_account_id),
  80. animation_speed_(animation_speed),
  81. animation_step_(ANIMATION_STEP_HIDE_OLD_USER),
  82. screen_cover_(GetScreenCover(NULL)),
  83. windows_by_account_id_() {
  84. Shell::Get()->overview_controller()->EndOverview(
  85. OverviewEndAction::kUserSwitch);
  86. BuildUserToWindowsListMap();
  87. AdvanceUserTransitionAnimation();
  88. if (animation_speed_.is_zero()) {
  89. FinalizeAnimation();
  90. } else {
  91. user_changed_animation_timer_ = std::make_unique<base::RepeatingTimer>();
  92. user_changed_animation_timer_->Start(
  93. FROM_HERE, animation_speed_,
  94. base::BindRepeating(&UserSwitchAnimator::AdvanceUserTransitionAnimation,
  95. base::Unretained(this)));
  96. }
  97. }
  98. UserSwitchAnimator::~UserSwitchAnimator() {
  99. FinalizeAnimation();
  100. }
  101. // static
  102. bool UserSwitchAnimator::CoversScreen(aura::Window* window) {
  103. // Full screen covers the screen naturally. Since a normal window can have the
  104. // same size as the work area, we only compare the bounds against the work
  105. // area.
  106. if (wm::WindowStateIs(window, ui::SHOW_STATE_FULLSCREEN))
  107. return true;
  108. gfx::Rect bounds = window->GetBoundsInScreen();
  109. gfx::Rect work_area =
  110. display::Screen::GetScreen()->GetDisplayNearestWindow(window).work_area();
  111. bounds.Intersect(work_area);
  112. return work_area == bounds;
  113. }
  114. void UserSwitchAnimator::AdvanceUserTransitionAnimation() {
  115. DCHECK_NE(animation_step_, ANIMATION_STEP_ENDED);
  116. TransitionWallpaper(animation_step_);
  117. TransitionUserShelf(animation_step_);
  118. TransitionWindows(animation_step_);
  119. // Advance to the next step.
  120. switch (animation_step_) {
  121. case ANIMATION_STEP_HIDE_OLD_USER:
  122. animation_step_ = ANIMATION_STEP_SHOW_NEW_USER;
  123. break;
  124. case ANIMATION_STEP_SHOW_NEW_USER:
  125. animation_step_ = ANIMATION_STEP_FINALIZE;
  126. break;
  127. case ANIMATION_STEP_FINALIZE:
  128. user_changed_animation_timer_.reset();
  129. animation_step_ = ANIMATION_STEP_ENDED;
  130. break;
  131. case ANIMATION_STEP_ENDED:
  132. NOTREACHED();
  133. break;
  134. }
  135. }
  136. void UserSwitchAnimator::CancelAnimation() {
  137. animation_step_ = ANIMATION_STEP_ENDED;
  138. }
  139. void UserSwitchAnimator::FinalizeAnimation() {
  140. user_changed_animation_timer_.reset();
  141. while (ANIMATION_STEP_ENDED != animation_step_)
  142. AdvanceUserTransitionAnimation();
  143. }
  144. void UserSwitchAnimator::TransitionWallpaper(AnimationStep animation_step) {
  145. auto* wallpaper_controller = Shell::Get()->wallpaper_controller();
  146. // Handle the wallpaper switch.
  147. if (animation_step == ANIMATION_STEP_HIDE_OLD_USER) {
  148. // Set the wallpaper cross dissolve animation duration to our complete
  149. // animation cycle for a fade in and fade out.
  150. base::TimeDelta duration =
  151. animation_speed_ * (NO_USER_COVERS_SCREEN == screen_cover_ ? 2 : 0);
  152. wallpaper_controller->SetAnimationDuration(
  153. duration > kMinimalAnimationTime ? duration : kMinimalAnimationTime);
  154. if (screen_cover_ != NEW_USER_COVERS_SCREEN) {
  155. wallpaper_controller->ShowUserWallpaper(new_account_id_);
  156. wallpaper_user_id_for_test_ =
  157. (NO_USER_COVERS_SCREEN == screen_cover_ ? "->" : "") +
  158. new_account_id_.Serialize();
  159. }
  160. } else if (animation_step == ANIMATION_STEP_FINALIZE) {
  161. // Revert the wallpaper cross dissolve animation duration back to the
  162. // default.
  163. if (screen_cover_ == NEW_USER_COVERS_SCREEN)
  164. wallpaper_controller->ShowUserWallpaper(new_account_id_);
  165. // Coming here the wallpaper user id is the final result. No matter how we
  166. // got here.
  167. wallpaper_user_id_for_test_ = new_account_id_.Serialize();
  168. wallpaper_controller->SetAnimationDuration(base::TimeDelta());
  169. }
  170. }
  171. void UserSwitchAnimator::TransitionUserShelf(AnimationStep animation_step) {
  172. if (animation_step != ANIMATION_STEP_SHOW_NEW_USER)
  173. return;
  174. owner_->delegate_->OnTransitionUserShelfToNewAccount();
  175. }
  176. void UserSwitchAnimator::TransitionWindows(AnimationStep animation_step) {
  177. // Disable the window position manager and the MRU window tracker temporarily.
  178. UserChangeActionDisabler disabler;
  179. // Animation duration.
  180. base::TimeDelta duration =
  181. base::Milliseconds(std::max(kMinimalAnimationTime.InMilliseconds(),
  182. 2 * animation_speed_.InMilliseconds()));
  183. switch (animation_step) {
  184. case ANIMATION_STEP_HIDE_OLD_USER: {
  185. // Hide the old users.
  186. for (auto& user_pair : windows_by_account_id_) {
  187. auto& show_for_account_id = user_pair.first;
  188. if (show_for_account_id == new_account_id_)
  189. continue;
  190. bool found_foreground_maximized_window = false;
  191. // We hide the windows such that the MRU window is the last one to be
  192. // hidden, at which point all other windows have already been hidden,
  193. // and hence the FocusController will not be able to find a next
  194. // activateable window to restore focus to, and so we don't change
  195. // window order (crbug.com/424307).
  196. PutMruWindowLast(&(user_pair.second));
  197. for (auto* window : user_pair.second) {
  198. // Minimized visiting windows (minimized windows with an owner
  199. // different than that of the for_show_account_id) should retrun to
  200. // their
  201. // original owners' desktops.
  202. MultiUserWindowManagerImpl::WindowToEntryMap::const_iterator itr =
  203. owner_->window_to_entry().find(window);
  204. DCHECK(itr != owner_->window_to_entry().end());
  205. if (show_for_account_id != itr->second->owner() &&
  206. wm::WindowStateIs(window, ui::SHOW_STATE_MINIMIZED)) {
  207. owner_->ShowWindowForUserIntern(window, itr->second->owner());
  208. wm::Unminimize(window);
  209. continue;
  210. }
  211. if (!found_foreground_maximized_window && CoversScreen(window) &&
  212. screen_cover_ == BOTH_USERS_COVER_SCREEN) {
  213. // Maximized windows should be hidden, but visually kept visible
  214. // in order to prevent showing the background while the animation is
  215. // in progress. Therefore we detach the old layer and recreate fresh
  216. // ones. The old layers will be destructed at the animation step
  217. // |ANIMATION_STEP_FINALIZE|.
  218. // old_layers_.push_back(wm::RecreateLayers(window));
  219. // We only want to do this for the first (foreground) maximized
  220. // window we encounter.
  221. found_foreground_maximized_window = true;
  222. std::unique_ptr<ui::LayerTreeOwner> old_layer =
  223. wm::RecreateLayers(window);
  224. window->layer()->parent()->StackAtBottom(old_layer->root());
  225. ui::ScopedLayerAnimationSettings settings(
  226. window->layer()->GetAnimator());
  227. settings.AddObserver(
  228. new MaximizedWindowAnimationWatcher(std::move(old_layer)));
  229. // Call SetWindowVisibility() within the scope of |settings| so that
  230. // MaximizedWindowAnimationWatcher is notified when the animation
  231. // completes.
  232. owner_->SetWindowVisibility(window, false, duration);
  233. } else {
  234. owner_->SetWindowVisibility(window, false, duration);
  235. }
  236. }
  237. }
  238. // Show new user.
  239. auto new_user_itr = windows_by_account_id_.find(new_account_id_);
  240. auto* desks_controller = Shell::Get()->desks_controller();
  241. if (new_user_itr == windows_by_account_id_.end()) {
  242. // Despite no new windows being shown, we still need to call
  243. // DesksController::OnNewUserShown() to properly restack visible on all
  244. // desks windows.
  245. desks_controller->OnNewUserShown();
  246. return;
  247. }
  248. for (auto* window : new_user_itr->second) {
  249. auto entry = owner_->window_to_entry().find(window);
  250. DCHECK(entry != owner_->window_to_entry().end());
  251. if (entry->second->show())
  252. owner_->SetWindowVisibility(window, true, duration);
  253. }
  254. desks_controller->OnNewUserShown();
  255. break;
  256. }
  257. case ANIMATION_STEP_SHOW_NEW_USER: {
  258. // In order to make the animation look better, we had to move the code
  259. // that shows the new user to the previous step. Hence, we do nothing
  260. // here.
  261. break;
  262. }
  263. case ANIMATION_STEP_FINALIZE: {
  264. // Reactivate the MRU window of the new user.
  265. aura::Window::Windows mru_list =
  266. Shell::Get()->mru_window_tracker()->BuildMruWindowList(kActiveDesk);
  267. if (!mru_list.empty()) {
  268. aura::Window* window = mru_list[0];
  269. if (owner_->IsWindowOnDesktopOfUser(window, new_account_id_) &&
  270. !wm::WindowStateIs(window, ui::SHOW_STATE_MINIMIZED)) {
  271. // Several unit tests come here without an activation client.
  272. wm::ActivationClient* client =
  273. wm::GetActivationClient(window->GetRootWindow());
  274. if (client)
  275. client->ActivateWindow(window);
  276. }
  277. }
  278. break;
  279. }
  280. case ANIMATION_STEP_ENDED:
  281. NOTREACHED();
  282. break;
  283. }
  284. }
  285. UserSwitchAnimator::TransitioningScreenCover UserSwitchAnimator::GetScreenCover(
  286. aura::Window* root_window) {
  287. TransitioningScreenCover cover = NO_USER_COVERS_SCREEN;
  288. for (auto& pair : owner_->window_to_entry()) {
  289. aura::Window* window = pair.first;
  290. if (root_window && window->GetRootWindow() != root_window)
  291. continue;
  292. if (window->IsVisible() && CoversScreen(window)) {
  293. if (cover == NEW_USER_COVERS_SCREEN)
  294. return BOTH_USERS_COVER_SCREEN;
  295. else
  296. cover = OLD_USER_COVERS_SCREEN;
  297. } else if (owner_->IsWindowOnDesktopOfUser(window, new_account_id_) &&
  298. CoversScreen(window)) {
  299. if (cover == OLD_USER_COVERS_SCREEN)
  300. return BOTH_USERS_COVER_SCREEN;
  301. else
  302. cover = NEW_USER_COVERS_SCREEN;
  303. }
  304. }
  305. return cover;
  306. }
  307. void UserSwitchAnimator::BuildUserToWindowsListMap() {
  308. // This is to be called only at the time this animation is constructed.
  309. DCHECK(windows_by_account_id_.empty());
  310. // For each unique parent window, we enumerate its children windows, and
  311. // for each child if it's in the |window_to_entry()| map, we add it to the
  312. // |windows_by_account_id_| map.
  313. // This gives us a list of windows per each user that is in the same order
  314. // they were created in their parent windows.
  315. std::set<aura::Window*> parent_windows;
  316. auto& window_to_entry_map = owner_->window_to_entry();
  317. for (auto& window_entry_pair : window_to_entry_map) {
  318. aura::Window* parent_window = window_entry_pair.first->parent();
  319. if (parent_windows.find(parent_window) == parent_windows.end()) {
  320. parent_windows.insert(parent_window);
  321. for (auto* child_window : parent_window->children()) {
  322. auto itr = window_to_entry_map.find(child_window);
  323. if (itr != window_to_entry_map.end()) {
  324. windows_by_account_id_[itr->second->show_for_user()].push_back(
  325. child_window);
  326. }
  327. }
  328. }
  329. }
  330. }
  331. } // namespace ash