1 // Copyright 2019 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 "ash/shelf/hotseat_widget.h"
6 
7 #include <utility>
8 
9 #include "ash/focus_cycler.h"
10 #include "ash/keyboard/ui/keyboard_ui_controller.h"
11 #include "ash/public/cpp/ash_features.h"
12 #include "ash/public/cpp/shelf_config.h"
13 #include "ash/public/cpp/shelf_model.h"
14 #include "ash/public/cpp/shelf_types.h"
15 #include "ash/public/cpp/wallpaper_controller_observer.h"
16 #include "ash/shelf/hotseat_transition_animator.h"
17 #include "ash/shelf/scrollable_shelf_view.h"
18 #include "ash/shelf/shelf_app_button.h"
19 #include "ash/shelf/shelf_layout_manager.h"
20 #include "ash/shelf/shelf_navigation_widget.h"
21 #include "ash/shelf/shelf_view.h"
22 #include "ash/shell.h"
23 #include "ash/system/status_area_widget.h"
24 #include "ash/wallpaper/wallpaper_controller_impl.h"
25 #include "ash/wm/overview/overview_controller.h"
26 #include "ash/wm/overview/overview_observer.h"
27 #include "ash/wm/tablet_mode/tablet_mode_controller.h"
28 #include "base/metrics/histogram_macros.h"
29 #include "ui/aura/scoped_window_targeter.h"
30 #include "ui/aura/window_targeter.h"
31 #include "ui/compositor/animation_throughput_reporter.h"
32 #include "ui/compositor/layer_animation_sequence.h"
33 #include "ui/compositor/scoped_layer_animation_settings.h"
34 #include "ui/gfx/color_analysis.h"
35 #include "ui/gfx/color_palette.h"
36 #include "ui/gfx/color_utils.h"
37 #include "ui/gfx/geometry/rounded_corners_f.h"
38 #include "ui/views/layout/fill_layout.h"
39 #include "ui/views/view_targeter_delegate.h"
40 #include "ui/views/widget/widget_delegate.h"
41 
42 namespace ash {
43 namespace {
44 
DoScopedAnimationSetting(ui::ScopedLayerAnimationSettings * animation_setter)45 void DoScopedAnimationSetting(
46     ui::ScopedLayerAnimationSettings* animation_setter) {
47   animation_setter->SetTransitionDuration(
48       ShelfConfig::Get()->shelf_animation_duration());
49   animation_setter->SetTweenType(gfx::Tween::EASE_OUT);
50   animation_setter->SetPreemptionStrategy(
51       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
52 }
53 
54 // Returns whether there is special hotseat animation for |transition|.
HasSpecialAnimation(HotseatWidget::StateTransition transition)55 bool HasSpecialAnimation(HotseatWidget::StateTransition transition) {
56   switch (transition) {
57     case HotseatWidget::StateTransition::kHomeLauncherAndExtended:
58     case HotseatWidget::StateTransition::kHomeLauncherAndHidden:
59       return true;
60     case HotseatWidget::StateTransition::kHiddenAndExtended:
61     case HotseatWidget::StateTransition::kOther:
62       return false;
63   }
64 }
65 
66 // Calculates the state transition type for the given previous state and
67 // the target state.
CalculateHotseatStateTransition(HotseatState previous_state,HotseatState target_state)68 HotseatWidget::StateTransition CalculateHotseatStateTransition(
69     HotseatState previous_state,
70     HotseatState target_state) {
71   if (previous_state == HotseatState::kNone ||
72       target_state == HotseatState::kNone) {
73     return HotseatWidget::StateTransition::kOther;
74   }
75 
76   if (previous_state == target_state)
77     return HotseatWidget::StateTransition::kOther;
78 
79   const bool related_to_homelauncher =
80       (previous_state == HotseatState::kShownHomeLauncher ||
81        target_state == HotseatState::kShownHomeLauncher);
82   const bool related_to_extended = (previous_state == HotseatState::kExtended ||
83                                     target_state == HotseatState::kExtended);
84   const bool related_to_hidden = (previous_state == HotseatState::kHidden ||
85                                   target_state == HotseatState::kHidden);
86 
87   if (related_to_homelauncher && related_to_extended)
88     return HotseatWidget::StateTransition::kHomeLauncherAndExtended;
89 
90   if (related_to_homelauncher && related_to_hidden)
91     return HotseatWidget::StateTransition::kHomeLauncherAndHidden;
92 
93   if (related_to_extended && related_to_hidden)
94     return HotseatWidget::StateTransition::kHiddenAndExtended;
95 
96   return HotseatWidget::StateTransition::kOther;
97 }
98 
99 // Base class for hotseat animation transition.
100 class HotseatStateTransitionAnimation : public ui::LayerAnimationElement {
101  public:
HotseatStateTransitionAnimation(const gfx::Rect & target_bounds_in_screen,double target_opacity,ui::Layer * hotseat_layer,HotseatWidget * hotseat_widget)102   HotseatStateTransitionAnimation(const gfx::Rect& target_bounds_in_screen,
103                                   double target_opacity,
104                                   ui::Layer* hotseat_layer,
105                                   HotseatWidget* hotseat_widget)
106       : ui::LayerAnimationElement(
107             LayerAnimationElement::BOUNDS | LayerAnimationElement::OPACITY,
108             hotseat_layer->GetAnimator()->GetTransitionDuration()),
109         target_widget_bounds_(target_bounds_in_screen),
110         target_opacity_(target_opacity),
111         tween_type_(hotseat_layer->GetAnimator()->tween_type()),
112         hotseat_widget_(hotseat_widget) {}
113 
114   ~HotseatStateTransitionAnimation() override = default;
115 
116   HotseatStateTransitionAnimation(const HotseatStateTransitionAnimation& rhs) =
117       delete;
118   HotseatStateTransitionAnimation& operator=(
119       const HotseatStateTransitionAnimation& rhs) = delete;
120 
121  protected:
122   // ui::LayerAnimationElement:
OnGetTarget(TargetValue * target) const123   void OnGetTarget(TargetValue* target) const override {
124     target->opacity = target_opacity_;
125     target->bounds = target_widget_bounds_;
126   }
127 
GetScrollableShelfView()128   ScrollableShelfView* GetScrollableShelfView() {
129     return hotseat_widget_->scrollable_shelf_view();
130   }
131 
132   // Hotseat widget's target bounds in screen.
133   gfx::Rect target_widget_bounds_;
134 
135   // Hotseat widget's initial opacity.
136   double start_opacity_ = 0.f;
137 
138   // Hotseat widget's target opacity.
139   double target_opacity_ = 0.f;
140 
141   gfx::Tween::Type tween_type_ = gfx::Tween::LINEAR;
142 
143   HotseatWidget* hotseat_widget_ = nullptr;
144 };
145 
146 // Animation implemented specifically for the transition between the home
147 // launcher state and the extended state.
148 class HomeAndExtendedTransitionAnimation
149     : public HotseatStateTransitionAnimation {
150  public:
HomeAndExtendedTransitionAnimation(const gfx::Rect & target_bounds_in_screen,double target_opacity,ui::Layer * hotseat_layer,HotseatWidget * hotseat_widget)151   HomeAndExtendedTransitionAnimation(const gfx::Rect& target_bounds_in_screen,
152                                      double target_opacity,
153                                      ui::Layer* hotseat_layer,
154                                      HotseatWidget* hotseat_widget)
155       : HotseatStateTransitionAnimation(target_bounds_in_screen,
156                                         target_opacity,
157                                         hotseat_layer,
158                                         hotseat_widget) {}
159   ~HomeAndExtendedTransitionAnimation() override = default;
160 
161   HomeAndExtendedTransitionAnimation(
162       const HomeAndExtendedTransitionAnimation& rhs) = delete;
163   HomeAndExtendedTransitionAnimation& operator=(
164       const HomeAndExtendedTransitionAnimation& rhs) = delete;
165 
166  private:
167   // HotseatStateTransitionAnimation:
OnStart(ui::LayerAnimationDelegate * delegate)168   void OnStart(ui::LayerAnimationDelegate* delegate) override {
169     DCHECK(hotseat_widget_->GetShelfView()->shelf()->IsHorizontalAlignment());
170 
171     ScrollableShelfView* scrollable_shelf_view = GetScrollableShelfView();
172     scrollable_shelf_view->set_is_padding_configured_externally(
173         /*is_padding_configured_externally=*/true);
174 
175     // Save initial and target padding insets.
176     initial_padding_insets_ = scrollable_shelf_view->edge_padding_insets();
177     target_padding_insets_ =
178         scrollable_shelf_view->CalculateEdgePadding(/*use_target_bounds=*/true);
179 
180     // Save initial opacity.
181     start_opacity_ = hotseat_widget_->GetNativeView()->layer()->opacity();
182 
183     // Save initial hotseat background bounds.
184     initial_hotseat_background_in_screen_ =
185         hotseat_widget_->GetWindowBoundsInScreen();
186     initial_hotseat_background_in_screen_.Inset(initial_padding_insets_);
187 
188     // Save target hotseat background bounds.
189     target_hotseat_background_in_screen_ = target_widget_bounds_;
190     target_hotseat_background_in_screen_.Inset(target_padding_insets_);
191   }
192 
193   // HotseatStateTransitionAnimation:
OnProgress(double current,ui::LayerAnimationDelegate * delegate)194   bool OnProgress(double current,
195                   ui::LayerAnimationDelegate* delegate) override {
196     const double tweened = gfx::Tween::CalculateValue(tween_type_, current);
197 
198     // Set scrollable shelf view's padding insets.
199     gfx::Insets insets_in_animation_progress;
200     insets_in_animation_progress.set_left(gfx::Tween::LinearIntValueBetween(
201         tweened, initial_padding_insets_.left(),
202         target_padding_insets_.left()));
203     insets_in_animation_progress.set_right(gfx::Tween::LinearIntValueBetween(
204         tweened, initial_padding_insets_.right(),
205         target_padding_insets_.right()));
206     ScrollableShelfView* scrollable_shelf_view = GetScrollableShelfView();
207     scrollable_shelf_view->SetEdgePaddingInsets(insets_in_animation_progress);
208 
209     // Update hotseat widget opacity.
210     delegate->SetOpacityFromAnimation(
211         gfx::Tween::DoubleValueBetween(tweened, start_opacity_,
212                                        target_opacity_),
213         ui::PropertyChangeReason::FROM_ANIMATION);
214 
215     // Calculate the hotseat widget's bounds.
216     const gfx::Rect hotseat_background_in_progress =
217         gfx::Tween::RectValueBetween(tweened,
218                                      initial_hotseat_background_in_screen_,
219                                      target_hotseat_background_in_screen_);
220     gfx::Rect widget_bounds_in_progress = hotseat_background_in_progress;
221     widget_bounds_in_progress.Inset(
222         -scrollable_shelf_view->edge_padding_insets());
223 
224     // Update hotseat widget bounds.
225     delegate->SetBoundsFromAnimation(widget_bounds_in_progress,
226                                      ui::PropertyChangeReason::FROM_ANIMATION);
227 
228     // Do recovering when the animation ends.
229     if (current == 1.f) {
230       scrollable_shelf_view->set_is_padding_configured_externally(
231           /*is_padding_configured_externally=*/false);
232     }
233 
234     return true;
235   }
236 
237   // HotseatStateTransitionAnimation:
OnAbort(ui::LayerAnimationDelegate * delegate)238   void OnAbort(ui::LayerAnimationDelegate* delegate) override {
239     GetScrollableShelfView()->set_is_padding_configured_externally(
240         /*is_padding_configured_externally=*/false);
241   }
242 
243   // Scrollable shelf's initial padding insets.
244   gfx::Insets initial_padding_insets_;
245 
246   // Scrollable shelf's target padding insets.
247   gfx::Insets target_padding_insets_;
248 
249   // Hotseat background's initial bounds in screen.
250   gfx::Rect initial_hotseat_background_in_screen_;
251 
252   // Hotseat background's target bounds in screen.
253   gfx::Rect target_hotseat_background_in_screen_;
254 };
255 
256 // Animation implemented specifically for the transition between the home
257 // launcher state and the hidden state.
258 class HomeAndHiddenTransitionAnimation
259     : public HotseatStateTransitionAnimation {
260  public:
HomeAndHiddenTransitionAnimation(const gfx::Rect & target_bounds_in_screen,double target_opacity,ui::Layer * hotseat_layer,HotseatWidget * hotseat_widget)261   HomeAndHiddenTransitionAnimation(const gfx::Rect& target_bounds_in_screen,
262                                    double target_opacity,
263                                    ui::Layer* hotseat_layer,
264                                    HotseatWidget* hotseat_widget)
265       : HotseatStateTransitionAnimation(target_bounds_in_screen,
266                                         target_opacity,
267                                         hotseat_layer,
268                                         hotseat_widget) {}
269   ~HomeAndHiddenTransitionAnimation() override = default;
270 
271  protected:
272   // HotseatStateTransitionAnimation:
OnStart(ui::LayerAnimationDelegate * delegate)273   void OnStart(ui::LayerAnimationDelegate* delegate) override {
274     DCHECK(hotseat_widget_->GetShelfView()->shelf()->IsHorizontalAlignment());
275 
276     start_opacity_ = hotseat_widget_->GetNativeView()->layer()->opacity();
277 
278     if (hotseat_widget_->state() == HotseatState::kHidden)
279       will_be_hidden_ = true;
280 
281     ScrollableShelfView* scrollable_shelf_view = GetScrollableShelfView();
282     const gfx::Rect current_widget_bounds =
283         hotseat_widget_->GetWindowBoundsInScreen();
284 
285     // Ensure that hotseat only has vertical movement during animation.
286     if (will_be_hidden_) {
287       animation_initial_bounds_ = current_widget_bounds;
288 
289       animation_target_bounds_ = current_widget_bounds;
290       animation_target_bounds_.set_y(target_widget_bounds_.y());
291     } else {
292       animation_initial_bounds_ = target_widget_bounds_;
293       animation_initial_bounds_.set_y(current_widget_bounds.y());
294 
295       // Ensure that hotseat is set with the target bounds at the end of
296       // animation when hotseat is going to show in home launcher.
297       animation_target_bounds_ = target_widget_bounds_;
298       const gfx::Insets target_padding_insets =
299           scrollable_shelf_view->CalculateEdgePadding(
300               /*use_target_bounds=*/true);
301       scrollable_shelf_view->SetEdgePaddingInsets(target_padding_insets);
302       delegate->SetBoundsFromAnimation(
303           animation_initial_bounds_, ui::PropertyChangeReason::FROM_ANIMATION);
304     }
305   }
306 
307   // HotseatStateTransitionAnimation:
OnProgress(double current,ui::LayerAnimationDelegate * delegate)308   bool OnProgress(double current,
309                   ui::LayerAnimationDelegate* delegate) override {
310     const double tweened = gfx::Tween::CalculateValue(tween_type_, current);
311     delegate->SetOpacityFromAnimation(
312         gfx::Tween::DoubleValueBetween(tweened, start_opacity_,
313                                        target_opacity_),
314         ui::PropertyChangeReason::FROM_ANIMATION);
315 
316     const gfx::Rect widget_bounds_in_progress = gfx::Tween::RectValueBetween(
317         tweened, animation_initial_bounds_, animation_target_bounds_);
318 
319     const bool reach_end = current == 1.f;
320 
321     // When hotseat is going to be hidden, |animation_target_bounds_| is not
322     // equal to |target_widget_bounds_|. So hotseat is set with the target
323     // bounds at the end of animation. It does not bring animation regression
324     // since hotseat is invisible to the user when setting bounds.
325     delegate->SetBoundsFromAnimation(will_be_hidden_ && reach_end
326                                          ? target_widget_bounds_
327                                          : widget_bounds_in_progress,
328                                      ui::PropertyChangeReason::FROM_ANIMATION);
329 
330     return true;
331   }
332 
333   // HotseatStateTransitionAnimation:
OnAbort(ui::LayerAnimationDelegate * delegate)334   void OnAbort(ui::LayerAnimationDelegate* delegate) override {}
335 
336  private:
337   // Whether hotseat widget is hidden after state transition animation.
338   bool will_be_hidden_ = false;
339 
340   // Note that |animation_initial_bounds_| and |animation_target_bounds_| may
341   // not be the hotseat's current bounds and |target_widget_bounds_|
342   // respectively.
343   gfx::Rect animation_initial_bounds_;
344   gfx::Rect animation_target_bounds_;
345 };
346 
347 // Custom window targeter for the hotseat. Used so the hotseat only processes
348 // events that land on the visible portion of the hotseat, and only while the
349 // hotseat is not animating.
350 class HotseatWindowTargeter : public aura::WindowTargeter {
351  public:
HotseatWindowTargeter(HotseatWidget * hotseat_widget)352   explicit HotseatWindowTargeter(HotseatWidget* hotseat_widget)
353       : hotseat_widget_(hotseat_widget) {}
354   ~HotseatWindowTargeter() override = default;
355 
356   HotseatWindowTargeter(const HotseatWindowTargeter& other) = delete;
357   HotseatWindowTargeter& operator=(const HotseatWindowTargeter& rhs) = delete;
358 
359   // aura::WindowTargeter:
SubtreeShouldBeExploredForEvent(aura::Window * window,const ui::LocatedEvent & event)360   bool SubtreeShouldBeExploredForEvent(aura::Window* window,
361                                        const ui::LocatedEvent& event) override {
362     // Do not handle events if the hotseat window is animating as it may animate
363     // over other items which want to process events.
364     if (hotseat_widget_->GetLayer()->GetAnimator()->is_animating())
365       return false;
366     return aura::WindowTargeter::SubtreeShouldBeExploredForEvent(window, event);
367   }
368 
GetHitTestRects(aura::Window * target,gfx::Rect * hit_test_rect_mouse,gfx::Rect * hit_test_rect_touch) const369   bool GetHitTestRects(aura::Window* target,
370                        gfx::Rect* hit_test_rect_mouse,
371                        gfx::Rect* hit_test_rect_touch) const override {
372     if (target == hotseat_widget_->GetNativeWindow()) {
373       // Shrink the hit bounds from the size of the window to the size of the
374       // hotseat translucent background.
375       gfx::Rect hit_bounds = target->bounds();
376       hit_bounds.ClampToCenteredSize(
377           hotseat_widget_->GetTranslucentBackgroundSize());
378       *hit_test_rect_mouse = *hit_test_rect_touch = hit_bounds;
379       return true;
380     }
381     return aura::WindowTargeter::GetHitTestRects(target, hit_test_rect_mouse,
382                                                  hit_test_rect_touch);
383   }
384 
385  private:
386   // Unowned and guaranteed to be not null for the duration of |this|.
387   HotseatWidget* const hotseat_widget_;
388 };
389 
390 }  // namespace
391 
392 class HotseatWidget::DelegateView : public HotseatTransitionAnimator::Observer,
393                                     public views::WidgetDelegateView,
394                                     public views::ViewTargeterDelegate,
395                                     public OverviewObserver,
396                                     public WallpaperControllerObserver {
397  public:
DelegateView()398   DelegateView() : translucent_background_(ui::LAYER_SOLID_COLOR) {
399     translucent_background_.SetName("hotseat/Background");
400     SetEventTargeter(std::make_unique<views::ViewTargeter>(this));
401   }
402   ~DelegateView() override;
403 
404   // views::ViewTargetDelegate:
TargetForRect(View * root,const gfx::Rect & rect)405   View* TargetForRect(View* root, const gfx::Rect& rect) override {
406     // If a context menu for a shelf app button is shown, redirect all events to
407     // the shelf app button. Context menus generally capture all events, but
408     // shelf app buttons' context menu redirect gesture events to the hotseat
409     // widget so shelf app button can continue handling drag events.
410     // See also HotseatWidget::OnGestureEvent().
411     views::View* item_with_context_menu =
412         scrollable_shelf_view_->shelf_view()->GetShelfItemViewWithContextMenu();
413     if (item_with_context_menu)
414       return item_with_context_menu;
415     return views::ViewTargeterDelegate::TargetForRect(root, rect);
416   }
417 
418   // Initializes the view.
419   void Init(ScrollableShelfView* scrollable_shelf_view,
420             ui::Layer* parent_layer,
421             HotseatWidget* hotseat_widget);
422 
423   // Updates the hotseat background.
424   void UpdateTranslucentBackground();
425 
426   void SetTranslucentBackground(const gfx::Rect& translucent_background_bounds);
427 
428   // Sets whether the background should be blurred as requested by the argument,
429   // unless the feature flag is disabled or |disable_blur_for_animations_| is
430   // true, in which case this disables background blur.
431   void SetBackgroundBlur(bool enable_blur);
432 
433   // HotseatTransitionAnimator::Observer:
434   void OnHotseatTransitionAnimationWillStart(HotseatState from_state,
435                                              HotseatState to_state) override;
436   void OnHotseatTransitionAnimationEnded(HotseatState from_state,
437                                          HotseatState to_state) override;
438   void OnHotseatTransitionAnimationAborted() override;
439 
440   // views::WidgetDelegateView:
441   bool CanActivate() const override;
442   void ReorderChildLayers(ui::Layer* parent_layer) override;
443 
444   // OverviewObserver:
445   void OnOverviewModeWillStart() override;
446   void OnOverviewModeEndingAnimationComplete(bool canceled) override;
447 
448   // WallpaperControllerObserver:
449   void OnWallpaperColorsChanged() override;
450 
set_focus_cycler(FocusCycler * focus_cycler)451   void set_focus_cycler(FocusCycler* focus_cycler) {
452     focus_cycler_ = focus_cycler;
453   }
454 
background_blur() const455   int background_blur() const {
456     return translucent_background_.background_blur();
457   }
458 
is_translucent_background_visible_for_test()459   bool is_translucent_background_visible_for_test() {
460     return translucent_background_.GetTargetVisibility();
461   }
462 
463  private:
464   void SetParentLayer(ui::Layer* layer);
465 
466   FocusCycler* focus_cycler_ = nullptr;
467   // A background layer that may be visible depending on HotseatState.
468   ui::Layer translucent_background_;
469   ScrollableShelfView* scrollable_shelf_view_ = nullptr;  // unowned.
470   HotseatWidget* hotseat_widget_ = nullptr;               // unowned.
471   // Blur is disabled during animations to improve performance.
472   int blur_lock_ = 0;
473 
474   // The most recent color that the |translucent_background_| has been animated
475   // to.
476   SkColor target_color_ = SK_ColorTRANSPARENT;
477 
478   DISALLOW_COPY_AND_ASSIGN(DelegateView);
479 };
480 
~DelegateView()481 HotseatWidget::DelegateView::~DelegateView() {
482   WallpaperControllerImpl* wallpaper_controller =
483       Shell::Get()->wallpaper_controller();
484   OverviewController* overview_controller = Shell::Get()->overview_controller();
485   if (wallpaper_controller)
486     wallpaper_controller->RemoveObserver(this);
487   if (overview_controller)
488     overview_controller->RemoveObserver(this);
489 }
490 
Init(ScrollableShelfView * scrollable_shelf_view,ui::Layer * parent_layer,HotseatWidget * hotseat_widget)491 void HotseatWidget::DelegateView::Init(
492     ScrollableShelfView* scrollable_shelf_view,
493     ui::Layer* parent_layer,
494     HotseatWidget* hotseat_widget) {
495   hotseat_widget_ = hotseat_widget;
496   SetLayoutManager(std::make_unique<views::FillLayout>());
497 
498   WallpaperControllerImpl* wallpaper_controller =
499       Shell::Get()->wallpaper_controller();
500   OverviewController* overview_controller = Shell::Get()->overview_controller();
501   if (wallpaper_controller)
502     wallpaper_controller->AddObserver(this);
503   if (overview_controller) {
504     overview_controller->AddObserver(this);
505     if (overview_controller->InOverviewSession())
506       ++blur_lock_;
507   }
508   SetParentLayer(parent_layer);
509 
510   DCHECK(scrollable_shelf_view);
511   scrollable_shelf_view_ = scrollable_shelf_view;
512 }
513 
UpdateTranslucentBackground()514 void HotseatWidget::DelegateView::UpdateTranslucentBackground() {
515   if (!HotseatWidget::ShouldShowHotseatBackground()) {
516     translucent_background_.SetVisible(false);
517     SetBackgroundBlur(false);
518     return;
519   }
520 
521   // Layer::SetBounds() does not mirror bounds under RTL. So set the mirrored
522   // bounds explicitly.
523   SetTranslucentBackground(scrollable_shelf_view_->GetMirroredRect(
524       scrollable_shelf_view_->GetHotseatBackgroundBounds()));
525 }
526 
SetTranslucentBackground(const gfx::Rect & background_bounds)527 void HotseatWidget::DelegateView::SetTranslucentBackground(
528     const gfx::Rect& background_bounds) {
529   DCHECK(HotseatWidget::ShouldShowHotseatBackground());
530 
531   translucent_background_.SetVisible(true);
532   SetBackgroundBlur(/*enable_blur=*/true);
533 
534   auto* animator = translucent_background_.GetAnimator();
535 
536   base::Optional<ui::AnimationThroughputReporter> reporter;
537   if (hotseat_widget_ && hotseat_widget_->state() != HotseatState::kNone) {
538     reporter.emplace(animator,
539                      hotseat_widget_->GetTranslucentBackgroundReportCallback());
540   }
541 
542   if (ShelfConfig::Get()->GetDefaultShelfColor() != target_color_) {
543     ui::ScopedLayerAnimationSettings color_animation_setter(animator);
544     DoScopedAnimationSetting(&color_animation_setter);
545     target_color_ = ShelfConfig::Get()->GetDefaultShelfColor();
546     translucent_background_.SetColor(target_color_);
547   }
548 
549   // Animate the bounds change if there's a change of width (for instance when
550   // dragging an app into, or out of, the shelf) and meanwhile scrollable
551   // shelf's bounds does not update at the same time.
552   const bool animate_bounds =
553       background_bounds.width() != translucent_background_.bounds().width() &&
554       (scrollable_shelf_view_ &&
555        !scrollable_shelf_view_->NeedUpdateToTargetBounds());
556   base::Optional<ui::ScopedLayerAnimationSettings> bounds_animation_setter;
557   if (animate_bounds) {
558     bounds_animation_setter.emplace(animator);
559     DoScopedAnimationSetting(&bounds_animation_setter.value());
560   }
561 
562   const int radius = hotseat_widget_->GetHotseatSize() / 2;
563   gfx::RoundedCornersF rounded_corners = {radius, radius, radius, radius};
564   if (translucent_background_.rounded_corner_radii() != rounded_corners)
565     translucent_background_.SetRoundedCornerRadius(rounded_corners);
566 
567   if (translucent_background_.GetTargetBounds() != background_bounds)
568     translucent_background_.SetBounds(background_bounds);
569 }
570 
SetBackgroundBlur(bool enable_blur)571 void HotseatWidget::DelegateView::SetBackgroundBlur(bool enable_blur) {
572   if (!features::IsBackgroundBlurEnabled() || blur_lock_ > 0)
573     return;
574 
575   const int blur_radius =
576       enable_blur ? ShelfConfig::Get()->shelf_blur_radius() : 0;
577   if (translucent_background_.background_blur() != blur_radius)
578     translucent_background_.SetBackgroundBlur(blur_radius);
579 }
580 
OnHotseatTransitionAnimationWillStart(HotseatState from_state,HotseatState to_state)581 void HotseatWidget::DelegateView::OnHotseatTransitionAnimationWillStart(
582     HotseatState from_state,
583     HotseatState to_state) {
584   DCHECK_LE(blur_lock_, 2);
585 
586   SetBackgroundBlur(false);
587   ++blur_lock_;
588 }
589 
OnHotseatTransitionAnimationEnded(HotseatState from_state,HotseatState to_state)590 void HotseatWidget::DelegateView::OnHotseatTransitionAnimationEnded(
591     HotseatState from_state,
592     HotseatState to_state) {
593   DCHECK_GT(blur_lock_, 0);
594 
595   --blur_lock_;
596   SetBackgroundBlur(true);
597 }
598 
OnHotseatTransitionAnimationAborted()599 void HotseatWidget::DelegateView::OnHotseatTransitionAnimationAborted() {
600   DCHECK_GT(blur_lock_, 0);
601 
602   --blur_lock_;
603 }
604 
CanActivate() const605 bool HotseatWidget::DelegateView::CanActivate() const {
606   // We don't want mouse clicks to activate us, but we need to allow
607   // activation when the user is using the keyboard (FocusCycler).
608   return focus_cycler_ && focus_cycler_->widget_activating() == GetWidget();
609 }
610 
ReorderChildLayers(ui::Layer * parent_layer)611 void HotseatWidget::DelegateView::ReorderChildLayers(ui::Layer* parent_layer) {
612   views::View::ReorderChildLayers(parent_layer);
613   parent_layer->StackAtBottom(&translucent_background_);
614 }
615 
OnOverviewModeWillStart()616 void HotseatWidget::DelegateView::OnOverviewModeWillStart() {
617   DCHECK_LE(blur_lock_, 2);
618 
619   SetBackgroundBlur(false);
620   ++blur_lock_;
621 }
622 
OnOverviewModeEndingAnimationComplete(bool canceled)623 void HotseatWidget::DelegateView::OnOverviewModeEndingAnimationComplete(
624     bool canceled) {
625   DCHECK_GT(blur_lock_, 0);
626 
627   --blur_lock_;
628   SetBackgroundBlur(true);
629 }
630 
OnWallpaperColorsChanged()631 void HotseatWidget::DelegateView::OnWallpaperColorsChanged() {
632   UpdateTranslucentBackground();
633 }
634 
SetParentLayer(ui::Layer * layer)635 void HotseatWidget::DelegateView::SetParentLayer(ui::Layer* layer) {
636   layer->Add(&translucent_background_);
637   ReorderLayers();
638 }
639 
640 ////////////////////////////////////////////////////////////////////////////////
641 // ScopedInStateTransition
642 
ScopedInStateTransition(HotseatWidget * hotseat_widget,HotseatState old_state,HotseatState target_state)643 HotseatWidget::ScopedInStateTransition::ScopedInStateTransition(
644     HotseatWidget* hotseat_widget,
645     HotseatState old_state,
646     HotseatState target_state)
647     : hotseat_widget_(hotseat_widget) {
648   hotseat_widget_->state_transition_in_progress_ =
649       CalculateHotseatStateTransition(old_state, target_state);
650 }
651 
~ScopedInStateTransition()652 HotseatWidget::ScopedInStateTransition::~ScopedInStateTransition() {
653   hotseat_widget_->state_transition_in_progress_.reset();
654 }
655 
656 ////////////////////////////////////////////////////////////////////////////////
657 // HotseatWidget
658 
HotseatWidget()659 HotseatWidget::HotseatWidget() : delegate_view_(new DelegateView()) {
660   ShelfConfig::Get()->AddObserver(this);
661 }
662 
~HotseatWidget()663 HotseatWidget::~HotseatWidget() {
664   ui::LayerAnimator* hotseat_layer_animator =
665       GetNativeView()->layer()->GetAnimator();
666   if (hotseat_layer_animator->is_animating())
667     hotseat_layer_animator->AbortAllAnimations();
668 
669   ShelfConfig::Get()->RemoveObserver(this);
670   shelf_->shelf_widget()->hotseat_transition_animator()->RemoveObserver(
671       delegate_view_);
672 }
673 
ShouldShowHotseatBackground()674 bool HotseatWidget::ShouldShowHotseatBackground() {
675   return Shell::Get()->tablet_mode_controller() &&
676          Shell::Get()->tablet_mode_controller()->InTabletMode();
677 }
678 
Initialize(aura::Window * container,Shelf * shelf)679 void HotseatWidget::Initialize(aura::Window* container, Shelf* shelf) {
680   DCHECK(container);
681   DCHECK(shelf);
682   shelf_ = shelf;
683   views::Widget::InitParams params(
684       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
685   params.name = "HotseatWidget";
686   params.delegate = delegate_view_;
687   params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
688   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
689   params.parent = container;
690   params.layer_type = ui::LAYER_NOT_DRAWN;
691   Init(std::move(params));
692   set_focus_on_creation(false);
693 
694   scrollable_shelf_view_ = GetContentsView()->AddChildView(
695       std::make_unique<ScrollableShelfView>(ShelfModel::Get(), shelf));
696   delegate_view_->Init(scrollable_shelf_view(), GetLayer(), this);
697   delegate_view_->SetEnableArrowKeyTraversal(true);
698 
699   // The initialization of scrollable shelf should update the translucent
700   // background which is stored in |delegate_view_|. So initializes
701   // |scrollable_shelf_view_| after |delegate_view_|.
702   scrollable_shelf_view_->Init();
703 }
704 
OnHotseatTransitionAnimatorCreated(HotseatTransitionAnimator * animator)705 void HotseatWidget::OnHotseatTransitionAnimatorCreated(
706     HotseatTransitionAnimator* animator) {
707   shelf_->shelf_widget()->hotseat_transition_animator()->AddObserver(
708       delegate_view_);
709 }
710 
OnMouseEvent(ui::MouseEvent * event)711 void HotseatWidget::OnMouseEvent(ui::MouseEvent* event) {
712   if (event->type() == ui::ET_MOUSE_PRESSED)
713     keyboard::KeyboardUIController::Get()->HideKeyboardImplicitlyByUser();
714   views::Widget::OnMouseEvent(event);
715 }
716 
OnGestureEvent(ui::GestureEvent * event)717 void HotseatWidget::OnGestureEvent(ui::GestureEvent* event) {
718   if (event->type() == ui::ET_GESTURE_TAP_DOWN)
719     keyboard::KeyboardUIController::Get()->HideKeyboardImplicitlyByUser();
720 
721   // Context menus for shelf app button forward gesture events to hotseat
722   // widget, so the shelf app button can continue handling drag even after the
723   // context menu starts capturing events. Ignore events not interesting to the
724   // shelf app button in this state.
725   ShelfAppButton* item_with_context_menu =
726       scrollable_shelf_view_->shelf_view()->GetShelfItemViewWithContextMenu();
727   if (item_with_context_menu &&
728       !ShelfAppButton::ShouldHandleEventFromContextMenu(event)) {
729     event->SetHandled();
730     return;
731   }
732 
733   if (!event->handled())
734     views::Widget::OnGestureEvent(event);
735 
736   // Ensure that the app button's drag state gets cleared on gesture end even if
737   // the event doesn't get delivered to the app button.
738   if (item_with_context_menu && event->type() == ui::ET_GESTURE_END)
739     item_with_context_menu->ClearDragStateOnGestureEnd();
740 }
741 
OnNativeWidgetActivationChanged(bool active)742 bool HotseatWidget::OnNativeWidgetActivationChanged(bool active) {
743   if (!Widget::OnNativeWidgetActivationChanged(active))
744     return false;
745 
746   scrollable_shelf_view_->OnFocusRingActivationChanged(active);
747   return true;
748 }
749 
OnShelfConfigUpdated()750 void HotseatWidget::OnShelfConfigUpdated() {
751   set_manually_extended(false);
752 }
753 
IsExtended() const754 bool HotseatWidget::IsExtended() const {
755   DCHECK(GetShelfView()->shelf()->IsHorizontalAlignment());
756   const int extended_bottom =
757       display::Screen::GetScreen()
758           ->GetDisplayNearestView(GetShelfView()->GetWidget()->GetNativeView())
759           .bounds()
760           .bottom() -
761       (ShelfConfig::Get()->shelf_size() +
762        ShelfConfig::Get()->hotseat_bottom_padding());
763   return GetWindowBoundsInScreen().bottom() == extended_bottom;
764 }
765 
FocusFirstOrLastFocusableChild(bool last)766 void HotseatWidget::FocusFirstOrLastFocusableChild(bool last) {
767   GetShelfView()->FindFirstOrLastFocusableChild(last)->RequestFocus();
768 }
769 
OnTabletModeChanged()770 void HotseatWidget::OnTabletModeChanged() {
771   GetShelfView()->OnTabletModeChanged();
772 }
773 
CalculateShelfViewOpacity() const774 float HotseatWidget::CalculateShelfViewOpacity() const {
775   const float target_opacity =
776       GetShelfView()->shelf()->shelf_layout_manager()->GetOpacity();
777   // Hotseat's shelf view should not be dimmed if hotseat is kExtended.
778   return (state() == HotseatState::kExtended) ? 1.0f : target_opacity;
779 }
780 
UpdateTranslucentBackground()781 void HotseatWidget::UpdateTranslucentBackground() {
782   delegate_view_->UpdateTranslucentBackground();
783 }
784 
CalculateHotseatYInScreen(HotseatState hotseat_target_state) const785 int HotseatWidget::CalculateHotseatYInScreen(
786     HotseatState hotseat_target_state) const {
787   DCHECK(shelf_->IsHorizontalAlignment());
788   int hotseat_distance_from_bottom_of_display = 0;
789   const int hotseat_size = GetHotseatSize();
790   switch (hotseat_target_state) {
791     case HotseatState::kShownClamshell:
792       hotseat_distance_from_bottom_of_display = hotseat_size;
793       break;
794     case HotseatState::kShownHomeLauncher:
795       // When the hotseat state is HotseatState::kShownHomeLauncher, the home
796       // launcher is showing in tablet mode. Elevate the hotseat a few px to
797       // match the navigation and status area.
798       hotseat_distance_from_bottom_of_display =
799           hotseat_size + ShelfConfig::Get()->hotseat_bottom_padding();
800       break;
801     case HotseatState::kHidden:
802       // Show the hotseat offscreen.
803       hotseat_distance_from_bottom_of_display = 0;
804       break;
805     case HotseatState::kExtended:
806       // Show the hotseat at its extended position.
807       hotseat_distance_from_bottom_of_display =
808           ShelfConfig::Get()->in_app_shelf_size() +
809           ShelfConfig::Get()->hotseat_bottom_padding() + hotseat_size;
810       break;
811     case HotseatState::kNone:
812       NOTREACHED();
813   }
814   const int target_shelf_size =
815       shelf_->shelf_widget()->GetTargetBounds().size().height();
816   const int hotseat_y_in_shelf =
817       -(hotseat_distance_from_bottom_of_display - target_shelf_size);
818   const int shelf_y = shelf_->shelf_widget()->GetTargetBounds().y();
819   return hotseat_y_in_shelf + shelf_y;
820 }
821 
CalculateTargetBoundsSize(HotseatState hotseat_target_state) const822 gfx::Size HotseatWidget::CalculateTargetBoundsSize(
823     HotseatState hotseat_target_state) const {
824   const gfx::Rect shelf_bounds = shelf_->shelf_widget()->GetTargetBounds();
825 
826   // |hotseat_size| is the height in horizontal alignment or the width in
827   // vertical alignment.
828   const int hotseat_size = GetHotseatSize();
829 
830   if (hotseat_target_state != HotseatState::kShownHomeLauncher &&
831       hotseat_target_state != HotseatState::kShownClamshell) {
832     DCHECK(shelf_->IsHorizontalAlignment());
833     // Give the hotseat more space if it is shown outside of the shelf.
834     return gfx::Size(shelf_bounds.width(), hotseat_size);
835   }
836 
837   const gfx::Size status_size =
838       shelf_->status_area_widget()->GetTargetBounds().size();
839   const gfx::Rect nav_bounds = shelf_->navigation_widget()->GetVisibleBounds();
840 
841   // The navigation widget has extra padding on the hotseat side, to center the
842   // buttons inside of it. Make sure to get the extra nav widget padding and
843   // take it into account when calculating the hotseat size.
844   const int nav_widget_padding =
845       nav_bounds.size().IsEmpty()
846           ? 0
847           : ShelfConfig::Get()->control_button_edge_spacing(
848                 true /* is_primary_axis_edge */);
849 
850   // The minimum gap between hotseat widget and other shelf components including
851   // the status area widget and shelf navigation widget (or the edge of display,
852   // if the shelf navigation widget does not show).
853   const int group_margin = ShelfConfig::Get()->GetAppIconGroupMargin();
854 
855   if (shelf_->IsHorizontalAlignment()) {
856     const int width = shelf_bounds.width() - nav_bounds.size().width() +
857                       nav_widget_padding - 2 * group_margin -
858                       status_size.width();
859     return gfx::Size(width, hotseat_size);
860   }
861 
862   const int height = shelf_bounds.height() - nav_bounds.size().height() +
863                      nav_widget_padding - 2 * group_margin -
864                      status_size.height();
865   return gfx::Size(hotseat_size, height);
866 }
867 
CalculateTargetBounds()868 void HotseatWidget::CalculateTargetBounds() {
869   ShelfLayoutManager* layout_manager = shelf_->shelf_layout_manager();
870   const HotseatState hotseat_target_state =
871       layout_manager->CalculateHotseatState(layout_manager->visibility_state(),
872                                             layout_manager->auto_hide_state());
873   const gfx::Size hotseat_target_size =
874       CalculateTargetBoundsSize(hotseat_target_state);
875 
876   if (hotseat_target_state == HotseatState::kShownHomeLauncher) {
877     target_size_for_shown_state_ = hotseat_target_size;
878   } else {
879     target_size_for_shown_state_ =
880         CalculateTargetBoundsSize(HotseatState::kShownHomeLauncher);
881   }
882 
883   const gfx::Rect shelf_bounds = shelf_->shelf_widget()->GetTargetBounds();
884   const gfx::Rect status_area_bounds =
885       shelf_->status_area_widget()->GetTargetBounds();
886 
887   // The minimum gap between hotseat widget and other shelf components including
888   // the status area widget and shelf navigation widget (or the edge of display,
889   // if the shelf navigation widget does not show).
890   const int group_margin = ShelfConfig::Get()->GetAppIconGroupMargin();
891 
892   gfx::Point hotseat_origin;
893   if (shelf_->IsHorizontalAlignment()) {
894     int hotseat_x;
895     if (hotseat_target_state != HotseatState::kShownHomeLauncher &&
896         hotseat_target_state != HotseatState::kShownClamshell) {
897       hotseat_x = shelf_bounds.x();
898     } else {
899       hotseat_x = base::i18n::IsRTL()
900                       ? status_area_bounds.right() + group_margin
901                       : status_area_bounds.x() - group_margin -
902                             hotseat_target_size.width();
903     }
904 
905     hotseat_origin =
906         gfx::Point(hotseat_x, CalculateHotseatYInScreen(hotseat_target_state));
907   } else {
908     hotseat_origin =
909         gfx::Point(shelf_bounds.x(), status_area_bounds.y() - group_margin -
910                                          hotseat_target_size.height());
911   }
912 
913   target_bounds_ = gfx::Rect(hotseat_origin, hotseat_target_size);
914 
915   // Check whether |target_bounds_| will change the state of app scaling. If
916   // so, update |target_bounds_| here to avoid re-layout later.
917   MaybeAdjustTargetBoundsForAppScaling(hotseat_target_state);
918 }
919 
GetTargetBounds() const920 gfx::Rect HotseatWidget::GetTargetBounds() const {
921   return target_bounds_;
922 }
923 
UpdateLayout(bool animate)924 void HotseatWidget::UpdateLayout(bool animate) {
925   const LayoutInputs new_layout_inputs = GetLayoutInputs();
926   if (layout_inputs_ == new_layout_inputs)
927     return;
928 
929   // Never show this widget outside of an active session.
930   if (!new_layout_inputs.is_active_session_state)
931     Hide();
932 
933   ui::Layer* shelf_view_layer = GetShelfView()->layer();
934   {
935     ui::ScopedLayerAnimationSettings animation_setter(
936         shelf_view_layer->GetAnimator());
937     animation_setter.SetTransitionDuration(
938         animate ? ShelfConfig::Get()->shelf_animation_duration()
939                 : base::TimeDelta::FromMilliseconds(0));
940     animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
941     animation_setter.SetPreemptionStrategy(
942         ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
943 
944     base::Optional<ui::AnimationThroughputReporter> reporter;
945     if (animate && state_ != HotseatState::kNone) {
946       reporter.emplace(animation_setter.GetAnimator(),
947                        shelf_->GetHotseatTransitionReportCallback(state_));
948     }
949 
950     shelf_view_layer->SetOpacity(new_layout_inputs.shelf_view_opacity);
951   }
952 
953   // If shelf view is invisible, the hotseat should be as well. Otherwise the
954   // hotseat opacit should be 1.0f to preserve background blur.
955   const double target_opacity =
956       (new_layout_inputs.shelf_view_opacity == 0.f ? 0.f : 1.f);
957   const gfx::Rect& target_bounds = new_layout_inputs.bounds;
958 
959   if (animate) {
960     LayoutHotseatByAnimation(target_opacity, target_bounds);
961   } else {
962     ui::Layer* hotseat_layer = GetNativeView()->layer();
963 
964     // If the running bounds animation is not aborted, it will be interrupted
965     // and set hotseat widget with the old target bounds which may differ from
966     // |target_bounds| greatly and bring DCHECK errors. For example,
967     // if hotseat animation is interrupted by the bounds setting triggered by
968     // shelf alignment update, hotseat will be caught in an intermediate state
969     // where the shelf alignment is new and the hotseat bounds are old.
970     hotseat_layer->GetAnimator()->AbortAllAnimations();
971 
972     hotseat_layer->SetOpacity(target_opacity);
973     SetBounds(target_bounds);
974   }
975 
976   layout_inputs_ = new_layout_inputs;
977   delegate_view_->UpdateTranslucentBackground();
978 
979   // Setting visibility during an animation causes the visibility property to
980   // animate. Set the visibility property without an animation.
981   if (new_layout_inputs.shelf_view_opacity != 0.0f &&
982       new_layout_inputs.is_active_session_state) {
983     ShowInactive();
984   }
985 }
986 
UpdateTargetBoundsForGesture(int shelf_position)987 void HotseatWidget::UpdateTargetBoundsForGesture(int shelf_position) {
988   if (shelf_->IsHorizontalAlignment())
989     target_bounds_.set_y(shelf_position);
990   else
991     target_bounds_.set_x(shelf_position);
992 }
993 
GetTranslucentBackgroundSize() const994 gfx::Size HotseatWidget::GetTranslucentBackgroundSize() const {
995   DCHECK(scrollable_shelf_view_);
996   return scrollable_shelf_view_->GetHotseatBackgroundBounds().size();
997 }
998 
SetFocusCycler(FocusCycler * focus_cycler)999 void HotseatWidget::SetFocusCycler(FocusCycler* focus_cycler) {
1000   delegate_view_->set_focus_cycler(focus_cycler);
1001   if (focus_cycler)
1002     focus_cycler->AddWidget(this);
1003 }
1004 
GetShelfView()1005 ShelfView* HotseatWidget::GetShelfView() {
1006   DCHECK(scrollable_shelf_view_);
1007   return scrollable_shelf_view_->shelf_view();
1008 }
1009 
GetHotseatSize() const1010 int HotseatWidget::GetHotseatSize() const {
1011   return ShelfConfig::Get()->GetShelfButtonSize(target_hotseat_density_);
1012 }
1013 
GetHotseatFullDragAmount() const1014 int HotseatWidget::GetHotseatFullDragAmount() const {
1015   ShelfConfig* shelf_config = ShelfConfig::Get();
1016   return shelf_config->shelf_size() + shelf_config->hotseat_bottom_padding() +
1017          GetHotseatSize();
1018 }
1019 
UpdateTargetHotseatDensityIfNeeded()1020 bool HotseatWidget::UpdateTargetHotseatDensityIfNeeded() {
1021   if (CalculateTargetHotseatDensity() == target_hotseat_density_) {
1022     return false;
1023   }
1024 
1025   shelf_->shelf_layout_manager()->LayoutShelf(/*animate=*/true);
1026   return true;
1027 }
1028 
GetHotseatBackgroundBlurForTest() const1029 int HotseatWidget::GetHotseatBackgroundBlurForTest() const {
1030   return delegate_view_->background_blur();
1031 }
1032 
GetIsTranslucentBackgroundVisibleForTest() const1033 bool HotseatWidget::GetIsTranslucentBackgroundVisibleForTest() const {
1034   return delegate_view_->is_translucent_background_visible_for_test();
1035 }
1036 
IsShowingShelfMenu() const1037 bool HotseatWidget::IsShowingShelfMenu() const {
1038   return GetShelfView()->IsShowingMenu();
1039 }
1040 
EventTargetsShelfView(const ui::LocatedEvent & event) const1041 bool HotseatWidget::EventTargetsShelfView(const ui::LocatedEvent& event) const {
1042   DCHECK_EQ(event.target(), GetNativeWindow());
1043   gfx::Point location_in_shelf_view = event.location();
1044   views::View::ConvertPointFromWidget(scrollable_shelf_view_,
1045                                       &location_in_shelf_view);
1046   return scrollable_shelf_view_->GetHotseatBackgroundBounds().Contains(
1047       location_in_shelf_view);
1048 }
1049 
GetShelfView() const1050 const ShelfView* HotseatWidget::GetShelfView() const {
1051   return const_cast<const ShelfView*>(
1052       const_cast<HotseatWidget*>(this)->GetShelfView());
1053 }
1054 
1055 metrics_util::ReportCallback
GetTranslucentBackgroundReportCallback()1056 HotseatWidget::GetTranslucentBackgroundReportCallback() {
1057   return shelf_->GetTranslucentBackgroundReportCallback(state_);
1058 }
1059 
SetState(HotseatState state)1060 void HotseatWidget::SetState(HotseatState state) {
1061   if (state_ == state)
1062     return;
1063 
1064   state_ = state;
1065 
1066   // If the hotseat is not extended we can use the normal targeting as the
1067   // hidden parts of the hotseat will not block non-shelf items from taking
1068   if (state == HotseatState::kExtended) {
1069     hotseat_window_targeter_ = std::make_unique<aura::ScopedWindowTargeter>(
1070         GetNativeWindow(), std::make_unique<HotseatWindowTargeter>(this));
1071   } else {
1072     hotseat_window_targeter_.reset();
1073   }
1074 }
1075 
GetLayoutInputs() const1076 HotseatWidget::LayoutInputs HotseatWidget::GetLayoutInputs() const {
1077   const ShelfLayoutManager* layout_manager = shelf_->shelf_layout_manager();
1078   return {target_bounds_, CalculateShelfViewOpacity(),
1079           layout_manager->is_active_session_state()};
1080 }
1081 
MaybeAdjustTargetBoundsForAppScaling(HotseatState hotseat_target_state)1082 void HotseatWidget::MaybeAdjustTargetBoundsForAppScaling(
1083     HotseatState hotseat_target_state) {
1084   // Return early if app scaling state does not change.
1085   HotseatDensity new_target_hotseat_density = CalculateTargetHotseatDensity();
1086   if (new_target_hotseat_density == target_hotseat_density_)
1087     return;
1088 
1089   target_hotseat_density_ = new_target_hotseat_density;
1090 
1091   // Update app icons of shelf view.
1092   scrollable_shelf_view_->shelf_view()->OnShelfConfigUpdated();
1093 
1094   const gfx::Point adjusted_hotseat_origin = gfx::Point(
1095       target_bounds_.x(), CalculateHotseatYInScreen(hotseat_target_state));
1096   target_bounds_ =
1097       gfx::Rect(adjusted_hotseat_origin,
1098                 gfx::Size(target_bounds_.width(), GetHotseatSize()));
1099 }
1100 
CalculateTargetHotseatDensity() const1101 HotseatDensity HotseatWidget::CalculateTargetHotseatDensity() const {
1102   // App scaling is only applied to the standard shelf. So the hotseat density
1103   // should not update in dense shelf.
1104   if (ShelfConfig::Get()->is_dense())
1105     return target_hotseat_density_;
1106 
1107   // TODO(crbug.com/1081476): Currently the scaling animation of hotseat bounds
1108   // and that of shelf icons do not synchronize due to performance issue. As a
1109   // result, shelf scaling is not applied to the hotseat state transition, such
1110   // as the transition from the home launcher state to the extended state.
1111   // Hotseat density relies on the hotseat bounds in the home launcher state
1112   // instead of the current hotseat state.
1113 
1114   // Try candidate button sizes in decreasing order. If shelf buttons in one
1115   // size can show without scrolling, return the density type corresponding to
1116   // that particular size; if no candidate size can make it, return
1117   // HotseatDensity::kDense.
1118   const std::vector<HotseatDensity> kCandidates = {HotseatDensity::kNormal,
1119                                                    HotseatDensity::kSemiDense};
1120   for (const auto& candidate : kCandidates) {
1121     if (!scrollable_shelf_view_->RequiresScrollingForItemSize(
1122             target_size_for_shown_state_,
1123             ShelfConfig::Get()->GetShelfButtonSize(candidate))) {
1124       return candidate;
1125     }
1126   }
1127   return HotseatDensity::kDense;
1128 }
1129 
LayoutHotseatByAnimation(double target_opacity,const gfx::Rect & target_bounds)1130 void HotseatWidget::LayoutHotseatByAnimation(double target_opacity,
1131                                              const gfx::Rect& target_bounds) {
1132   ui::Layer* hotseat_layer = GetNativeView()->layer();
1133 
1134   ui::ScopedLayerAnimationSettings animation_setter(
1135       hotseat_layer->GetAnimator());
1136   animation_setter.SetTransitionDuration(
1137       ShelfConfig::Get()->shelf_animation_duration());
1138   animation_setter.SetTweenType(gfx::Tween::EASE_OUT);
1139   animation_setter.SetPreemptionStrategy(
1140       ui::LayerAnimator::IMMEDIATELY_ANIMATE_TO_NEW_TARGET);
1141 
1142   base::Optional<ui::AnimationThroughputReporter> reporter;
1143   if (state_ != HotseatState::kNone) {
1144     reporter.emplace(animation_setter.GetAnimator(),
1145                      shelf_->GetHotseatTransitionReportCallback(state_));
1146   }
1147 
1148   if (!state_transition_in_progress_.has_value()) {
1149     // Hotseat animation is not triggered by the update in |state_|. So apply
1150     // the normal bounds animation.
1151     StartNormalBoundsAnimation(target_opacity, target_bounds);
1152     return;
1153   }
1154 
1155   if (HasSpecialAnimation(*state_transition_in_progress_)) {
1156     StartHotseatTransitionAnimation(*state_transition_in_progress_,
1157                                     target_opacity, target_bounds);
1158   } else {
1159     StartNormalBoundsAnimation(target_opacity, target_bounds);
1160   }
1161 }
1162 
StartHotseatTransitionAnimation(StateTransition state_transition,double target_opacity,const gfx::Rect & target_bounds)1163 void HotseatWidget::StartHotseatTransitionAnimation(
1164     StateTransition state_transition,
1165     double target_opacity,
1166     const gfx::Rect& target_bounds) {
1167   ui::Layer* hotseat_layer = GetNativeView()->layer();
1168   std::unique_ptr<ui::LayerAnimationElement> animation_elements;
1169   switch (state_transition) {
1170     case StateTransition::kHomeLauncherAndExtended:
1171       animation_elements = std::make_unique<HomeAndExtendedTransitionAnimation>(
1172           target_bounds, target_opacity, hotseat_layer,
1173           /*hotseat_widget=*/this);
1174       break;
1175     case StateTransition::kHomeLauncherAndHidden:
1176       animation_elements = std::make_unique<HomeAndHiddenTransitionAnimation>(
1177           target_bounds, target_opacity, hotseat_layer,
1178           /*hotseat_widget=*/this);
1179       break;
1180     case StateTransition::kHiddenAndExtended:
1181     case StateTransition::kOther:
1182       NOTREACHED();
1183   }
1184 
1185   auto* sequence =
1186       new ui::LayerAnimationSequence(std::move(animation_elements));
1187   hotseat_layer->GetAnimator()->StartAnimation(sequence);
1188 }
1189 
StartNormalBoundsAnimation(double target_opacity,const gfx::Rect & target_bounds)1190 void HotseatWidget::StartNormalBoundsAnimation(double target_opacity,
1191                                                const gfx::Rect& target_bounds) {
1192   GetNativeView()->layer()->SetOpacity(target_opacity);
1193   SetBounds(target_bounds);
1194 }
1195 
1196 }  // namespace ash
1197