blob: 773278d65efe88309f3ab140496d794ce1fbe64c [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 "components/media_message_center/media_notification_view_modern_impl.h"
#include "base/containers/contains.h"
#include "base/metrics/histogram_macros.h"
#include "base/ranges/algorithm.h"
#include "components/media_message_center/media_artwork_view.h"
#include "components/media_message_center/media_controls_progress_view.h"
#include "components/media_message_center/media_notification_background_ash_impl.h"
#include "components/media_message_center/media_notification_background_impl.h"
#include "components/media_message_center/media_notification_container.h"
#include "components/media_message_center/media_notification_item.h"
#include "components/media_message_center/media_notification_util.h"
#include "components/media_message_center/media_notification_volume_slider_view.h"
#include "components/media_message_center/notification_theme.h"
#include "components/media_message_center/vector_icons/vector_icons.h"
#include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/gfx/canvas.h"
#include "ui/gfx/color_palette.h"
#include "ui/gfx/font.h"
#include "ui/gfx/font_list.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/message_center/public/cpp/message_center_constants.h"
#include "ui/views/animation/ink_drop.h"
#include "ui/views/border.h"
#include "ui/views/controls/button/image_button_factory.h"
#include "ui/views/controls/highlight_path_generator.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/style/typography.h"
#include "ui/views/view_class_properties.h"
namespace media_message_center {
using media_session::mojom::MediaSessionAction;
namespace {
constexpr gfx::Size kMediaNotificationViewBaseSize = {350, 168};
constexpr gfx::Size kArtworkSize = {72, 72};
constexpr gfx::Size kInfoContainerSize = {
kMediaNotificationViewBaseSize.width(), kArtworkSize.height()};
constexpr int kArtworkVignetteCornerRadius = 5;
constexpr gfx::Size kLabelsContainerBaseSize = {
kMediaNotificationViewBaseSize.width() - kArtworkSize.width(),
kInfoContainerSize.height()};
constexpr gfx::Size kPipButtonSize = {30, 20};
constexpr int kPipButtonIconSize = 16;
constexpr auto kNotificationControlsInsets = gfx::Insets::TLBR(8, 0, 0, 8);
constexpr gfx::Size kButtonsContainerSize = {
kMediaNotificationViewBaseSize.width(), 40};
constexpr gfx::Size kMediaControlsContainerSize = {
328 /* base width - dismissbutton size - margin*/, 32};
constexpr auto kMediaControlsContainerInsets = gfx::Insets::TLBR(0, 22, 0, 0);
constexpr int kMediaControlsButtonSpacing = 16;
constexpr auto kProgressBarInsets = gfx::Insets::TLBR(0, 16, 0, 16);
constexpr auto kInfoContainerInsets = gfx::Insets::TLBR(0, 15, 0, 16);
constexpr int kInfoContainerSpacing = 12;
constexpr gfx::Size kUtilButtonsContainerSize = {
kMediaNotificationViewBaseSize.width(), 39};
constexpr auto kUtilButtonsContainerInsets = gfx::Insets::TLBR(7, 16, 12, 16);
constexpr int kUtilButtonsSpacing = 8;
constexpr int kTitleArtistLineHeight = 20;
constexpr gfx::Size kMediaButtonSize = {24, 24};
constexpr gfx::Size kPlayPauseButtonSize = {32, 32};
constexpr int kMediaButtonIconSize = 14;
constexpr int kPlayPauseIconSize = 20;
constexpr gfx::Size kFaviconSize = {20, 20};
constexpr gfx::Size kVolumeSliderSize = {50, 20};
constexpr gfx::Size kMuteButtonSize = {20, 20};
constexpr int kMuteButtonIconSize = 16;
void RecordMetadataHistogram(
MediaNotificationViewModernImpl::Metadata metadata) {
UMA_HISTOGRAM_ENUMERATION(
MediaNotificationViewModernImpl::kMetadataHistogramName, metadata);
}
const gfx::VectorIcon* GetVectorIconForMediaAction(MediaSessionAction action) {
switch (action) {
case MediaSessionAction::kPreviousTrack:
return &kMediaPreviousTrackIcon;
case MediaSessionAction::kSeekBackward:
return &kMediaSeekBackwardIcon;
case MediaSessionAction::kPlay:
return &kPlayArrowIcon;
case MediaSessionAction::kPause:
return &kPauseIcon;
case MediaSessionAction::kSeekForward:
return &kMediaSeekForwardIcon;
case MediaSessionAction::kNextTrack:
return &kMediaNextTrackIcon;
case MediaSessionAction::kEnterPictureInPicture:
return &kMediaEnterPipIcon;
case MediaSessionAction::kExitPictureInPicture:
return &kMediaExitPipIcon;
case MediaSessionAction::kStop:
case MediaSessionAction::kSkipAd:
case MediaSessionAction::kSeekTo:
case MediaSessionAction::kScrubTo:
case MediaSessionAction::kSwitchAudioDevice:
case MediaSessionAction::kToggleMicrophone:
case MediaSessionAction::kToggleCamera:
case MediaSessionAction::kHangUp:
case MediaSessionAction::kRaise:
case MediaSessionAction::kSetMute:
NOTREACHED();
break;
}
return nullptr;
}
const std::u16string GetAccessibleNameForMediaAction(
MediaSessionAction action) {
switch (action) {
case MediaSessionAction::kPreviousTrack:
return l10n_util::GetStringUTF16(
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_PREVIOUS_TRACK);
case MediaSessionAction::kSeekBackward:
return l10n_util::GetStringUTF16(
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_SEEK_BACKWARD);
case MediaSessionAction::kPlay:
return l10n_util::GetStringUTF16(
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_PLAY);
case MediaSessionAction::kPause:
return l10n_util::GetStringUTF16(
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_PAUSE);
case MediaSessionAction::kSeekForward:
return l10n_util::GetStringUTF16(
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_SEEK_FORWARD);
case MediaSessionAction::kNextTrack:
return l10n_util::GetStringUTF16(
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_NEXT_TRACK);
case MediaSessionAction::kEnterPictureInPicture:
return l10n_util::GetStringUTF16(
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_ENTER_PIP);
case MediaSessionAction::kExitPictureInPicture:
return l10n_util::GetStringUTF16(
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_EXIT_PIP);
case MediaSessionAction::kStop:
case MediaSessionAction::kSkipAd:
case MediaSessionAction::kSeekTo:
case MediaSessionAction::kScrubTo:
case MediaSessionAction::kSwitchAudioDevice:
case MediaSessionAction::kToggleMicrophone:
case MediaSessionAction::kToggleCamera:
case MediaSessionAction::kHangUp:
case MediaSessionAction::kRaise:
case MediaSessionAction::kSetMute:
NOTREACHED();
break;
}
return std::u16string();
}
class MediaButton : public views::ImageButton {
public:
MediaButton(PressedCallback callback, int icon_size, gfx::Size button_size)
: ImageButton(callback), icon_size_(icon_size) {
SetHasInkDropActionOnClick(true);
views::InstallRoundRectHighlightPathGenerator(this, gfx::Insets(),
button_size.height() / 2);
views::InkDrop::Get(this)->SetMode(views::InkDropHost::InkDropMode::ON);
views::InkDrop::Get(this)->SetBaseColorCallback(base::BindRepeating(
&MediaButton::GetForegroundColor, base::Unretained(this)));
SetImageHorizontalAlignment(ImageButton::ALIGN_CENTER);
SetImageVerticalAlignment(ImageButton::ALIGN_MIDDLE);
SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
SetFlipCanvasOnPaintForRTLUI(false);
SetPreferredSize(button_size);
}
void SetButtonColor(SkColor foreground_color,
SkColor foreground_disabled_color) {
foreground_color_ = foreground_color;
foreground_disabled_color_ = foreground_disabled_color;
views::SetImageFromVectorIconWithColor(
this, *GetVectorIconForMediaAction(GetActionFromButtonTag(*this)),
icon_size_, foreground_color_, foreground_disabled_color_);
SchedulePaint();
}
void set_tag(int tag) {
views::ImageButton::set_tag(tag);
SetTooltipText(
GetAccessibleNameForMediaAction(GetActionFromButtonTag(*this)));
SetAccessibleName(
GetAccessibleNameForMediaAction(GetActionFromButtonTag(*this)));
views::SetImageFromVectorIconWithColor(
this, *GetVectorIconForMediaAction(GetActionFromButtonTag(*this)),
icon_size_, foreground_color_, foreground_disabled_color_);
}
private:
SkColor GetForegroundColor() { return foreground_color_; }
SkColor foreground_color_ = gfx::kPlaceholderColor;
SkColor foreground_disabled_color_ = gfx::kPlaceholderColor;
int icon_size_;
};
} // anonymous namespace
// static
const char MediaNotificationViewModernImpl::kArtworkHistogramName[] =
"Media.Notification.ArtworkPresent";
// static
const char MediaNotificationViewModernImpl::kMetadataHistogramName[] =
"Media.Notification.MetadataPresent";
MediaNotificationViewModernImpl::MediaNotificationViewModernImpl(
MediaNotificationContainer* container,
base::WeakPtr<MediaNotificationItem> item,
std::unique_ptr<views::View> notification_controls_view,
std::unique_ptr<views::View> notification_footer_view,
int notification_width,
absl::optional<NotificationTheme> theme)
: container_(container), item_(std::move(item)), theme_(theme) {
DCHECK(container_);
DCHECK(notification_controls_view);
SetPreferredSize(kMediaNotificationViewBaseSize);
DCHECK(notification_width >= kMediaNotificationViewBaseSize.width())
<< "MediaNotificationViewModernImpl expects a width of at least "
<< kMediaNotificationViewBaseSize.width();
auto border_insets = gfx::Insets::VH(
0, (kMediaNotificationViewBaseSize.width() - notification_width) / 2);
SetBorder(views::CreateEmptyBorder(border_insets));
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0));
bool is_cros = theme_.has_value();
if (is_cros) {
// We don't want the background to paint the artwork since we're painting it
// ourselves.
SetBackground(std::make_unique<MediaNotificationBackgroundAshImpl>(
/*paint_artwork=*/false));
} else {
// Force the artwork width to be zero since we're painting the artwork
// ourselves.
SetBackground(std::make_unique<MediaNotificationBackgroundImpl>(
message_center::kNotificationCornerRadius,
message_center::kNotificationCornerRadius,
/*artwork_max_width_pct=*/0));
}
UpdateCornerRadius(message_center::kNotificationCornerRadius,
message_center::kNotificationCornerRadius);
{
// The button container contains media_controls_container_ and
// notification_controls_view.
auto buttons_container = std::make_unique<views::View>();
buttons_container->SetPreferredSize(kButtonsContainerSize);
auto* buttons_container_layout =
buttons_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 0));
buttons_container_layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kStart);
// The media controls container contains buttons for media playback. This
// includes play/pause, fast-forward/rewind, and skip controls.
auto media_controls_container = std::make_unique<views::View>();
media_controls_container->SetPreferredSize(kMediaControlsContainerSize);
auto* media_controls_layout = media_controls_container->SetLayoutManager(
std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
kMediaControlsContainerInsets, kMediaControlsButtonSpacing));
media_controls_layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
media_controls_layout->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kCenter);
// Media controls should always be presented left-to-right,
// regardless of the local UI direction.
media_controls_container->SetMirrored(false);
CreateMediaButton(media_controls_container.get(),
MediaSessionAction::kPreviousTrack);
CreateMediaButton(media_controls_container.get(),
MediaSessionAction::kSeekBackward);
{
auto play_pause_button = std::make_unique<MediaButton>(
views::Button::PressedCallback(), kPlayPauseIconSize,
kPlayPauseButtonSize);
play_pause_button->SetCallback(
base::BindRepeating(&MediaNotificationViewModernImpl::ButtonPressed,
base::Unretained(this), play_pause_button.get()));
play_pause_button->set_tag(static_cast<int>(MediaSessionAction::kPlay));
play_pause_button_ =
media_controls_container->AddChildView(std::move(play_pause_button));
}
CreateMediaButton(media_controls_container.get(),
MediaSessionAction::kSeekForward);
CreateMediaButton(media_controls_container.get(),
MediaSessionAction::kNextTrack);
media_controls_container_ =
buttons_container->AddChildView(std::move(media_controls_container));
buttons_container_layout->SetFlexForView(media_controls_container_, 1);
notification_controls_view->SetProperty(views::kMarginsKey,
kNotificationControlsInsets);
buttons_container->AddChildView(std::move(notification_controls_view));
AddChildView(std::move(buttons_container));
}
auto progress_view = std::make_unique<MediaControlsProgressView>(
base::BindRepeating(&MediaNotificationViewModernImpl::SeekTo,
base::Unretained(this)),
true /* is_modern_notification */);
progress_view->SetProperty(views::kMarginsKey, kProgressBarInsets);
progress_ = AddChildView(std::move(progress_view));
progress_->SetVisible(true);
{
// The info container contains the notification artwork, the labels for the
// title and artist text, the picture in picture button, and the dismiss
// button.
auto info_container = std::make_unique<views::View>();
info_container->SetPreferredSize(kInfoContainerSize);
auto* info_container_layout =
info_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, kInfoContainerInsets,
kInfoContainerSpacing));
info_container_layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
{
auto artwork_container = std::make_unique<views::View>();
artwork_container->SetPreferredSize(kArtworkSize);
auto* artwork_container_layout = artwork_container->SetLayoutManager(
std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 0));
artwork_container_layout->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kCenter);
artwork_container_layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
{
auto artwork = std::make_unique<MediaArtworkView>(
kArtworkVignetteCornerRadius, kArtworkSize, kFaviconSize);
artwork_ = artwork_container->AddChildView(std::move(artwork));
}
artwork_container_ =
info_container->AddChildView(std::move(artwork_container));
}
{
auto labels_container = std::make_unique<views::View>();
labels_container->SetPreferredSize(
gfx::Size(kLabelsContainerBaseSize.width() -
kNotificationControlsInsets.width(),
kLabelsContainerBaseSize.height()));
auto* labels_container_layout_manager =
labels_container->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0));
labels_container_layout_manager->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kCenter);
labels_container_layout_manager->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kStart);
{
auto title_label = std::make_unique<views::Label>(
base::EmptyString16(), views::style::CONTEXT_LABEL,
views::style::STYLE_PRIMARY);
title_label->SetLineHeight(kTitleArtistLineHeight);
title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
title_label_ = labels_container->AddChildView(std::move(title_label));
}
{
auto subtitle_label = std::make_unique<views::Label>(
base::EmptyString16(), views::style::CONTEXT_LABEL,
views::style::STYLE_SECONDARY);
subtitle_label->SetLineHeight(18);
subtitle_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
subtitle_label_ =
labels_container->AddChildView(std::move(subtitle_label));
}
info_container->AddChildView(std::move(labels_container));
}
AddChildView(std::move(info_container));
}
{
// This view contains pip button, mute button and cast buttons.
auto util_buttons_container = std::make_unique<views::View>();
util_buttons_container->SetPreferredSize(kUtilButtonsContainerSize);
auto* util_buttons_layout = util_buttons_container->SetLayoutManager(
std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
kUtilButtonsContainerInsets, kUtilButtonsSpacing));
util_buttons_layout->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kStart);
util_buttons_layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kStretch);
if (item_->SourceType() != SourceType::kCast) {
// The picture-in-picture button appears directly under the media
// labels.
auto picture_in_picture_button = std::make_unique<MediaButton>(
views::Button::PressedCallback(), kPipButtonIconSize, kPipButtonSize);
picture_in_picture_button->SetCallback(base::BindRepeating(
&MediaNotificationViewModernImpl::ButtonPressed,
base::Unretained(this), picture_in_picture_button.get()));
picture_in_picture_button->set_tag(
static_cast<int>(MediaSessionAction::kEnterPictureInPicture));
picture_in_picture_button_ = util_buttons_container->AddChildView(
std::move(picture_in_picture_button));
}
if (notification_footer_view) {
auto* footer_view = util_buttons_container->AddChildView(
std::move(notification_footer_view));
util_buttons_layout->SetFlexForView(footer_view, 1);
}
if (item_->SourceType() == SourceType::kCast) {
auto volume_slider = std::make_unique<MediaNotificationVolumeSliderView>(
base::BindRepeating(&MediaNotificationViewModernImpl::SetVolume,
base::Unretained(this)));
volume_slider->SetPreferredSize(kVolumeSliderSize);
volume_slider_ =
util_buttons_container->AddChildView(std::move(volume_slider));
}
auto mute_button =
std::make_unique<views::ToggleImageButton>(base::BindRepeating(
&MediaNotificationViewModernImpl::OnMuteButtonClicked,
base::Unretained(this)));
mute_button->SetPreferredSize(kMuteButtonSize);
mute_button->SetImageHorizontalAlignment(
views::ImageButton::HorizontalAlignment::ALIGN_CENTER);
mute_button->SetImageVerticalAlignment(
views::ImageButton::VerticalAlignment::ALIGN_MIDDLE);
mute_button->SetTooltipText(l10n_util::GetStringUTF16(
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_MUTE));
mute_button->SetAccessibleName(l10n_util::GetStringUTF16(
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_MUTE));
mute_button_ = util_buttons_container->AddChildView(std::move(mute_button));
AddChildView(std::move(util_buttons_container));
}
if (item_)
item_->SetView(this);
}
MediaNotificationViewModernImpl::~MediaNotificationViewModernImpl() {
if (item_)
item_->SetView(nullptr);
}
void MediaNotificationViewModernImpl::UpdateCornerRadius(int top_radius,
int bottom_radius) {
if (GetMediaNotificationBackground()->UpdateCornerRadius(top_radius,
bottom_radius)) {
SchedulePaint();
}
}
void MediaNotificationViewModernImpl::GetAccessibleNodeData(
ui::AXNodeData* node_data) {
node_data->role = ax::mojom::Role::kListItem;
node_data->AddStringAttribute(
ax::mojom::StringAttribute::kRoleDescription,
l10n_util::GetStringUTF8(
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACCESSIBLE_NAME));
if (!accessible_name_.empty())
node_data->SetName(accessible_name_);
}
void MediaNotificationViewModernImpl::UpdateWithMediaSessionInfo(
const media_session::mojom::MediaSessionInfoPtr& session_info) {
bool playing =
session_info && session_info->playback_state ==
media_session::mojom::MediaPlaybackState::kPlaying;
MediaSessionAction action =
playing ? MediaSessionAction::kPause : MediaSessionAction::kPlay;
play_pause_button_->set_tag(static_cast<int>(action));
bool in_picture_in_picture =
session_info &&
session_info->picture_in_picture_state ==
media_session::mojom::MediaPictureInPictureState::kInPictureInPicture;
if (picture_in_picture_button_) {
action = in_picture_in_picture ? MediaSessionAction::kExitPictureInPicture
: MediaSessionAction::kEnterPictureInPicture;
picture_in_picture_button_->set_tag(static_cast<int>(action));
}
UpdateActionButtonsVisibility();
container_->OnMediaSessionInfoChanged(session_info);
PreferredSizeChanged();
Layout();
SchedulePaint();
}
void MediaNotificationViewModernImpl::UpdateWithMediaMetadata(
const media_session::MediaMetadata& metadata) {
title_label_->SetText(metadata.title);
subtitle_label_->SetText(metadata.source_title);
accessible_name_ = GetAccessibleNameFromMetadata(metadata);
// The title label should only be a11y-focusable when there is text to be
// read.
if (metadata.title.empty()) {
title_label_->SetFocusBehavior(FocusBehavior::NEVER);
} else {
title_label_->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
RecordMetadataHistogram(Metadata::kTitle);
}
// The subtitle label should only be a11y-focusable when there is text to be
// read.
if (metadata.source_title.empty()) {
subtitle_label_->SetFocusBehavior(FocusBehavior::NEVER);
} else {
subtitle_label_->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
RecordMetadataHistogram(Metadata::kSource);
}
RecordMetadataHistogram(Metadata::kCount);
container_->OnMediaSessionMetadataChanged(metadata);
PreferredSizeChanged();
Layout();
SchedulePaint();
}
void MediaNotificationViewModernImpl::UpdateWithMediaActions(
const base::flat_set<media_session::mojom::MediaSessionAction>& actions) {
enabled_actions_ = actions;
UpdateActionButtonsVisibility();
PreferredSizeChanged();
Layout();
SchedulePaint();
}
void MediaNotificationViewModernImpl::UpdateWithMediaPosition(
const media_session::MediaPosition& position) {
position_ = position;
progress_->UpdateProgress(position);
}
void MediaNotificationViewModernImpl::UpdateWithMediaArtwork(
const gfx::ImageSkia& image) {
GetMediaNotificationBackground()->UpdateArtwork(image);
UMA_HISTOGRAM_BOOLEAN(kArtworkHistogramName, !image.isNull());
artwork_->SetImage(image);
artwork_->SetPreferredSize(kArtworkSize);
UpdateForegroundColor();
container_->OnMediaArtworkChanged(image);
PreferredSizeChanged();
Layout();
SchedulePaint();
}
void MediaNotificationViewModernImpl::UpdateWithFavicon(
const gfx::ImageSkia& icon) {
GetMediaNotificationBackground()->UpdateFavicon(icon);
artwork_->SetFavicon(icon);
artwork_->SetPreferredSize(kArtworkSize);
UpdateForegroundColor();
SchedulePaint();
}
void MediaNotificationViewModernImpl::OnThemeChanged() {
MediaNotificationView::OnThemeChanged();
UpdateForegroundColor();
}
void MediaNotificationViewModernImpl::UpdateDeviceSelectorAvailability(
bool availability) {
GetMediaNotificationBackground()->UpdateDeviceSelectorAvailability(
availability);
}
void MediaNotificationViewModernImpl::UpdateWithMuteStatus(bool mute) {
if (mute_button_) {
mute_button_->SetToggled(mute);
const auto mute_button_description_id =
mute ? IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_UNMUTE
: IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_MUTE;
mute_button_->SetTooltipText(
l10n_util::GetStringUTF16(mute_button_description_id));
mute_button_->SetAccessibleName(
l10n_util::GetStringUTF16(mute_button_description_id));
}
if (volume_slider_)
volume_slider_->SetMute(mute);
}
void MediaNotificationViewModernImpl::UpdateWithVolume(float volume) {
if (volume_slider_)
volume_slider_->SetVolume(volume);
}
void MediaNotificationViewModernImpl::UpdateActionButtonsVisibility() {
for (auto* view : media_controls_container_->children()) {
views::Button* action_button = views::Button::AsButton(view);
bool should_show = base::Contains(enabled_actions_,
GetActionFromButtonTag(*action_button));
bool should_invalidate = should_show != action_button->GetVisible();
action_button->SetVisible(should_show);
if (should_invalidate)
action_button->InvalidateLayout();
}
if (picture_in_picture_button_) {
const bool should_show_pip =
base::ranges::any_of(enabled_actions_, [](MediaSessionAction action) {
return action == MediaSessionAction::kEnterPictureInPicture ||
action == MediaSessionAction::kExitPictureInPicture;
});
if (picture_in_picture_button_->GetVisible() != should_show_pip) {
picture_in_picture_button_->SetVisible(should_show_pip);
picture_in_picture_button_->InvalidateLayout();
}
}
container_->OnVisibleActionsChanged(enabled_actions_);
}
void MediaNotificationViewModernImpl::CreateMediaButton(
views::View* parent_view,
MediaSessionAction action) {
auto button = std::make_unique<MediaButton>(
views::Button::PressedCallback(), kMediaButtonIconSize, kMediaButtonSize);
button->SetCallback(
base::BindRepeating(&MediaNotificationViewModernImpl::ButtonPressed,
base::Unretained(this), button.get()));
button->set_tag(static_cast<int>(action));
parent_view->AddChildView(std::move(button));
}
MediaNotificationBackground*
MediaNotificationViewModernImpl::GetMediaNotificationBackground() {
return static_cast<MediaNotificationBackground*>(background());
}
void MediaNotificationViewModernImpl::UpdateForegroundColor() {
if (!GetWidget())
return;
const SkColor background =
GetMediaNotificationBackground()->GetBackgroundColor(*this);
const SkColor foreground =
GetMediaNotificationBackground()->GetForegroundColor(*this);
const SkColor disabled_icon_color =
SkColorSetA(foreground, gfx::kDisabledControlAlpha);
NotificationTheme theme;
if (theme_.has_value()) {
theme = *theme_;
} else {
theme.primary_text_color = foreground;
theme.secondary_text_color = foreground;
theme.enabled_icon_color = foreground;
theme.disabled_icon_color = disabled_icon_color;
theme.separator_color = SkColorSetA(foreground, 0x1F);
}
artwork_->SetBackgroundColor(theme.disabled_icon_color);
artwork_->SetVignetteColor(background);
progress_->SetForegroundColor(theme.primary_text_color);
progress_->SetBackgroundColor(theme.disabled_icon_color);
progress_->SetTextColor(theme.primary_text_color);
if (volume_slider_)
volume_slider_->UpdateColor(theme.primary_text_color,
theme.disabled_icon_color);
if (mute_button_) {
views::SetImageFromVectorIconWithColor(
mute_button_, vector_icons::kVolumeUpIcon, kMuteButtonIconSize,
theme.enabled_icon_color, theme.disabled_icon_color);
views::SetToggledImageFromVectorIconWithColor(
mute_button_, vector_icons::kVolumeOffIcon, kMediaButtonIconSize,
theme.enabled_icon_color, theme.disabled_icon_color);
}
// Update the colors for the labels
title_label_->SetEnabledColor(theme.primary_text_color);
subtitle_label_->SetEnabledColor(theme.secondary_text_color);
title_label_->SetBackgroundColor(background);
subtitle_label_->SetBackgroundColor(background);
// Update the colors for the toggle buttons (play/pause and
// picture-in-picture)
play_pause_button_->SetButtonColor(theme.enabled_icon_color,
theme.disabled_icon_color);
if (picture_in_picture_button_)
picture_in_picture_button_->SetButtonColor(theme.enabled_icon_color,
theme.disabled_icon_color);
// Update the colors for the media control buttons.
for (views::View* child : media_controls_container_->children()) {
// Skip the play pause button since it is a special case.
if (child == play_pause_button_)
continue;
MediaButton* button = static_cast<MediaButton*>(child);
button->SetButtonColor(theme.enabled_icon_color, theme.disabled_icon_color);
}
SchedulePaint();
container_->OnColorsChanged(theme.enabled_icon_color,
theme.disabled_icon_color, background);
}
void MediaNotificationViewModernImpl::ButtonPressed(views::Button* button) {
if (item_)
item_->OnMediaSessionActionButtonPressed(GetActionFromButtonTag(*button));
}
void MediaNotificationViewModernImpl::SeekTo(double seek_progress) {
item_->SeekTo(seek_progress * position_.duration());
}
void MediaNotificationViewModernImpl::OnMuteButtonClicked() {
item_->SetMute(!mute_button_->GetToggled());
}
void MediaNotificationViewModernImpl::SetVolume(float volume) {
item_->SetVolume(volume);
item_->SetMute(volume == 0);
}
views::Button*
MediaNotificationViewModernImpl::picture_in_picture_button_for_testing() const {
return picture_in_picture_button_;
}
BEGIN_METADATA(MediaNotificationViewModernImpl, views::View)
END_METADATA
} // namespace media_message_center