1 // Copyright 2020 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/system/holding_space/holding_space_tray_bubble.h"
6 
7 #include <vector>
8 
9 #include "ash/public/cpp/holding_space/holding_space_constants.h"
10 #include "ash/public/cpp/holding_space/holding_space_item.h"
11 #include "ash/public/cpp/holding_space/holding_space_metrics.h"
12 #include "ash/public/cpp/holding_space/holding_space_prefs.h"
13 #include "ash/public/cpp/shelf_config.h"
14 #include "ash/session/session_controller_impl.h"
15 #include "ash/shell.h"
16 #include "ash/system/holding_space/holding_space_item_view.h"
17 #include "ash/system/holding_space/holding_space_tray.h"
18 #include "ash/system/holding_space/pinned_files_container.h"
19 #include "ash/system/holding_space/recent_files_container.h"
20 #include "ash/system/tray/tray_bubble_wrapper.h"
21 #include "ash/system/tray/tray_constants.h"
22 #include "ash/system/tray/tray_utils.h"
23 #include "ash/wm/work_area_insets.h"
24 #include "ui/aura/window.h"
25 #include "ui/gfx/geometry/insets.h"
26 #include "ui/views/layout/box_layout.h"
27 
28 namespace ash {
29 
30 namespace {
31 
32 // Helpers ---------------------------------------------------------------------
33 
34 // Finds all visible `HoldingSpaceItem`s in `parent`'s view hierarchy.
FindVisibleHoldingSpaceItems(views::View * parent,std::vector<const HoldingSpaceItem * > * result)35 void FindVisibleHoldingSpaceItems(
36     views::View* parent,
37     std::vector<const HoldingSpaceItem*>* result) {
38   for (views::View* view : parent->children()) {
39     if (view->GetVisible() && HoldingSpaceItemView::IsInstance(view))
40       result->push_back(HoldingSpaceItemView::Cast(view)->item());
41     FindVisibleHoldingSpaceItems(view, result);
42   }
43 }
44 
45 // Records the time from first availability to first entry into holding space.
RecordTimeFromFirstAvailabilityToFirstEntry(PrefService * prefs)46 void RecordTimeFromFirstAvailabilityToFirstEntry(PrefService* prefs) {
47   base::Time time_of_first_availability =
48       holding_space_prefs::GetTimeOfFirstAvailability(prefs).value();
49   base::Time time_of_first_entry =
50       holding_space_prefs::GetTimeOfFirstEntry(prefs).value();
51   holding_space_metrics::RecordTimeFromFirstAvailabilityToFirstEntry(
52       time_of_first_entry - time_of_first_availability);
53 }
54 
55 // Sets up the layer for the specified `view`.
SetupViewLayer(views::View * view)56 void SetupViewLayer(views::View* view) {
57   view->SetPaintToLayer(ui::LAYER_SOLID_COLOR);
58 
59   auto* layer = view->layer();
60   layer->SetRoundedCornerRadius(gfx::RoundedCornersF{kUnifiedTrayCornerRadius});
61   layer->SetColor(AshColorProvider::Get()->GetBaseLayerColor(
62       AshColorProvider::BaseLayerType::kTransparent80));
63   layer->SetBackgroundBlur(kUnifiedMenuBackgroundBlur);
64   layer->SetFillsBoundsOpaquely(false);
65   layer->SetIsFastRoundedCorner(true);
66 }
67 
68 // HoldingSpaceBubbleContainer -------------------------------------------------
69 
70 class HoldingSpaceBubbleContainer : public views::View {
71  public:
HoldingSpaceBubbleContainer()72   HoldingSpaceBubbleContainer() {
73     layout_ = SetLayoutManager(std::make_unique<views::BoxLayout>(
74         views::BoxLayout::Orientation::kVertical, gfx::Insets(),
75         kHoldingSpaceContainerSpacing));
76   }
77 
SetFlexForChild(views::View * child,int flex)78   void SetFlexForChild(views::View* child, int flex) {
79     layout_->SetFlexForView(child, flex);
80   }
81 
82  private:
83   // views::View:
ChildPreferredSizeChanged(views::View * child)84   void ChildPreferredSizeChanged(views::View* child) override {
85     if (GetWidget())
86       PreferredSizeChanged();
87   }
88 
ChildVisibilityChanged(views::View * child)89   void ChildVisibilityChanged(views::View* child) override {
90     if (GetWidget())
91       PreferredSizeChanged();
92   }
93 
94   views::BoxLayout* layout_ = nullptr;
95 };
96 
97 }  // namespace
98 
99 // HoldingSpaceTrayBubble ------------------------------------------------------
100 
HoldingSpaceTrayBubble(HoldingSpaceTray * holding_space_tray,bool show_by_click)101 HoldingSpaceTrayBubble::HoldingSpaceTrayBubble(
102     HoldingSpaceTray* holding_space_tray,
103     bool show_by_click)
104     : holding_space_tray_(holding_space_tray) {
105   TrayBubbleView::InitParams init_params;
106   init_params.delegate = holding_space_tray;
107   init_params.parent_window = holding_space_tray->GetBubbleWindowContainer();
108   init_params.anchor_view = nullptr;
109   init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
110   init_params.anchor_rect =
111       holding_space_tray->shelf()->GetSystemTrayAnchorRect();
112   init_params.insets = GetTrayBubbleInsets();
113   init_params.shelf_alignment = holding_space_tray->shelf()->alignment();
114   init_params.preferred_width = kHoldingSpaceBubbleWidth;
115   init_params.close_on_deactivate = true;
116   init_params.show_by_click = show_by_click;
117   init_params.has_shadow = false;
118 
119   // Create and customize bubble view.
120   TrayBubbleView* bubble_view = new TrayBubbleView(init_params);
121   bubble_view->SetMaxHeight(CalculateMaxHeight());
122 
123   HoldingSpaceBubbleContainer* bubble_container = bubble_view->AddChildView(
124       std::make_unique<HoldingSpaceBubbleContainer>());
125 
126   // Add pinned files container.
127   pinned_files_container_ = bubble_container->AddChildView(
128       std::make_unique<PinnedFilesContainer>(&delegate_));
129   bubble_container->SetFlexForChild(pinned_files_container_, 1);
130   SetupViewLayer(pinned_files_container_);
131 
132   // Add recent files container.
133   recent_files_container_ = bubble_container->AddChildView(
134       std::make_unique<RecentFilesContainer>(&delegate_));
135   SetupViewLayer(recent_files_container_);
136 
137   // Populate both containers if holding space model has already been attached.
138   HoldingSpaceModel* model = HoldingSpaceController::Get()->model();
139   if (model) {
140     pinned_files_container_->OnHoldingSpaceModelAttached(model);
141     recent_files_container_->OnHoldingSpaceModelAttached(model);
142   }
143 
144   // Show the bubble.
145   bubble_wrapper_ = std::make_unique<TrayBubbleWrapper>(
146       holding_space_tray, bubble_view, false /* is_persistent */);
147 
148   // Set bubble frame to be invisible.
149   bubble_wrapper_->GetBubbleWidget()
150       ->non_client_view()
151       ->frame_view()
152       ->SetVisible(false);
153 
154   PrefService* const prefs =
155       Shell::Get()->session_controller()->GetLastActiveUserPrefService();
156 
157   // Mark when holding space was first entered. If this is not the first entry
158   // into holding space, this will no-op. If this is the first entry, record the
159   // amount of time from first availability to first entry into holding space.
160   if (holding_space_prefs::MarkTimeOfFirstEntry(prefs))
161     RecordTimeFromFirstAvailabilityToFirstEntry(prefs);
162 
163   // Record visible holding space items.
164   std::vector<const HoldingSpaceItem*> visible_items;
165   FindVisibleHoldingSpaceItems(bubble_view, &visible_items);
166   holding_space_metrics::RecordItemCounts(visible_items);
167 
168   shelf_observer_.Add(holding_space_tray_->shelf());
169   tablet_mode_observer_.Add(Shell::Get()->tablet_mode_controller());
170 }
171 
~HoldingSpaceTrayBubble()172 HoldingSpaceTrayBubble::~HoldingSpaceTrayBubble() {
173   bubble_wrapper_->bubble_view()->ResetDelegate();
174 
175   // Explicitly reset holding space item view containers so that they will stop
176   // observing the holding space controller/model while they are asynchronously
177   // destroyed.
178   pinned_files_container_->Reset();
179   recent_files_container_->Reset();
180 }
181 
AnchorUpdated()182 void HoldingSpaceTrayBubble::AnchorUpdated() {
183   bubble_wrapper_->bubble_view()->UpdateBubble();
184 }
185 
GetBubbleView()186 TrayBubbleView* HoldingSpaceTrayBubble::GetBubbleView() {
187   return bubble_wrapper_->bubble_view();
188 }
189 
GetBubbleWidget()190 views::Widget* HoldingSpaceTrayBubble::GetBubbleWidget() {
191   return bubble_wrapper_->GetBubbleWidget();
192 }
193 
CalculateMaxHeight() const194 int HoldingSpaceTrayBubble::CalculateMaxHeight() const {
195   const WorkAreaInsets* work_area = WorkAreaInsets::ForWindow(
196       holding_space_tray_->shelf()->GetWindow()->GetRootWindow());
197 
198   const int bottom =
199       holding_space_tray_->shelf()->IsHorizontalAlignment()
200           ? holding_space_tray_->shelf()->GetShelfBoundsInScreen().y()
201           : work_area->user_work_area_bounds().bottom();
202 
203   const int free_space_height_above_anchor =
204       bottom - work_area->user_work_area_bounds().y();
205 
206   const gfx::Insets insets = GetTrayBubbleInsets();
207   const int bubble_vertical_margin = insets.top() + insets.bottom();
208 
209   return free_space_height_above_anchor - bubble_vertical_margin;
210 }
211 
UpdateBubbleBounds()212 void HoldingSpaceTrayBubble::UpdateBubbleBounds() {
213   bubble_wrapper_->bubble_view()->SetMaxHeight(CalculateMaxHeight());
214   bubble_wrapper_->bubble_view()->ChangeAnchorRect(
215       holding_space_tray_->shelf()->GetSystemTrayAnchorRect());
216 }
217 
OnDisplayConfigurationChanged()218 void HoldingSpaceTrayBubble::OnDisplayConfigurationChanged() {
219   UpdateBubbleBounds();
220 }
221 
OnAutoHideStateChanged(ShelfAutoHideState state)222 void HoldingSpaceTrayBubble::OnAutoHideStateChanged(ShelfAutoHideState state) {
223   UpdateBubbleBounds();
224 }
225 
OnTabletModeStarted()226 void HoldingSpaceTrayBubble::OnTabletModeStarted() {
227   UpdateBubbleBounds();
228 }
229 
OnTabletModeEnded()230 void HoldingSpaceTrayBubble::OnTabletModeEnded() {
231   UpdateBubbleBounds();
232 }
233 
234 }  // namespace ash
235