[go: nahoru, domu]

blob: d10cf5108aeab570cf6b3d4a15f71f598850a686 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/system/media/media_tray.h"
#include "ash/focus_cycler.h"
#include "ash/public/cpp/ash_pref_names.h"
#include "ash/public/cpp/media_notification_provider.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/root_window_controller.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shelf/shelf.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/style/ash_color_provider.h"
#include "ash/system/tray/tray_bubble_view.h"
#include "ash/system/tray/tray_bubble_wrapper.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/tray/tray_container.h"
#include "ash/system/tray/tray_utils.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_util.h"
#include "components/media_message_center/media_notification_view_impl.h"
#include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "media/base/media_switches.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/display/manager/display_manager.h"
#include "ui/display/manager/managed_display_info.h"
#include "ui/gfx/geometry/insets.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/image_view.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
namespace ash {
namespace {
constexpr int kNoMediaTextFontSizeIncrease = 2;
constexpr int kTitleFontSizeIncrease = 4;
constexpr int kTitleViewHeight = 56;
constexpr gfx::Insets kTitleViewInsets = gfx::Insets(0, 16, 0, 16);
// Minimum screen diagonal (in inches) for pinning global media controls
// on shelf by default.
constexpr float kMinimumScreenSizeDiagonal = 10.0f;
// Calculate screen size and returns true if screen size is larger than
// kMinimumScreenSizeDiagonal.
bool GetIsPinnedToShelfByDefault() {
// Happens in test.
if (!Shell::HasInstance())
return false;
display::ManagedDisplayInfo info =
Shell::Get()->display_manager()->GetDisplayInfo(
display::Screen::GetScreen()->GetPrimaryDisplay().id());
DCHECK(info.device_dpi());
float screen_width = info.size_in_pixel().width() / info.device_dpi();
float screen_height = info.size_in_pixel().height() / info.device_dpi();
float diagonal_len = sqrt(pow(screen_width, 2) + pow(screen_height, 2));
return diagonal_len > kMinimumScreenSizeDiagonal;
}
// Used for getting default pin state for experiment.
bool GetIsPinnedToShelfByFeatureParams() {
switch (media::kCrosGlobalMediaControlsPinParam.Get()) {
case media::kCrosGlobalMediaControlsPinOptions::kPin:
return true;
case media::kCrosGlobalMediaControlsPinOptions::kNotPin:
return false;
case media::kCrosGlobalMediaControlsPinOptions::kHeuristic:
return GetIsPinnedToShelfByDefault();
}
NOTREACHED();
return false;
}
// Enum that specifies the pin state of global media controls.
enum PinState {
kDefault = 0,
kUnpinned,
kPinned,
};
// View that contains global media controls' title.
class GlobalMediaControlsTitleView : public views::View {
public:
GlobalMediaControlsTitleView() {
SetBorder(views::CreatePaddedBorder(
views::CreateSolidSidedBorder(
0, 0, kMenuSeparatorWidth, 0,
AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kSeparatorColor)),
gfx::Insets(kMenuSeparatorVerticalPadding, 0,
kMenuSeparatorVerticalPadding - kMenuSeparatorWidth, 0)));
auto* box_layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, kTitleViewInsets));
box_layout->set_minimum_cross_axis_size(kTitleViewHeight);
box_layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
auto* title_label = AddChildView(std::make_unique<views::Label>());
title_label->SetText(
l10n_util::GetStringUTF16(IDS_ASH_GLOBAL_MEDIA_CONTROLS_TITLE));
title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
title_label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kTextColorPrimary));
title_label->SetAutoColorReadabilityEnabled(false);
title_label->SetFontList(views::Label::GetDefaultFontList().Derive(
kTitleFontSizeIncrease, gfx::Font::NORMAL, gfx::Font::Weight::MEDIUM));
// Media tray should always be pinned to shelf when we are opening the
// dialog.
DCHECK(MediaTray::IsPinnedToShelf());
pin_button_ = AddChildView(std::make_unique<MediaTray::PinButton>());
box_layout->SetFlexForView(title_label, 1);
}
views::Button* pin_button() { return pin_button_; }
private:
MediaTray::PinButton* pin_button_ = nullptr;
};
} // namespace
// static
void MediaTray::RegisterProfilePrefs(PrefRegistrySimple* registry) {
registry->RegisterIntegerPref(prefs::kGlobalMediaControlsPinned,
PinState::kDefault);
}
// static
bool MediaTray::IsPinnedToShelf() {
PrefService* pref_service =
Shell::Get()->session_controller()->GetActivePrefService();
DCHECK(pref_service);
switch (pref_service->GetInteger(prefs::kGlobalMediaControlsPinned)) {
case PinState::kPinned:
return true;
case PinState::kUnpinned:
return false;
case PinState::kDefault:
return GetIsPinnedToShelfByFeatureParams();
}
NOTREACHED();
return false;
}
// static
void MediaTray::SetPinnedToShelf(bool pinned) {
PrefService* pref_service =
Shell::Get()->session_controller()->GetActivePrefService();
DCHECK(pref_service);
pref_service->SetInteger(prefs::kGlobalMediaControlsPinned,
pinned ? PinState::kPinned : PinState::kUnpinned);
}
MediaTray::PinButton::PinButton()
: TopShortcutButton(
base::BindRepeating(&PinButton::ButtonPressed,
base::Unretained(this)),
MediaTray::IsPinnedToShelf() ? kPinnedIcon : kUnpinnedIcon,
MediaTray::IsPinnedToShelf()
? IDS_ASH_GLOBAL_MEDIA_CONTROLS_PINNED_BUTTON_TOOLTIP_TEXT
: IDS_ASH_GLOBAL_MEDIA_CONTROLS_UNPINNED_BUTTON_TOOLTIP_TEXT) {}
void MediaTray::PinButton::ButtonPressed() {
MediaTray::SetPinnedToShelf(!MediaTray::IsPinnedToShelf());
base::UmaHistogramBoolean("Media.CrosGlobalMediaControls.PinAction",
MediaTray::IsPinnedToShelf());
SetImage(views::Button::STATE_NORMAL,
CreateVectorIcon(
MediaTray::IsPinnedToShelf() ? kPinnedIcon : kUnpinnedIcon,
kTrayTopShortcutButtonIconSize,
AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconColorPrimary)));
SetTooltipText(l10n_util::GetStringUTF16(
MediaTray::IsPinnedToShelf()
? IDS_ASH_GLOBAL_MEDIA_CONTROLS_PINNED_BUTTON_TOOLTIP_TEXT
: IDS_ASH_GLOBAL_MEDIA_CONTROLS_UNPINNED_BUTTON_TOOLTIP_TEXT));
}
MediaTray::MediaTray(Shelf* shelf) : TrayBackgroundView(shelf) {
if (MediaNotificationProvider::Get())
MediaNotificationProvider::Get()->AddObserver(this);
Shell::Get()->session_controller()->AddObserver(this);
auto icon = std::make_unique<views::ImageView>();
icon->SetTooltipText(l10n_util::GetStringUTF16(
IDS_ASH_GLOBAL_MEDIA_CONTROLS_BUTTON_TOOLTIP_TEXT));
icon->SetImage(gfx::CreateVectorIcon(
kGlobalMediaControlsIcon,
TrayIconColor(Shell::Get()->session_controller()->GetSessionState())));
tray_container()->SetMargin(kMediaTrayPadding, 0);
icon_ = tray_container()->AddChildView(std::move(icon));
}
MediaTray::~MediaTray() {
if (bubble_)
bubble_->GetBubbleView()->ResetDelegate();
if (MediaNotificationProvider::Get())
MediaNotificationProvider::Get()->RemoveObserver(this);
Shell::Get()->session_controller()->RemoveObserver(this);
}
void MediaTray::OnNotificationListChanged() {
UpdateDisplayState();
}
void MediaTray::OnNotificationListViewSizeChanged() {
if (!bubble_)
return;
bubble_->GetBubbleView()->UpdateBubble();
}
std::u16string MediaTray::GetAccessibleNameForTray() {
return l10n_util::GetStringUTF16(
IDS_ASH_GLOBAL_MEDIA_CONTROLS_BUTTON_TOOLTIP_TEXT);
}
void MediaTray::UpdateAfterLoginStatusChange() {
UpdateDisplayState();
PreferredSizeChanged();
}
void MediaTray::HandleLocaleChange() {
icon_->SetTooltipText(l10n_util::GetStringUTF16(
IDS_ASH_GLOBAL_MEDIA_CONTROLS_BUTTON_TOOLTIP_TEXT));
}
bool MediaTray::PerformAction(const ui::Event& event) {
if (bubble_)
CloseBubble();
else
ShowBubble();
return true;
}
void MediaTray::ShowBubble() {
DCHECK(MediaNotificationProvider::Get());
SetNotificationColorTheme();
TrayBubbleView::InitParams init_params;
init_params.delegate = this;
init_params.parent_window = GetBubbleWindowContainer();
init_params.anchor_view = nullptr;
init_params.anchor_mode = TrayBubbleView::AnchorMode::kRect;
init_params.anchor_rect = GetAnchorBoundsInScreen();
init_params.insets = GetTrayBubbleInsets();
init_params.shelf_alignment = shelf()->alignment();
init_params.preferred_width = kTrayMenuWidth;
init_params.close_on_deactivate = true;
init_params.has_shadow = false;
init_params.translucent = true;
init_params.corner_radius = kTrayItemCornerRadius;
init_params.reroute_event_handler = true;
TrayBubbleView* bubble_view = new TrayBubbleView(init_params);
auto* title_view = bubble_view->AddChildView(
std::make_unique<GlobalMediaControlsTitleView>());
title_view->SetPaintToLayer();
title_view->layer()->SetFillsBoundsOpaquely(false);
pin_button_ = title_view->pin_button();
content_view_ = bubble_view->AddChildView(
MediaNotificationProvider::Get()->GetMediaNotificationListView(
kMenuSeparatorWidth));
bubble_ = std::make_unique<TrayBubbleWrapper>(this, bubble_view,
false /*is_persistent*/);
SetIsActive(true);
base::UmaHistogramBoolean("Media.CrosGlobalMediaControls.RepeatUsageOnShelf",
bubble_has_shown_);
bubble_has_shown_ = true;
}
void MediaTray::CloseBubble() {
if (MediaNotificationProvider::Get())
MediaNotificationProvider::Get()->OnBubbleClosing();
SetIsActive(false);
empty_state_view_ = nullptr;
bubble_.reset();
shelf()->UpdateAutoHideState();
}
void MediaTray::HideBubbleWithView(const TrayBubbleView* bubble_view) {
if (bubble_ && bubble_->bubble_view() == bubble_view)
CloseBubble();
}
void MediaTray::ClickedOutsideBubble() {
CloseBubble();
}
void MediaTray::OnLockStateChanged(bool locked) {
UpdateDisplayState();
}
void MediaTray::OnActiveUserPrefServiceChanged(PrefService* pref_service) {
pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>();
pref_change_registrar_->Init(pref_service);
pref_change_registrar_->Add(
prefs::kGlobalMediaControlsPinned,
base::BindRepeating(&MediaTray::OnGlobalMediaControlsPinPrefChanged,
base::Unretained(this)));
OnGlobalMediaControlsPinPrefChanged();
}
void MediaTray::UpdateDisplayState() {
if (!MediaNotificationProvider::Get())
return;
if (bubble_ && Shell::Get()->session_controller()->IsScreenLocked())
CloseBubble();
bool has_session =
MediaNotificationProvider::Get()->HasActiveNotifications() ||
MediaNotificationProvider::Get()->HasFrozenNotifications();
if (bubble_ && !has_session)
ShowEmptyState();
if (bubble_ && has_session && empty_state_view_)
empty_state_view_->SetVisible(false);
bool should_show = has_session &&
!Shell::Get()->session_controller()->IsScreenLocked() &&
IsPinnedToShelf();
SetVisiblePreferred(should_show);
}
std::u16string MediaTray::GetAccessibleNameForBubble() {
return l10n_util::GetStringUTF16(IDS_ASH_GLOBAL_MEDIA_CONTROLS_TITLE);
}
void MediaTray::SetNotificationColorTheme() {
if (!MediaNotificationProvider::Get())
return;
media_message_center::NotificationTheme theme;
theme.primary_text_color = AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kTextColorPrimary);
theme.secondary_text_color = AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kTextColorSecondary);
theme.enabled_icon_color = AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconColorPrimary);
theme.disabled_icon_color = AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kIconColorSecondary);
theme.separator_color = AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kSeparatorColor);
MediaNotificationProvider::Get()->SetColorTheme(theme);
}
void MediaTray::OnGlobalMediaControlsPinPrefChanged() {
UpdateDisplayState();
}
void MediaTray::ShowEmptyState() {
DCHECK(content_view_);
if (empty_state_view_) {
empty_state_view_->SetVisible(true);
return;
}
// Create and add empty state view containing a label indicating there's no
// active session
auto empty_state_view = std::make_unique<views::View>();
auto* layout =
empty_state_view->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal));
layout->set_minimum_cross_axis_size(content_view_->bounds().height());
layout->set_main_axis_alignment(views::BoxLayout::MainAxisAlignment::kCenter);
auto no_media_label = std::make_unique<views::Label>();
no_media_label->SetAutoColorReadabilityEnabled(false);
no_media_label->SetSubpixelRenderingEnabled(false);
no_media_label->SetEnabledColor(AshColorProvider::Get()->GetContentLayerColor(
AshColorProvider::ContentLayerType::kTextColorSecondary));
no_media_label->SetText(
l10n_util::GetStringUTF16(IDS_ASH_GLOBAL_MEDIA_CONTROLS_NO_MEDIA_TEXT));
no_media_label->SetFontList(
views::Label::GetDefaultFontList().DeriveWithSizeDelta(
kNoMediaTextFontSizeIncrease));
empty_state_view->AddChildView(std::move(no_media_label));
empty_state_view->SetPaintToLayer();
empty_state_view->layer()->SetFillsBoundsOpaquely(false);
empty_state_view_ =
bubble_->GetBubbleView()->AddChildView(std::move(empty_state_view));
}
void MediaTray::AnchorUpdated() {
if (!bubble_)
return;
bubble_->GetBubbleView()->SetAnchorRect(
shelf()->GetStatusAreaWidget()->GetMediaTrayAnchorRect());
}
} // namespace ash