[go: nahoru, domu]

blob: d10cf5108aeab570cf6b3d4a15f71f598850a686 [file] [log] [blame]
Jazz Xu46752732020-08-25 23:01:051// 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/media/media_tray.h"
6
Jazz Xu943f66a2020-10-28 12:05:537#include "ash/focus_cycler.h"
Jazz Xubfefcba2020-09-25 01:59:138#include "ash/public/cpp/ash_pref_names.h"
Jazz Xu46752732020-08-25 23:01:059#include "ash/public/cpp/media_notification_provider.h"
10#include "ash/resources/vector_icons/vector_icons.h"
Jazz Xubfefcba2020-09-25 01:59:1311#include "ash/root_window_controller.h"
Jazz Xu46752732020-08-25 23:01:0512#include "ash/session/session_controller_impl.h"
Jazz Xuf0d66f2e2020-09-11 20:56:0413#include "ash/shelf/shelf.h"
Jazz Xu46752732020-08-25 23:01:0514#include "ash/shell.h"
15#include "ash/strings/grit/ash_strings.h"
Jazz Xuf0d66f2e2020-09-11 20:56:0416#include "ash/style/ash_color_provider.h"
17#include "ash/system/tray/tray_bubble_view.h"
18#include "ash/system/tray/tray_bubble_wrapper.h"
Jazz Xu46752732020-08-25 23:01:0519#include "ash/system/tray/tray_constants.h"
20#include "ash/system/tray/tray_container.h"
21#include "ash/system/tray/tray_utils.h"
Jazz Xu5ba33292020-10-08 22:00:1322#include "base/metrics/histogram_functions.h"
Jazz Xu46752732020-08-25 23:01:0523#include "base/strings/string_util.h"
Jazz Xuac3c3d02020-10-07 19:55:5524#include "components/media_message_center/media_notification_view_impl.h"
Jazz Xubfefcba2020-09-25 01:59:1325#include "components/prefs/pref_change_registrar.h"
26#include "components/prefs/pref_registry_simple.h"
27#include "components/prefs/pref_service.h"
Jazz Xu88df89552020-10-23 22:56:5128#include "media/base/media_switches.h"
Jazz Xu46752732020-08-25 23:01:0529#include "ui/base/l10n/l10n_util.h"
Jazz Xubfefcba2020-09-25 01:59:1330#include "ui/display/manager/display_manager.h"
31#include "ui/display/manager/managed_display_info.h"
Jazz Xuf0d66f2e2020-09-11 20:56:0432#include "ui/gfx/geometry/insets.h"
Jazz Xu46752732020-08-25 23:01:0533#include "ui/gfx/paint_vector_icon.h"
Jazz Xubfefcba2020-09-25 01:59:1334#include "ui/views/controls/button/image_button.h"
Jazz Xu46752732020-08-25 23:01:0535#include "ui/views/controls/image_view.h"
Jazz Xuf0d66f2e2020-09-11 20:56:0436#include "ui/views/controls/label.h"
Jazz Xuf0d66f2e2020-09-11 20:56:0437#include "ui/views/layout/box_layout.h"
Jazz Xu0fdf99112020-10-02 18:13:1338#include "ui/views/layout/fill_layout.h"
Jazz Xu46752732020-08-25 23:01:0539
40namespace ash {
41
Jazz Xuf0d66f2e2020-09-11 20:56:0442namespace {
43
Jazz Xu0fdf99112020-10-02 18:13:1344constexpr int kNoMediaTextFontSizeIncrease = 2;
Jazz Xu58a681f2020-09-30 01:40:3745constexpr int kTitleFontSizeIncrease = 4;
46constexpr int kTitleViewHeight = 56;
47
Jazz Xubfefcba2020-09-25 01:59:1348constexpr gfx::Insets kTitleViewInsets = gfx::Insets(0, 16, 0, 16);
49
50// Minimum screen diagonal (in inches) for pinning global media controls
51// on shelf by default.
52constexpr float kMinimumScreenSizeDiagonal = 10.0f;
53
54// Calculate screen size and returns true if screen size is larger than
55// kMinimumScreenSizeDiagonal.
56bool GetIsPinnedToShelfByDefault() {
57 // Happens in test.
58 if (!Shell::HasInstance())
59 return false;
60
61 display::ManagedDisplayInfo info =
62 Shell::Get()->display_manager()->GetDisplayInfo(
63 display::Screen::GetScreen()->GetPrimaryDisplay().id());
64 DCHECK(info.device_dpi());
65 float screen_width = info.size_in_pixel().width() / info.device_dpi();
66 float screen_height = info.size_in_pixel().height() / info.device_dpi();
67
68 float diagonal_len = sqrt(pow(screen_width, 2) + pow(screen_height, 2));
69 return diagonal_len > kMinimumScreenSizeDiagonal;
70}
71
Jazz Xu88df89552020-10-23 22:56:5172// Used for getting default pin state for experiment.
73bool GetIsPinnedToShelfByFeatureParams() {
74 switch (media::kCrosGlobalMediaControlsPinParam.Get()) {
75 case media::kCrosGlobalMediaControlsPinOptions::kPin:
76 return true;
77 case media::kCrosGlobalMediaControlsPinOptions::kNotPin:
78 return false;
79 case media::kCrosGlobalMediaControlsPinOptions::kHeuristic:
80 return GetIsPinnedToShelfByDefault();
81 }
82
83 NOTREACHED();
84 return false;
85}
86
Jazz Xubfefcba2020-09-25 01:59:1387// Enum that specifies the pin state of global media controls.
88enum PinState {
89 kDefault = 0,
90 kUnpinned,
91 kPinned,
92};
93
Jazz Xuf0d66f2e2020-09-11 20:56:0494// View that contains global media controls' title.
95class GlobalMediaControlsTitleView : public views::View {
96 public:
97 GlobalMediaControlsTitleView() {
98 SetBorder(views::CreatePaddedBorder(
99 views::CreateSolidSidedBorder(
100 0, 0, kMenuSeparatorWidth, 0,
101 AshColorProvider::Get()->GetContentLayerColor(
102 AshColorProvider::ContentLayerType::kSeparatorColor)),
103 gfx::Insets(kMenuSeparatorVerticalPadding, 0,
104 kMenuSeparatorVerticalPadding - kMenuSeparatorWidth, 0)));
105
106 auto* box_layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
Jazz Xubfefcba2020-09-25 01:59:13107 views::BoxLayout::Orientation::kHorizontal, kTitleViewInsets));
Jazz Xu58a681f2020-09-30 01:40:37108 box_layout->set_minimum_cross_axis_size(kTitleViewHeight);
Jazz Xubfefcba2020-09-25 01:59:13109 box_layout->set_cross_axis_alignment(
110 views::BoxLayout::CrossAxisAlignment::kCenter);
Jazz Xuf0d66f2e2020-09-11 20:56:04111
112 auto* title_label = AddChildView(std::make_unique<views::Label>());
113 title_label->SetText(
114 l10n_util::GetStringUTF16(IDS_ASH_GLOBAL_MEDIA_CONTROLS_TITLE));
115 title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
Jazz Xu58a681f2020-09-30 01:40:37116 title_label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
117 AshColorProvider::ContentLayerType::kTextColorPrimary));
118 title_label->SetAutoColorReadabilityEnabled(false);
119 title_label->SetFontList(views::Label::GetDefaultFontList().Derive(
120 kTitleFontSizeIncrease, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
Jazz Xuf0d66f2e2020-09-11 20:56:04121
Jazz Xubfefcba2020-09-25 01:59:13122 // Media tray should always be pinned to shelf when we are opening the
123 // dialog.
124 DCHECK(MediaTray::IsPinnedToShelf());
125 pin_button_ = AddChildView(std::make_unique<MediaTray::PinButton>());
126
Jazz Xuf0d66f2e2020-09-11 20:56:04127 box_layout->SetFlexForView(title_label, 1);
128 }
Jazz Xubfefcba2020-09-25 01:59:13129
130 views::Button* pin_button() { return pin_button_; }
131
132 private:
133 MediaTray::PinButton* pin_button_ = nullptr;
Jazz Xuf0d66f2e2020-09-11 20:56:04134};
135
136} // namespace
137
Jazz Xubfefcba2020-09-25 01:59:13138// static
139void MediaTray::RegisterProfilePrefs(PrefRegistrySimple* registry) {
140 registry->RegisterIntegerPref(prefs::kGlobalMediaControlsPinned,
141 PinState::kDefault);
142}
143
144// static
145bool MediaTray::IsPinnedToShelf() {
146 PrefService* pref_service =
147 Shell::Get()->session_controller()->GetActivePrefService();
148 DCHECK(pref_service);
149 switch (pref_service->GetInteger(prefs::kGlobalMediaControlsPinned)) {
150 case PinState::kPinned:
151 return true;
152 case PinState::kUnpinned:
153 return false;
154 case PinState::kDefault:
Jazz Xu88df89552020-10-23 22:56:51155 return GetIsPinnedToShelfByFeatureParams();
Jazz Xubfefcba2020-09-25 01:59:13156 }
157
158 NOTREACHED();
159 return false;
160}
161
162// static
163void MediaTray::SetPinnedToShelf(bool pinned) {
164 PrefService* pref_service =
165 Shell::Get()->session_controller()->GetActivePrefService();
166 DCHECK(pref_service);
167 pref_service->SetInteger(prefs::kGlobalMediaControlsPinned,
168 pinned ? PinState::kPinned : PinState::kUnpinned);
169}
170
171MediaTray::PinButton::PinButton()
172 : TopShortcutButton(
Peter Kasting256b9472020-10-31 03:50:53173 base::BindRepeating(&PinButton::ButtonPressed,
174 base::Unretained(this)),
Jazz Xubfefcba2020-09-25 01:59:13175 MediaTray::IsPinnedToShelf() ? kPinnedIcon : kUnpinnedIcon,
176 MediaTray::IsPinnedToShelf()
177 ? IDS_ASH_GLOBAL_MEDIA_CONTROLS_PINNED_BUTTON_TOOLTIP_TEXT
178 : IDS_ASH_GLOBAL_MEDIA_CONTROLS_UNPINNED_BUTTON_TOOLTIP_TEXT) {}
179
Peter Kasting256b9472020-10-31 03:50:53180void MediaTray::PinButton::ButtonPressed() {
Jazz Xubfefcba2020-09-25 01:59:13181 MediaTray::SetPinnedToShelf(!MediaTray::IsPinnedToShelf());
Jazz Xubc6f6562020-10-09 04:28:43182 base::UmaHistogramBoolean("Media.CrosGlobalMediaControls.PinAction",
183 MediaTray::IsPinnedToShelf());
184
Jazz Xubfefcba2020-09-25 01:59:13185 SetImage(views::Button::STATE_NORMAL,
186 CreateVectorIcon(
187 MediaTray::IsPinnedToShelf() ? kPinnedIcon : kUnpinnedIcon,
188 kTrayTopShortcutButtonIconSize,
189 AshColorProvider::Get()->GetContentLayerColor(
190 AshColorProvider::ContentLayerType::kIconColorPrimary)));
191 SetTooltipText(l10n_util::GetStringUTF16(
192 MediaTray::IsPinnedToShelf()
193 ? IDS_ASH_GLOBAL_MEDIA_CONTROLS_PINNED_BUTTON_TOOLTIP_TEXT
194 : IDS_ASH_GLOBAL_MEDIA_CONTROLS_UNPINNED_BUTTON_TOOLTIP_TEXT));
195}
196
Jazz Xu46752732020-08-25 23:01:05197MediaTray::MediaTray(Shelf* shelf) : TrayBackgroundView(shelf) {
Jazz Xu0dda0e3d2020-09-21 22:44:09198 if (MediaNotificationProvider::Get())
199 MediaNotificationProvider::Get()->AddObserver(this);
Jazz Xu46752732020-08-25 23:01:05200
Jazz Xu14f07c92020-09-23 04:08:56201 Shell::Get()->session_controller()->AddObserver(this);
202
Jazz Xu46752732020-08-25 23:01:05203 auto icon = std::make_unique<views::ImageView>();
Allen Bauerd9daac112020-09-15 21:54:04204 icon->SetTooltipText(l10n_util::GetStringUTF16(
Jazz Xu46752732020-08-25 23:01:05205 IDS_ASH_GLOBAL_MEDIA_CONTROLS_BUTTON_TOOLTIP_TEXT));
206 icon->SetImage(gfx::CreateVectorIcon(
207 kGlobalMediaControlsIcon,
208 TrayIconColor(Shell::Get()->session_controller()->GetSessionState())));
209
210 tray_container()->SetMargin(kMediaTrayPadding, 0);
211 icon_ = tray_container()->AddChildView(std::move(icon));
212}
213
214MediaTray::~MediaTray() {
Jazz Xuf0d66f2e2020-09-11 20:56:04215 if (bubble_)
216 bubble_->GetBubbleView()->ResetDelegate();
217
Jazz Xu46752732020-08-25 23:01:05218 if (MediaNotificationProvider::Get())
219 MediaNotificationProvider::Get()->RemoveObserver(this);
Jazz Xu14f07c92020-09-23 04:08:56220
221 Shell::Get()->session_controller()->RemoveObserver(this);
Jazz Xu46752732020-08-25 23:01:05222}
223
224void MediaTray::OnNotificationListChanged() {
225 UpdateDisplayState();
226}
227
Jazz Xuf0d66f2e2020-09-11 20:56:04228void MediaTray::OnNotificationListViewSizeChanged() {
229 if (!bubble_)
230 return;
231
232 bubble_->GetBubbleView()->UpdateBubble();
233}
Jazz Xu46752732020-08-25 23:01:05234
Jan Wilken Dörrie85285b02021-03-11 23:38:47235std::u16string MediaTray::GetAccessibleNameForTray() {
Jazz Xu46752732020-08-25 23:01:05236 return l10n_util::GetStringUTF16(
237 IDS_ASH_GLOBAL_MEDIA_CONTROLS_BUTTON_TOOLTIP_TEXT);
238}
239
240void MediaTray::UpdateAfterLoginStatusChange() {
241 UpdateDisplayState();
242 PreferredSizeChanged();
243}
244
245void MediaTray::HandleLocaleChange() {
Allen Bauerd9daac112020-09-15 21:54:04246 icon_->SetTooltipText(l10n_util::GetStringUTF16(
Jazz Xu46752732020-08-25 23:01:05247 IDS_ASH_GLOBAL_MEDIA_CONTROLS_BUTTON_TOOLTIP_TEXT));
248}
249
Jazz Xuf0d66f2e2020-09-11 20:56:04250bool MediaTray::PerformAction(const ui::Event& event) {
251 if (bubble_)
252 CloseBubble();
253 else
Ahmed Mehfooz15cebdd2021-03-03 01:07:07254 ShowBubble();
Jazz Xuf0d66f2e2020-09-11 20:56:04255 return true;
256}
257
Ahmed Mehfooz15cebdd2021-03-03 01:07:07258void MediaTray::ShowBubble() {
Jazz Xuf0d66f2e2020-09-11 20:56:04259 DCHECK(MediaNotificationProvider::Get());
Jazz Xuee0ed7a2020-11-10 22:43:05260 SetNotificationColorTheme();
Jazz Xuf0d66f2e2020-09-11 20:56:04261
262 TrayBubbleView::InitParams init_params;
263 init_params.delegate = this;
264 init_params.parent_window = GetBubbleWindowContainer();
265 init_params.anchor_view = nullptr;
266 init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
Jazz Xubd417d82020-10-15 22:16:34267 init_params.anchor_rect = GetAnchorBoundsInScreen();
Jazz Xuf0d66f2e2020-09-11 20:56:04268 init_params.insets = GetTrayBubbleInsets();
269 init_params.shelf_alignment = shelf()->alignment();
270 init_params.preferred_width = kTrayMenuWidth;
271 init_params.close_on_deactivate = true;
272 init_params.has_shadow = false;
273 init_params.translucent = true;
274 init_params.corner_radius = kTrayItemCornerRadius;
Ahmed Mehfooz15cebdd2021-03-03 01:07:07275 init_params.reroute_event_handler = true;
Jazz Xuf0d66f2e2020-09-11 20:56:04276
277 TrayBubbleView* bubble_view = new TrayBubbleView(init_params);
278
Jazz Xubfefcba2020-09-25 01:59:13279 auto* title_view = bubble_view->AddChildView(
Jazz Xuf0d66f2e2020-09-11 20:56:04280 std::make_unique<GlobalMediaControlsTitleView>());
Jazz Xubfefcba2020-09-25 01:59:13281 title_view->SetPaintToLayer();
282 title_view->layer()->SetFillsBoundsOpaquely(false);
283 pin_button_ = title_view->pin_button();
Jazz Xuf0d66f2e2020-09-11 20:56:04284
Jazz Xu0fdf99112020-10-02 18:13:13285 content_view_ = bubble_view->AddChildView(
Jazz Xuf0d66f2e2020-09-11 20:56:04286 MediaNotificationProvider::Get()->GetMediaNotificationListView(
Jazz Xuf0d66f2e2020-09-11 20:56:04287 kMenuSeparatorWidth));
288
289 bubble_ = std::make_unique<TrayBubbleWrapper>(this, bubble_view,
290 false /*is_persistent*/);
291 SetIsActive(true);
Jazz Xu5ba33292020-10-08 22:00:13292
293 base::UmaHistogramBoolean("Media.CrosGlobalMediaControls.RepeatUsageOnShelf",
294 bubble_has_shown_);
295 bubble_has_shown_ = true;
Jazz Xuf0d66f2e2020-09-11 20:56:04296}
297
298void MediaTray::CloseBubble() {
299 if (MediaNotificationProvider::Get())
300 MediaNotificationProvider::Get()->OnBubbleClosing();
301 SetIsActive(false);
Jazz Xu0fdf99112020-10-02 18:13:13302 empty_state_view_ = nullptr;
Jazz Xuf0d66f2e2020-09-11 20:56:04303 bubble_.reset();
304 shelf()->UpdateAutoHideState();
305}
306
307void MediaTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {
308 if (bubble_ && bubble_->bubble_view() == bubble_view)
309 CloseBubble();
310}
311
312void MediaTray::ClickedOutsideBubble() {
313 CloseBubble();
314}
315
Jazz Xu14f07c92020-09-23 04:08:56316void MediaTray::OnLockStateChanged(bool locked) {
317 UpdateDisplayState();
318}
319
Jazz Xubfefcba2020-09-25 01:59:13320void MediaTray::OnActiveUserPrefServiceChanged(PrefService* pref_service) {
321 pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
322 pref_change_registrar_->Init(pref_service);
323 pref_change_registrar_->Add(
324 prefs::kGlobalMediaControlsPinned,
325 base::BindRepeating(&MediaTray::OnGlobalMediaControlsPinPrefChanged,
326 base::Unretained(this)));
327 OnGlobalMediaControlsPinPrefChanged();
328}
329
Jazz Xu46752732020-08-25 23:01:05330void MediaTray::UpdateDisplayState() {
331 if (!MediaNotificationProvider::Get())
332 return;
333
Jazz Xu0fdf99112020-10-02 18:13:13334 if (bubble_ && Shell::Get()->session_controller()->IsScreenLocked())
Jazz Xuf0d66f2e2020-09-11 20:56:04335 CloseBubble();
Jazz Xuaa4cbc52020-09-30 19:00:33336
Jazz Xu0fdf99112020-10-02 18:13:13337 bool has_session =
338 MediaNotificationProvider::Get()->HasActiveNotifications() ||
339 MediaNotificationProvider::Get()->HasFrozenNotifications();
340
341 if (bubble_ && !has_session)
342 ShowEmptyState();
343
Jazz Xu7cfbf5f2020-11-10 21:14:55344 if (bubble_ && has_session && empty_state_view_)
345 empty_state_view_->SetVisible(false);
346
Jazz Xu0fdf99112020-10-02 18:13:13347 bool should_show = has_session &&
348 !Shell::Get()->session_controller()->IsScreenLocked() &&
349 IsPinnedToShelf();
350
351 SetVisiblePreferred(should_show);
Jazz Xu46752732020-08-25 23:01:05352}
353
Jan Wilken Dörrie85285b02021-03-11 23:38:47354std::u16string MediaTray::GetAccessibleNameForBubble() {
Jazz Xu4935f6b2020-11-05 19:26:02355 return l10n_util::GetStringUTF16(IDS_ASH_GLOBAL_MEDIA_CONTROLS_TITLE);
356}
357
Jazz Xuac3c3d02020-10-07 19:55:55358void MediaTray::SetNotificationColorTheme() {
359 if (!MediaNotificationProvider::Get())
360 return;
361
362 media_message_center::NotificationTheme theme;
363 theme.primary_text_color = AshColorProvider::Get()->GetContentLayerColor(
364 AshColorProvider::ContentLayerType::kTextColorPrimary);
365 theme.secondary_text_color = AshColorProvider::Get()->GetContentLayerColor(
366 AshColorProvider::ContentLayerType::kTextColorSecondary);
367 theme.enabled_icon_color = AshColorProvider::Get()->GetContentLayerColor(
368 AshColorProvider::ContentLayerType::kIconColorPrimary);
369 theme.disabled_icon_color = AshColorProvider::Get()->GetContentLayerColor(
370 AshColorProvider::ContentLayerType::kIconColorSecondary);
371 theme.separator_color = AshColorProvider::Get()->GetContentLayerColor(
372 AshColorProvider::ContentLayerType::kSeparatorColor);
373 MediaNotificationProvider::Get()->SetColorTheme(theme);
374}
375
Jazz Xubfefcba2020-09-25 01:59:13376void MediaTray::OnGlobalMediaControlsPinPrefChanged() {
377 UpdateDisplayState();
378}
379
Jazz Xu0fdf99112020-10-02 18:13:13380void MediaTray::ShowEmptyState() {
381 DCHECK(content_view_);
Jazz Xu7cfbf5f2020-11-10 21:14:55382 if (empty_state_view_) {
383 empty_state_view_->SetVisible(true);
Jazz Xu0fdf99112020-10-02 18:13:13384 return;
Jazz Xu7cfbf5f2020-11-10 21:14:55385 }
Jazz Xu0fdf99112020-10-02 18:13:13386
387 // Create and add empty state view containing a label indicating there's no
388 // active session
389 auto empty_state_view = std::make_unique<views::View>();
390 auto* layout =
391 empty_state_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
392 views::BoxLayout::Orientation::kHorizontal));
393 layout->set_minimum_cross_axis_size(content_view_->bounds().height());
394 layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kCenter);
395
396 auto no_media_label = std::make_unique<views::Label>();
397 no_media_label->SetAutoColorReadabilityEnabled(false);
398 no_media_label->SetSubpixelRenderingEnabled(false);
399 no_media_label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
400 AshColorProvider::ContentLayerType::kTextColorSecondary));
401 no_media_label->SetText(
402 l10n_util::GetStringUTF16(IDS_ASH_GLOBAL_MEDIA_CONTROLS_NO_MEDIA_TEXT));
403 no_media_label->SetFontList(
404 views::Label::GetDefaultFontList().DeriveWithSizeDelta(
405 kNoMediaTextFontSizeIncrease));
406 empty_state_view->AddChildView(std::move(no_media_label));
407
408 empty_state_view->SetPaintToLayer();
409 empty_state_view->layer()->SetFillsBoundsOpaquely(false);
410 empty_state_view_ =
411 bubble_->GetBubbleView()->AddChildView(std::move(empty_state_view));
412}
413
Jazz Xubd417d82020-10-15 22:16:34414void MediaTray::AnchorUpdated() {
415 if (!bubble_)
416 return;
417
418 bubble_->GetBubbleView()->SetAnchorRect(
419 shelf()->GetStatusAreaWidget()->GetMediaTrayAnchorRect());
420}
421
Jazz Xu46752732020-08-25 23:01:05422} // namespace ash