GMC: Implement redesigned UI
This change implements a newer version of the global media controls UI based on updated UI mocks. Bug: 1124401 Change-Id: I0827b33ec97697ead88e7db4dd8b5248668bb236 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2399619 Commit-Queue: Noah Rose Ledesma <noahrose@google.com> Reviewed-by: Tommy Steimel <steimel@chromium.org> Cr-Commit-Position: refs/heads/master@{#805993}
This commit is contained in:

committed by
Commit Bot

parent
84436a2b17
commit
a9b3e907ce
chrome/browser/ui/views/global_media_controls
components/media_message_center
@ -5,6 +5,7 @@
|
||||
#include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl_view.h"
|
||||
|
||||
#include "base/feature_list.h"
|
||||
#include "base/metrics/field_trial_params.h"
|
||||
#include "chrome/browser/ui/global_media_controls/media_notification_container_impl.h"
|
||||
#include "chrome/browser/ui/global_media_controls/media_notification_container_observer.h"
|
||||
#include "chrome/browser/ui/global_media_controls/media_notification_service.h"
|
||||
@ -31,10 +32,10 @@ namespace {
|
||||
|
||||
// TODO(steimel): We need to decide on the correct values here.
|
||||
constexpr int kWidth = 400;
|
||||
// TODO(noahrose): Should these sizes include the height of the audio
|
||||
// device selector view?
|
||||
constexpr int kModernUIWidth = 350;
|
||||
constexpr gfx::Size kNormalSize = gfx::Size(kWidth, 100);
|
||||
constexpr gfx::Size kExpandedSize = gfx::Size(kWidth, 150);
|
||||
constexpr gfx::Size kModernUISize = gfx::Size(kModernUIWidth, 100);
|
||||
constexpr gfx::Size kDismissButtonSize = gfx::Size(30, 30);
|
||||
constexpr int kDismissButtonIconSize = 20;
|
||||
constexpr int kDismissButtonBackgroundRadius = 15;
|
||||
@ -118,12 +119,16 @@ MediaNotificationContainerImplView::MediaNotificationContainerImplView(
|
||||
|
||||
std::unique_ptr<media_message_center::MediaNotificationView> view;
|
||||
if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsModernUI)) {
|
||||
view = std::make_unique<
|
||||
media_message_center::MediaNotificationViewModernImpl>();
|
||||
view =
|
||||
std::make_unique<media_message_center::MediaNotificationViewModernImpl>(
|
||||
this, std::move(item), std::move(dismiss_button_placeholder),
|
||||
kModernUIWidth);
|
||||
SetPreferredSize(kModernUISize);
|
||||
} else {
|
||||
view = std::make_unique<media_message_center::MediaNotificationViewImpl>(
|
||||
this, std::move(item), std::move(dismiss_button_placeholder),
|
||||
base::string16(), kWidth, /*should_show_icon=*/false);
|
||||
SetPreferredSize(kNormalSize);
|
||||
}
|
||||
view_ = swipeable_container_->AddChildView(std::move(view));
|
||||
|
||||
@ -496,7 +501,12 @@ bool MediaNotificationContainerImplView::ShouldHandleMouseEvent(
|
||||
}
|
||||
|
||||
void MediaNotificationContainerImplView::OnSizeChanged() {
|
||||
gfx::Size new_size = is_expanded_ ? kExpandedSize : kNormalSize;
|
||||
gfx::Size new_size;
|
||||
if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsModernUI)) {
|
||||
new_size = kModernUISize;
|
||||
} else {
|
||||
new_size = is_expanded_ ? kExpandedSize : kNormalSize;
|
||||
}
|
||||
|
||||
// |new_size| does not contain the height for the audio device selector view.
|
||||
// If this view is present, we should query it for its preferred height and
|
||||
|
@ -49,6 +49,7 @@ source_set("unit_tests") {
|
||||
"media_controls_progress_view_unittest.cc",
|
||||
"media_notification_background_unittest.cc",
|
||||
"media_notification_view_impl_unittest.cc",
|
||||
"media_notification_view_modern_impl_unittest.cc",
|
||||
]
|
||||
|
||||
deps = [
|
||||
|
@ -49,6 +49,7 @@ class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaNotificationBackground
|
||||
private:
|
||||
friend class MediaNotificationBackgroundTest;
|
||||
friend class MediaNotificationViewImplTest;
|
||||
friend class MediaNotificationViewModernImplTest;
|
||||
FRIEND_TEST_ALL_PREFIXES(MediaNotificationBackgroundRTLTest,
|
||||
BoundsSanityCheck);
|
||||
|
||||
|
@ -3,3 +3,593 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "components/media_message_center/media_notification_view_modern_impl.h"
|
||||
|
||||
#include "base/metrics/histogram_macros.h"
|
||||
#include "base/stl_util.h"
|
||||
#include "components/media_message_center/media_notification_background.h"
|
||||
#include "components/media_message_center/media_notification_constants.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/vector_icons/vector_icons.h"
|
||||
#include "components/strings/grit/components_strings.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/gfx/canvas.h"
|
||||
#include "ui/gfx/font.h"
|
||||
#include "ui/gfx/font_list.h"
|
||||
#include "ui/gfx/paint_vector_icon.h"
|
||||
#include "ui/message_center/public/cpp/message_center_constants.h"
|
||||
#include "ui/views/border.h"
|
||||
#include "ui/views/controls/button/image_button_factory.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, 175};
|
||||
constexpr gfx::Size kInfoContainerSize = {
|
||||
kMediaNotificationViewBaseSize.width(), 100};
|
||||
constexpr gfx::Insets kArtworkContainerBorderInsets = {15, 10, 15, 0};
|
||||
constexpr gfx::Size kArtworkContainerSize = {100, kInfoContainerSize.height()};
|
||||
constexpr int kArtworkVignetteCornerRadius = 5;
|
||||
constexpr gfx::Size kLabelsContainerBaseSize = {
|
||||
kMediaNotificationViewBaseSize.width() - kArtworkContainerSize.width(),
|
||||
kInfoContainerSize.height()};
|
||||
constexpr gfx::Insets kLabelsContainerBorderInsets = {15, 10, 0, 0};
|
||||
constexpr gfx::Size kPipSeparatorSize = {1, 14};
|
||||
constexpr gfx::Size kPipButtonSize = {20, 20};
|
||||
constexpr int kPipButtonIconSize = 18;
|
||||
constexpr gfx::Size kNotificationControlsSpacerSize = {100, 1};
|
||||
constexpr gfx::Insets kNotificationControlsInsets = {5, 0, 0, 5};
|
||||
constexpr gfx::Size kMediaControlsContainerSize = {350, 75};
|
||||
constexpr int kMediaControlsButtonSpacing = 16;
|
||||
constexpr gfx::Insets kMediaControlsBorderInsets = {0, 0, 10, 0};
|
||||
|
||||
constexpr int kTitleArtistLineHeight = 20;
|
||||
constexpr gfx::Size kMediaButtonSize = gfx::Size(36, 36);
|
||||
constexpr int kMediaButtonIconSize = 20;
|
||||
|
||||
// An image view with a rounded rectangle vignette
|
||||
class MediaArtworkView : public views::ImageView {
|
||||
public:
|
||||
explicit MediaArtworkView(float corner_radius)
|
||||
: corner_radius_(corner_radius) {}
|
||||
|
||||
void SetVignetteColor(const SkColor& vignette_color) {
|
||||
vignette_color_ = vignette_color;
|
||||
}
|
||||
|
||||
// ImageView
|
||||
void OnPaint(gfx::Canvas* canvas) override;
|
||||
|
||||
private:
|
||||
SkColor vignette_color_;
|
||||
float corner_radius_;
|
||||
};
|
||||
|
||||
void MediaArtworkView::OnPaint(gfx::Canvas* canvas) {
|
||||
views::ImageView::OnPaint(canvas);
|
||||
auto path = SkPath().addRoundRect(RectToSkRect(GetLocalBounds()),
|
||||
corner_radius_, corner_radius_);
|
||||
path.toggleInverseFillType();
|
||||
cc::PaintFlags paint_flags;
|
||||
paint_flags.setStyle(cc::PaintFlags::kFill_Style);
|
||||
paint_flags.setAntiAlias(true);
|
||||
paint_flags.setColor(vignette_color_);
|
||||
canvas->DrawPath(path, paint_flags);
|
||||
}
|
||||
|
||||
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:
|
||||
NOTREACHED();
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
} // 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,
|
||||
int notification_width)
|
||||
: container_(container), item_(std::move(item)) {
|
||||
DCHECK(container_);
|
||||
|
||||
SetPreferredSize(kMediaNotificationViewBaseSize);
|
||||
|
||||
DCHECK(notification_width >= kMediaNotificationViewBaseSize.width())
|
||||
<< "MediaNotificationViewModernImpl expects a width of at least "
|
||||
<< kMediaNotificationViewBaseSize.width();
|
||||
auto border_insets = gfx::Insets(
|
||||
0, (kMediaNotificationViewBaseSize.width() - notification_width) / 2);
|
||||
SetBorder(views::CreateEmptyBorder(border_insets));
|
||||
|
||||
SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kVertical, gfx::Insets(), 0));
|
||||
|
||||
SetBackground(std::make_unique<MediaNotificationBackground>(
|
||||
message_center::kNotificationCornerRadius,
|
||||
message_center::kNotificationCornerRadius, 0));
|
||||
|
||||
UpdateCornerRadius(message_center::kNotificationCornerRadius,
|
||||
message_center::kNotificationCornerRadius);
|
||||
|
||||
{
|
||||
// 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, gfx::Insets(), 0));
|
||||
info_container_layout->set_cross_axis_alignment(
|
||||
views::BoxLayout::CrossAxisAlignment::kStart);
|
||||
|
||||
{
|
||||
auto artwork_container = std::make_unique<views::View>();
|
||||
artwork_container->SetBorder(
|
||||
views::CreateEmptyBorder(kArtworkContainerBorderInsets));
|
||||
artwork_container->SetPreferredSize(kArtworkContainerSize);
|
||||
|
||||
// The artwork container will become visible once artwork has been set in
|
||||
// UpdateWithMediaArtwork
|
||||
artwork_container->SetVisible(false);
|
||||
|
||||
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);
|
||||
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(
|
||||
{kLabelsContainerBaseSize.width() -
|
||||
(notification_controls_view->GetPreferredSize().width() +
|
||||
kNotificationControlsInsets.width()),
|
||||
kLabelsContainerBaseSize.height()});
|
||||
labels_container->SetBorder(
|
||||
views::CreateEmptyBorder(kLabelsContainerBorderInsets));
|
||||
|
||||
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::kStart);
|
||||
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));
|
||||
}
|
||||
|
||||
{
|
||||
// Put a vertical spacer between the labels and the pip button.
|
||||
auto spacer = std::make_unique<views::View>();
|
||||
spacer->SetPreferredSize(kPipSeparatorSize);
|
||||
labels_container->AddChildView(std::move(spacer));
|
||||
}
|
||||
|
||||
{
|
||||
// The picture-in-picture button appears directly under the media
|
||||
// labels.
|
||||
auto picture_in_picture_button =
|
||||
views::CreateVectorToggleImageButton(this);
|
||||
picture_in_picture_button->set_tag(
|
||||
static_cast<int>(MediaSessionAction::kEnterPictureInPicture));
|
||||
picture_in_picture_button->SetPreferredSize(kPipButtonSize);
|
||||
picture_in_picture_button->SetFocusBehavior(
|
||||
views::View::FocusBehavior::ALWAYS);
|
||||
picture_in_picture_button->SetTooltipText(l10n_util::GetStringUTF16(
|
||||
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_ENTER_PIP));
|
||||
picture_in_picture_button->SetToggledTooltipText(
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_EXIT_PIP));
|
||||
picture_in_picture_button->EnableCanvasFlippingForRTLUI(false);
|
||||
views::SetImageFromVectorIconWithColor(
|
||||
picture_in_picture_button.get(),
|
||||
*GetVectorIconForMediaAction(
|
||||
MediaSessionAction::kEnterPictureInPicture),
|
||||
kPipButtonIconSize, SK_ColorBLACK);
|
||||
picture_in_picture_button_ = labels_container->AddChildView(
|
||||
std::move(picture_in_picture_button));
|
||||
}
|
||||
|
||||
info_container->AddChildView(std::move(labels_container));
|
||||
}
|
||||
|
||||
{
|
||||
// If there is no artwork to display, a vertical spacer should be added
|
||||
// between the labels container and the dismiss button.
|
||||
auto notification_controls_spacer = std::make_unique<views::View>();
|
||||
notification_controls_spacer->SetPreferredSize(
|
||||
kNotificationControlsSpacerSize);
|
||||
notification_controls_spacer_ =
|
||||
info_container->AddChildView(std::move(notification_controls_spacer));
|
||||
}
|
||||
|
||||
notification_controls_view->SetProperty(views::kMarginsKey,
|
||||
kNotificationControlsInsets);
|
||||
info_container->AddChildView(std::move(notification_controls_view));
|
||||
|
||||
AddChildView(std::move(info_container));
|
||||
}
|
||||
|
||||
{
|
||||
// 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, gfx::Insets(),
|
||||
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_container->SetBorder(
|
||||
views::CreateEmptyBorder(kMediaControlsBorderInsets));
|
||||
|
||||
// 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,
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_PREVIOUS_TRACK));
|
||||
CreateMediaButton(
|
||||
media_controls_container.get(), MediaSessionAction::kSeekBackward,
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_SEEK_BACKWARD));
|
||||
|
||||
{
|
||||
auto play_pause_button = views::CreateVectorToggleImageButton(this);
|
||||
play_pause_button->set_tag(static_cast<int>(MediaSessionAction::kPlay));
|
||||
play_pause_button->SetPreferredSize(kMediaButtonSize);
|
||||
play_pause_button->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
|
||||
play_pause_button->SetTooltipText(l10n_util::GetStringUTF16(
|
||||
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_PLAY));
|
||||
play_pause_button->SetToggledTooltipText(l10n_util::GetStringUTF16(
|
||||
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_PAUSE));
|
||||
play_pause_button->EnableCanvasFlippingForRTLUI(false);
|
||||
play_pause_button_ =
|
||||
media_controls_container->AddChildView(std::move(play_pause_button));
|
||||
}
|
||||
|
||||
CreateMediaButton(
|
||||
media_controls_container.get(), MediaSessionAction::kSeekForward,
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_SEEK_FORWARD));
|
||||
CreateMediaButton(
|
||||
media_controls_container.get(), MediaSessionAction::kNextTrack,
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_NEXT_TRACK));
|
||||
|
||||
media_controls_container_ =
|
||||
AddChildView(std::move(media_controls_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::ButtonPressed(views::Button* sender,
|
||||
const ui::Event& event) {
|
||||
if (item_) {
|
||||
item_->OnMediaSessionActionButtonPressed(GetActionFromButtonTag(*sender));
|
||||
}
|
||||
}
|
||||
|
||||
void MediaNotificationViewModernImpl::UpdateWithMediaSessionInfo(
|
||||
const media_session::mojom::MediaSessionInfoPtr& session_info) {
|
||||
bool playing =
|
||||
session_info && session_info->playback_state ==
|
||||
media_session::mojom::MediaPlaybackState::kPlaying;
|
||||
play_pause_button_->SetToggled(playing);
|
||||
|
||||
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;
|
||||
picture_in_picture_button_->SetToggled(in_picture_in_picture);
|
||||
|
||||
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::UpdateWithMediaArtwork(
|
||||
const gfx::ImageSkia& image) {
|
||||
GetMediaNotificationBackground()->UpdateArtwork(image);
|
||||
|
||||
UMA_HISTOGRAM_BOOLEAN(kArtworkHistogramName, !image.isNull());
|
||||
|
||||
if (!image.isNull()) {
|
||||
artwork_container_->SetVisible(true);
|
||||
// When there is artwork to display, this spacer is no logner needed
|
||||
notification_controls_spacer_->SetVisible(false);
|
||||
}
|
||||
|
||||
artwork_->SetImage(image);
|
||||
artwork_->SetPreferredSize({70, 70});
|
||||
artwork_->SetVignetteColor(
|
||||
GetMediaNotificationBackground()->GetBackgroundColor(*this));
|
||||
|
||||
UpdateForegroundColor();
|
||||
|
||||
container_->OnMediaArtworkChanged(image);
|
||||
|
||||
PreferredSizeChanged();
|
||||
Layout();
|
||||
SchedulePaint();
|
||||
}
|
||||
|
||||
void MediaNotificationViewModernImpl::UpdateWithFavicon(
|
||||
const gfx::ImageSkia& icon) {
|
||||
GetMediaNotificationBackground()->UpdateFavicon(icon);
|
||||
|
||||
UpdateForegroundColor();
|
||||
SchedulePaint();
|
||||
}
|
||||
|
||||
void MediaNotificationViewModernImpl::OnThemeChanged() {
|
||||
MediaNotificationView::OnThemeChanged();
|
||||
UpdateForegroundColor();
|
||||
}
|
||||
|
||||
void MediaNotificationViewModernImpl::UpdateDeviceSelectorAvailability(
|
||||
bool availability) {
|
||||
GetMediaNotificationBackground()->UpdateDeviceSelectorAvailability(
|
||||
availability);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
container_->OnVisibleActionsChanged(enabled_actions_);
|
||||
}
|
||||
|
||||
void MediaNotificationViewModernImpl::CreateMediaButton(
|
||||
views::View* parent_view,
|
||||
MediaSessionAction action,
|
||||
const base::string16& accessible_name) {
|
||||
auto button = views::CreateVectorImageButton(this);
|
||||
button->set_tag(static_cast<int>(action));
|
||||
button->SetPreferredSize(kMediaButtonSize);
|
||||
button->SetAccessibleName(accessible_name);
|
||||
button->SetTooltipText(accessible_name);
|
||||
button->SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
|
||||
button->EnableCanvasFlippingForRTLUI(false);
|
||||
parent_view->AddChildView(std::move(button));
|
||||
}
|
||||
|
||||
MediaNotificationBackground*
|
||||
MediaNotificationViewModernImpl::GetMediaNotificationBackground() {
|
||||
return static_cast<MediaNotificationBackground*>(background());
|
||||
}
|
||||
|
||||
void MediaNotificationViewModernImpl::UpdateForegroundColor() {
|
||||
const SkColor background =
|
||||
GetMediaNotificationBackground()->GetBackgroundColor(*this);
|
||||
const SkColor foreground =
|
||||
GetMediaNotificationBackground()->GetForegroundColor(*this);
|
||||
const SkColor disabled_icon_color =
|
||||
SkColorSetA(foreground, gfx::kDisabledControlAlpha);
|
||||
|
||||
// Update the colors for the labels
|
||||
title_label_->SetEnabledColor(foreground);
|
||||
subtitle_label_->SetEnabledColor(disabled_icon_color);
|
||||
|
||||
title_label_->SetBackgroundColor(background);
|
||||
subtitle_label_->SetBackgroundColor(background);
|
||||
|
||||
// Update the colors for the toggle buttons (play/pause and
|
||||
// picture-in-picture)
|
||||
views::SetImageFromVectorIconWithColor(
|
||||
play_pause_button_,
|
||||
*GetVectorIconForMediaAction(MediaSessionAction::kPlay),
|
||||
kMediaButtonIconSize, foreground);
|
||||
views::SetToggledImageFromVectorIconWithColor(
|
||||
play_pause_button_,
|
||||
*GetVectorIconForMediaAction(MediaSessionAction::kPause),
|
||||
kMediaButtonIconSize, foreground, disabled_icon_color);
|
||||
|
||||
views::SetImageFromVectorIconWithColor(
|
||||
picture_in_picture_button_,
|
||||
*GetVectorIconForMediaAction(MediaSessionAction::kEnterPictureInPicture),
|
||||
kPipButtonIconSize, foreground);
|
||||
views::SetToggledImageFromVectorIconWithColor(
|
||||
picture_in_picture_button_,
|
||||
*GetVectorIconForMediaAction(MediaSessionAction::kExitPictureInPicture),
|
||||
kPipButtonIconSize, foreground, 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;
|
||||
|
||||
views::ImageButton* button = static_cast<views::ImageButton*>(child);
|
||||
|
||||
views::SetImageFromVectorIconWithColor(
|
||||
button, *GetVectorIconForMediaAction(GetActionFromButtonTag(*button)),
|
||||
kMediaButtonIconSize, foreground);
|
||||
|
||||
button->SchedulePaint();
|
||||
}
|
||||
|
||||
SchedulePaint();
|
||||
container_->OnColorsChanged(foreground, background);
|
||||
}
|
||||
|
||||
} // namespace media_message_center
|
||||
|
@ -7,27 +7,146 @@
|
||||
|
||||
#include "components/media_message_center/media_notification_view.h"
|
||||
|
||||
#include "base/component_export.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/optional.h"
|
||||
#include "components/media_message_center/media_notification_view.h"
|
||||
#include "services/media_session/public/mojom/media_session.mojom.h"
|
||||
#include "ui/views/controls/button/button.h"
|
||||
#include "ui/views/controls/button/image_button.h"
|
||||
#include "ui/views/controls/image_view.h"
|
||||
#include "ui/views/controls/label.h"
|
||||
|
||||
namespace views {
|
||||
class ToggleImageButton;
|
||||
} // namespace views
|
||||
|
||||
namespace media_message_center {
|
||||
|
||||
namespace {
|
||||
class MediaArtworkView;
|
||||
} // anonymous namespace
|
||||
|
||||
class MediaNotificationBackground;
|
||||
class MediaNotificationContainer;
|
||||
class MediaNotificationItem;
|
||||
|
||||
class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaNotificationViewModernImpl
|
||||
: public MediaNotificationView {
|
||||
: public MediaNotificationView,
|
||||
public views::ButtonListener {
|
||||
public:
|
||||
MediaNotificationViewModernImpl() = default;
|
||||
// The name of the histogram used when recording whether the artwork was
|
||||
// present.
|
||||
static const char kArtworkHistogramName[];
|
||||
|
||||
// The name of the histogram used when recording the type of metadata that was
|
||||
// displayed.
|
||||
static const char kMetadataHistogramName[];
|
||||
|
||||
// The type of metadata that was displayed. This is used in metrics so new
|
||||
// values must only be added to the end.
|
||||
enum class Metadata {
|
||||
kTitle,
|
||||
kArtist,
|
||||
kAlbum,
|
||||
kCount,
|
||||
kSource,
|
||||
kMaxValue = kSource,
|
||||
};
|
||||
|
||||
MediaNotificationViewModernImpl(
|
||||
MediaNotificationContainer* container,
|
||||
base::WeakPtr<MediaNotificationItem> item,
|
||||
std::unique_ptr<views::View> notification_controls_view,
|
||||
int notification_width);
|
||||
MediaNotificationViewModernImpl(const MediaNotificationViewModernImpl&) =
|
||||
delete;
|
||||
MediaNotificationViewModernImpl& operator=(
|
||||
const MediaNotificationViewModernImpl&) = delete;
|
||||
~MediaNotificationViewModernImpl() override;
|
||||
|
||||
// views::View:
|
||||
void GetAccessibleNodeData(ui::AXNodeData* node_data) override;
|
||||
void OnThemeChanged() override;
|
||||
|
||||
// views::ButtonListener:
|
||||
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
|
||||
|
||||
// MediaNotificationView
|
||||
void SetForcedExpandedState(bool* forced_expanded_state) override {}
|
||||
void SetExpanded(bool expanded) override {}
|
||||
void UpdateCornerRadius(int top_radius, int bottom_radius) override {}
|
||||
void UpdateCornerRadius(int top_radius, int bottom_radius) override;
|
||||
void UpdateWithMediaSessionInfo(
|
||||
const media_session::mojom::MediaSessionInfoPtr& session_info) override {}
|
||||
const media_session::mojom::MediaSessionInfoPtr& session_info) override;
|
||||
void UpdateWithMediaMetadata(
|
||||
const media_session::MediaMetadata& metadata) override {}
|
||||
const media_session::MediaMetadata& metadata) override;
|
||||
void UpdateWithMediaActions(
|
||||
const base::flat_set<media_session::mojom::MediaSessionAction>& actions)
|
||||
override {}
|
||||
void UpdateWithMediaArtwork(const gfx::ImageSkia& image) override {}
|
||||
void UpdateWithFavicon(const gfx::ImageSkia& icon) override {}
|
||||
override;
|
||||
void UpdateWithMediaArtwork(const gfx::ImageSkia& image) override;
|
||||
void UpdateWithFavicon(const gfx::ImageSkia& icon) override;
|
||||
void UpdateWithVectorIcon(const gfx::VectorIcon& vector_icon) override {}
|
||||
void UpdateDeviceSelectorAvailability(bool availability) override {}
|
||||
void UpdateDeviceSelectorAvailability(bool availability) override;
|
||||
|
||||
// Testing methods
|
||||
const views::Label* title_label_for_testing() const { return title_label_; }
|
||||
|
||||
const views::Label* subtitle_label_for_testing() const {
|
||||
return subtitle_label_;
|
||||
}
|
||||
|
||||
const views::Button* picture_in_picture_button_for_testing() const {
|
||||
return picture_in_picture_button_;
|
||||
}
|
||||
|
||||
const views::View* media_controls_container_for_testing() const {
|
||||
return media_controls_container_;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class MediaNotificationViewModernImplTest;
|
||||
|
||||
// Creates an image button with an icon that matches |action| and adds it
|
||||
// to |parent_view|. When clicked it will trigger |action| on the session.
|
||||
// |accessible_name| is the text used for screen readers and the
|
||||
// button's tooltip.
|
||||
void CreateMediaButton(views::View* parent_view,
|
||||
media_session::mojom::MediaSessionAction action,
|
||||
const base::string16& accessible_name);
|
||||
|
||||
void UpdateActionButtonsVisibility();
|
||||
|
||||
MediaNotificationBackground* GetMediaNotificationBackground();
|
||||
|
||||
void UpdateForegroundColor();
|
||||
|
||||
// Container that receives events.
|
||||
MediaNotificationContainer* const container_;
|
||||
|
||||
// Keeps track of media metadata and controls the session when buttons are
|
||||
// clicked.
|
||||
base::WeakPtr<MediaNotificationItem> item_;
|
||||
|
||||
bool has_artwork_ = false;
|
||||
|
||||
// Set of enabled actions.
|
||||
base::flat_set<media_session::mojom::MediaSessionAction> enabled_actions_;
|
||||
|
||||
// Stores the text to be read by screen readers describing the notification.
|
||||
// Contains the title, artist and album separated by hyphens.
|
||||
base::string16 accessible_name_;
|
||||
|
||||
MediaNotificationBackground* background_;
|
||||
|
||||
// Container views directly attached to this view.
|
||||
views::View* artwork_container_ = nullptr;
|
||||
MediaArtworkView* artwork_ = nullptr;
|
||||
views::Label* title_label_ = nullptr;
|
||||
views::Label* subtitle_label_ = nullptr;
|
||||
views::ToggleImageButton* picture_in_picture_button_ = nullptr;
|
||||
views::View* notification_controls_spacer_ = nullptr;
|
||||
views::View* media_controls_container_ = nullptr;
|
||||
views::ToggleImageButton* play_pause_button_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace media_message_center
|
||||
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user