summaryrefslogtreecommitdiff
path: root/chromium/components/media_message_center
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-16 11:45:35 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2020-07-17 08:59:23 +0000
commit552906b0f222c5d5dd11b9fd73829d510980461a (patch)
tree3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/components/media_message_center
parent1b05827804eaf047779b597718c03e7d38344261 (diff)
downloadqtwebengine-chromium-552906b0f222c5d5dd11b9fd73829d510980461a.tar.gz
BASELINE: Update Chromium to 83.0.4103.122
Change-Id: Ie3a82f5bb0076eec2a7c6a6162326b4301ee291e Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/components/media_message_center')
-rw-r--r--chromium/components/media_message_center/media_controls_progress_view.cc8
-rw-r--r--chromium/components/media_message_center/media_controls_progress_view_unittest.cc28
-rw-r--r--chromium/components/media_message_center/media_notification_background.cc14
-rw-r--r--chromium/components/media_message_center/media_notification_controller.h5
-rw-r--r--chromium/components/media_message_center/media_notification_item.cc4
-rw-r--r--chromium/components/media_message_center/media_notification_item.h8
-rw-r--r--chromium/components/media_message_center/media_notification_util.cc24
-rw-r--r--chromium/components/media_message_center/media_notification_util.h10
-rw-r--r--chromium/components/media_message_center/media_notification_view.h5
-rw-r--r--chromium/components/media_message_center/media_notification_view_impl.cc111
-rw-r--r--chromium/components/media_message_center/media_notification_view_impl.h8
-rw-r--r--chromium/components/media_message_center/media_notification_view_impl_unittest.cc176
-rw-r--r--chromium/components/media_message_center/media_session_notification_item.cc35
-rw-r--r--chromium/components/media_message_center/media_session_notification_item.h20
14 files changed, 387 insertions, 69 deletions
diff --git a/chromium/components/media_message_center/media_controls_progress_view.cc b/chromium/components/media_message_center/media_controls_progress_view.cc
index 2382471c155..ebc6769eec7 100644
--- a/chromium/components/media_message_center/media_controls_progress_view.cc
+++ b/chromium/components/media_message_center/media_controls_progress_view.cc
@@ -64,10 +64,10 @@ MediaControlsProgressView::MediaControlsProgressView(
auto time_spacing = std::make_unique<views::View>();
time_spacing->SetPreferredSize(kTimeSpacingSize);
- time_spacing->SetProperty(views::kFlexBehaviorKey,
- views::FlexSpecification::ForSizeRule(
- views::MinimumFlexSizeRule::kPreferred,
- views::MaximumFlexSizeRule::kUnbounded));
+ time_spacing->SetProperty(
+ views::kFlexBehaviorKey,
+ views::FlexSpecification(views::MinimumFlexSizeRule::kPreferred,
+ views::MaximumFlexSizeRule::kUnbounded));
time_view->AddChildView(std::move(time_spacing));
auto duration = std::make_unique<views::Label>();
diff --git a/chromium/components/media_message_center/media_controls_progress_view_unittest.cc b/chromium/components/media_message_center/media_controls_progress_view_unittest.cc
index 9f791ca37f3..82e76c8c721 100644
--- a/chromium/components/media_message_center/media_controls_progress_view_unittest.cc
+++ b/chromium/components/media_message_center/media_controls_progress_view_unittest.cc
@@ -120,8 +120,8 @@ TEST_F(MAYBE_MediaControlsProgressViewTest, UpdateProgress) {
base::ASCIIToUTF16("05:00"));
EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
- task_environment_->FastForwardBy(base::TimeDelta::FromSeconds(30));
- task_environment_->RunUntilIdle();
+ task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(30));
+ task_environment()->RunUntilIdle();
EXPECT_EQ(progress_view_->duration_for_testing(),
base::ASCIIToUTF16("10:00"));
@@ -143,8 +143,8 @@ TEST_F(MAYBE_MediaControlsProgressViewTest, UpdateProgressFastPlayback) {
base::ASCIIToUTF16("05:00"));
EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
- task_environment_->FastForwardBy(base::TimeDelta::FromSeconds(15));
- task_environment_->RunUntilIdle();
+ task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(15));
+ task_environment()->RunUntilIdle();
EXPECT_EQ(progress_view_->duration_for_testing(),
base::ASCIIToUTF16("10:00"));
@@ -166,8 +166,8 @@ TEST_F(MAYBE_MediaControlsProgressViewTest, UpdateProgressSlowPlayback) {
base::ASCIIToUTF16("05:00"));
EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
- task_environment_->FastForwardBy(base::TimeDelta::FromSeconds(60));
- task_environment_->RunUntilIdle();
+ task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(60));
+ task_environment()->RunUntilIdle();
EXPECT_EQ(progress_view_->duration_for_testing(),
base::ASCIIToUTF16("10:00"));
@@ -189,8 +189,8 @@ TEST_F(MAYBE_MediaControlsProgressViewTest, UpdateProgressNegativePlayback) {
base::ASCIIToUTF16("05:00"));
EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
- task_environment_->FastForwardBy(base::TimeDelta::FromSeconds(30));
- task_environment_->RunUntilIdle();
+ task_environment()->FastForwardBy(base::TimeDelta::FromSeconds(30));
+ task_environment()->RunUntilIdle();
EXPECT_EQ(progress_view_->duration_for_testing(),
base::ASCIIToUTF16("10:00"));
@@ -213,8 +213,8 @@ TEST_F(MAYBE_MediaControlsProgressViewTest, UpdateProgressPastDuration) {
EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
// Move forward in time past the duration.
- task_environment_->FastForwardBy(base::TimeDelta::FromMinutes(6));
- task_environment_->RunUntilIdle();
+ task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(6));
+ task_environment()->RunUntilIdle();
// Verify the progress does not go past the duration.
EXPECT_EQ(progress_view_->duration_for_testing(),
@@ -238,8 +238,8 @@ TEST_F(MAYBE_MediaControlsProgressViewTest, UpdateProgressBeforeStart) {
EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
// Move forward in time before the start using negative playback rate.
- task_environment_->FastForwardBy(base::TimeDelta::FromMinutes(6));
- task_environment_->RunUntilIdle();
+ task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(6));
+ task_environment()->RunUntilIdle();
// Verify the progress does not go below 0.
EXPECT_EQ(progress_view_->duration_for_testing(),
@@ -262,8 +262,8 @@ TEST_F(MAYBE_MediaControlsProgressViewTest, UpdateProgressPaused) {
base::ASCIIToUTF16("05:00"));
EXPECT_EQ(progress_view_->progress_bar_for_testing()->GetValue(), .5);
- task_environment_->FastForwardBy(base::TimeDelta::FromMinutes(6));
- task_environment_->RunUntilIdle();
+ task_environment()->FastForwardBy(base::TimeDelta::FromMinutes(6));
+ task_environment()->RunUntilIdle();
// Verify the progress does not change while media is paused.
EXPECT_EQ(progress_view_->duration_for_testing(),
diff --git a/chromium/components/media_message_center/media_notification_background.cc b/chromium/components/media_message_center/media_notification_background.cc
index 86db3b7de2d..97cadb59af3 100644
--- a/chromium/components/media_message_center/media_notification_background.cc
+++ b/chromium/components/media_message_center/media_notification_background.cc
@@ -35,6 +35,8 @@ constexpr double kMediaNotificationForegroundColorMostPopularMinSaturation =
// The ratio for the more vibrant foreground color to use.
constexpr double kMediaNotificationForegroundColorMoreVibrantRatio = 1.0;
+constexpr float kMediaNotificationMinimumContrastRatio = 7.0;
+
bool IsNearlyWhiteOrBlack(SkColor color) {
color_utils::HSL hsl;
color_utils::SkColorToHSL(color, &hsl);
@@ -356,7 +358,9 @@ SkColor MediaNotificationBackground::GetForegroundColor(
? *foreground_color_
: views::style::GetColor(owner, views::style::CONTEXT_LABEL,
views::style::STYLE_PRIMARY);
- return color_utils::BlendForMinContrast(foreground, GetBackgroundColor(owner))
+ return color_utils::BlendForMinContrast(
+ foreground, GetBackgroundColor(owner), base::nullopt,
+ kMediaNotificationMinimumContrastRatio)
.color;
}
@@ -382,11 +386,17 @@ gfx::Rect MediaNotificationBackground::GetArtworkBounds(
const views::View& owner) const {
const gfx::Rect& view_bounds = owner.GetContentsBounds();
int width = GetArtworkWidth(view_bounds.size());
+ int visible_width = GetArtworkVisibleWidth(view_bounds.size());
+
+ // This offset is for centering artwork if artwork visible width is smaller
+ // than artwork width.
+ int horizontal_offset = (width - visible_width) / 2;
// The artwork should be positioned on the far right hand side of the
// notification and be the same height.
return owner.GetMirroredRect(
- gfx::Rect(view_bounds.right() - width, 0, width, view_bounds.height()));
+ gfx::Rect(view_bounds.right() - width + horizontal_offset, 0, width,
+ view_bounds.height()));
}
gfx::Rect MediaNotificationBackground::GetFilledBackgroundBounds(
diff --git a/chromium/components/media_message_center/media_notification_controller.h b/chromium/components/media_message_center/media_notification_controller.h
index 7fdbcdb5bfc..344bf9e70bf 100644
--- a/chromium/components/media_message_center/media_notification_controller.h
+++ b/chromium/components/media_message_center/media_notification_controller.h
@@ -8,6 +8,7 @@
#include <string>
#include "base/component_export.h"
+#include "services/media_session/public/mojom/media_session.mojom.h"
template <typename T>
class scoped_refptr;
@@ -37,7 +38,9 @@ class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaNotificationController {
// Notifies the MediaNotificationController that a media button was pressed on
// the MediaNotificationView.
- virtual void LogMediaSessionActionButtonPressed(const std::string& id) = 0;
+ virtual void LogMediaSessionActionButtonPressed(
+ const std::string& id,
+ media_session::mojom::MediaSessionAction action) = 0;
protected:
virtual ~MediaNotificationController() = default;
diff --git a/chromium/components/media_message_center/media_notification_item.cc b/chromium/components/media_message_center/media_notification_item.cc
index 8f3cee7212b..bcd2655aab3 100644
--- a/chromium/components/media_message_center/media_notification_item.cc
+++ b/chromium/components/media_message_center/media_notification_item.cc
@@ -11,6 +11,10 @@ const char MediaNotificationItem::kUserActionHistogramName[] =
"Media.Notification.UserAction";
// static
+const char MediaNotificationItem::kCastUserActionHistogramName[] =
+ "Media.Notification.Cast.UserAction";
+
+// static
const char MediaNotificationItem::kSourceHistogramName[] =
"Media.Notification.Source";
diff --git a/chromium/components/media_message_center/media_notification_item.h b/chromium/components/media_message_center/media_notification_item.h
index 4b84db0f5cd..54c5c37b78c 100644
--- a/chromium/components/media_message_center/media_notification_item.h
+++ b/chromium/components/media_message_center/media_notification_item.h
@@ -24,6 +24,10 @@ class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaNotificationItem {
// The name of the histogram used when recording user actions.
static const char kUserActionHistogramName[];
+ // The name of the histogram used when recording user actions for Cast
+ // notifications.
+ static const char kCastUserActionHistogramName[];
+
// The name of the histogram used when recording the source.
static const char kSourceHistogramName[];
@@ -34,7 +38,9 @@ class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaNotificationItem {
kWeb,
kAssistant,
kArc,
- kMaxValue = kArc,
+ kLocalCastSession,
+ kNonLocalCastSession,
+ kMaxValue = kNonLocalCastSession,
};
MediaNotificationItem() = default;
diff --git a/chromium/components/media_message_center/media_notification_util.cc b/chromium/components/media_message_center/media_notification_util.cc
index ac683831483..59b9fa54433 100644
--- a/chromium/components/media_message_center/media_notification_util.cc
+++ b/chromium/components/media_message_center/media_notification_util.cc
@@ -18,9 +18,14 @@ namespace {
// show all the action buttons then this is used to determine which will be
// shown.
constexpr MediaSessionAction kPreferredActions[] = {
- MediaSessionAction::kPlay, MediaSessionAction::kPause,
- MediaSessionAction::kPreviousTrack, MediaSessionAction::kNextTrack,
- MediaSessionAction::kSeekBackward, MediaSessionAction::kSeekForward,
+ MediaSessionAction::kPlay,
+ MediaSessionAction::kPause,
+ MediaSessionAction::kPreviousTrack,
+ MediaSessionAction::kNextTrack,
+ MediaSessionAction::kSeekBackward,
+ MediaSessionAction::kSeekForward,
+ MediaSessionAction::kEnterPictureInPicture,
+ MediaSessionAction::kExitPictureInPicture,
};
// The maximum number of media notifications to count when recording the
@@ -31,6 +36,7 @@ const int kMediaNotificationCountHistogramMax = 20;
} // namespace
const char kCountHistogramName[] = "Media.Notification.Count";
+const char kCastCountHistogramName[] = "Media.Notification.Cast.Count";
base::string16 GetAccessibleNameFromMetadata(
media_session::MediaMetadata session_metadata) {
@@ -81,9 +87,21 @@ MediaSessionAction GetPlayPauseIgnoredAction(
: MediaSessionAction::kPlay;
}
+MediaSessionAction GetPictureInPictureIgnoredAction(
+ MediaSessionAction current_action) {
+ return current_action == MediaSessionAction::kEnterPictureInPicture
+ ? MediaSessionAction::kExitPictureInPicture
+ : MediaSessionAction::kEnterPictureInPicture;
+}
+
void RecordConcurrentNotificationCount(size_t count) {
UMA_HISTOGRAM_EXACT_LINEAR(kCountHistogramName, count,
kMediaNotificationCountHistogramMax);
}
+void RecordConcurrentCastNotificationCount(size_t count) {
+ UMA_HISTOGRAM_EXACT_LINEAR(kCastCountHistogramName, count,
+ kMediaNotificationCountHistogramMax);
+}
+
} // namespace media_message_center
diff --git a/chromium/components/media_message_center/media_notification_util.h b/chromium/components/media_message_center/media_notification_util.h
index d27ff2dc142..8a5d385418b 100644
--- a/chromium/components/media_message_center/media_notification_util.h
+++ b/chromium/components/media_message_center/media_notification_util.h
@@ -49,10 +49,20 @@ COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER)
media_session::mojom::MediaSessionAction GetPlayPauseIgnoredAction(
media_session::mojom::MediaSessionAction current_action);
+// Returns the action on the enter/exit pip toggle button that should be
+// ignored when calculating the visible actions.
+COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER)
+media_session::mojom::MediaSessionAction GetPictureInPictureIgnoredAction(
+ media_session::mojom::MediaSessionAction current_action);
+
// Records the concurrent number of media notifications displayed.
COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER)
void RecordConcurrentNotificationCount(size_t count);
+// Records the concurrent number of Cast media notifications displayed.
+COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER)
+void RecordConcurrentCastNotificationCount(size_t count);
+
} // namespace media_message_center
#endif // COMPONENTS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_UTIL_H_
diff --git a/chromium/components/media_message_center/media_notification_view.h b/chromium/components/media_message_center/media_notification_view.h
index 31a12ee6e9c..96aa7421f67 100644
--- a/chromium/components/media_message_center/media_notification_view.h
+++ b/chromium/components/media_message_center/media_notification_view.h
@@ -10,6 +10,7 @@
namespace gfx {
class ImageSkia;
+struct VectorIcon;
} // namespace gfx
namespace media_session {
@@ -39,7 +40,11 @@ class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaNotificationView
const base::flat_set<media_session::mojom::MediaSessionAction>&
actions) = 0;
virtual void UpdateWithMediaArtwork(const gfx::ImageSkia& image) = 0;
+ // Updates the background color to match that of the favicon.
virtual void UpdateWithFavicon(const gfx::ImageSkia& icon) = 0;
+ // Sets the icon to be displayed in the notification's header section.
+ // |vector_icon| must outlive the MediaNotificationView.
+ virtual void UpdateWithVectorIcon(const gfx::VectorIcon& vector_icon) = 0;
};
} // namespace media_message_center
diff --git a/chromium/components/media_message_center/media_notification_view_impl.cc b/chromium/components/media_message_center/media_notification_view_impl.cc
index a88d333b10a..6d9e607fd1e 100644
--- a/chromium/components/media_message_center/media_notification_view_impl.cc
+++ b/chromium/components/media_message_center/media_notification_view_impl.cc
@@ -19,6 +19,8 @@
#include "ui/base/l10n/l10n_util.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/message_center/views/notification_header_view.h"
#include "ui/views/controls/button/image_button_factory.h"
#include "ui/views/layout/box_layout.h"
@@ -33,16 +35,16 @@ namespace {
// The number of actions supported when the notification is expanded or not.
constexpr size_t kMediaNotificationActionsCount = 3;
-constexpr size_t kMediaNotificationExpandedActionsCount = 5;
+constexpr size_t kMediaNotificationExpandedActionsCount = 6;
// Dimensions.
constexpr int kDefaultMarginSize = 8;
-constexpr int kMediaButtonIconSize = 28;
+constexpr int kMediaButtonIconSize = 16;
constexpr int kTitleArtistLineHeight = 20;
constexpr double kMediaImageMaxWidthPct = 0.3;
constexpr double kMediaImageMaxWidthExpandedPct = 0.4;
constexpr gfx::Size kMediaButtonSize = gfx::Size(36, 36);
-constexpr int kMediaButtonRowSeparator = 8;
+constexpr int kMediaButtonRowSeparator = 0;
constexpr gfx::Insets kMediaTitleArtistInsets = gfx::Insets(8, 8, 0, 8);
constexpr gfx::Insets kIconlessMediaNotificationHeaderInsets =
gfx::Insets(6, 14, 0, 6);
@@ -50,6 +52,7 @@ constexpr gfx::Insets kIconMediaNotificationHeaderInsets =
gfx::Insets(6, 0, 0, 6);
constexpr gfx::Size kMediaNotificationButtonRowSize =
gfx::Size(124, kMediaButtonSize.height());
+constexpr gfx::Size kPipButtonSeparatorViewSize = gfx::Size(20, 24);
void RecordMetadataHistogram(MediaNotificationViewImpl::Metadata metadata) {
UMA_HISTOGRAM_ENUMERATION(MediaNotificationViewImpl::kMetadataHistogramName,
@@ -70,6 +73,10 @@ const gfx::VectorIcon* GetVectorIconForMediaAction(MediaSessionAction action) {
return &vector_icons::kMediaSeekForwardIcon;
case MediaSessionAction::kNextTrack:
return &vector_icons::kMediaNextTrackIcon;
+ case MediaSessionAction::kEnterPictureInPicture:
+ return &vector_icons::kMediaEnterPipIcon;
+ case MediaSessionAction::kExitPictureInPicture:
+ return &vector_icons::kMediaExitPipIcon;
case MediaSessionAction::kStop:
case MediaSessionAction::kSkipAd:
case MediaSessionAction::kSeekTo:
@@ -127,7 +134,7 @@ MediaNotificationViewImpl::MediaNotificationViewImpl(
header_row->SetProperty(views::kMarginsKey,
kIconMediaNotificationHeaderInsets);
} else {
- header_row->HideAppIcon();
+ header_row->SetAppIconVisible(false);
header_row->SetProperty(views::kMarginsKey,
kIconlessMediaNotificationHeaderInsets);
}
@@ -207,6 +214,39 @@ MediaNotificationViewImpl::MediaNotificationViewImpl(
l10n_util::GetStringUTF16(
IDS_MEDIA_MESSAGE_CENTER_MEDIA_NOTIFICATION_ACTION_NEXT_TRACK));
+ auto pip_button_separator_view = std::make_unique<views::View>();
+ auto* pip_button_separator_view_layout =
+ pip_button_separator_view->SetLayoutManager(
+ std::make_unique<views::BoxLayout>(
+ views::BoxLayout::Orientation::kHorizontal, gfx::Insets(), 0));
+ pip_button_separator_view_layout->set_main_axis_alignment(
+ views::BoxLayout::MainAxisAlignment::kCenter);
+ pip_button_separator_view_layout->set_cross_axis_alignment(
+ views::BoxLayout::CrossAxisAlignment::kCenter);
+ pip_button_separator_view->SetPreferredSize(kPipButtonSeparatorViewSize);
+
+ auto pip_button_separator_stroke = std::make_unique<views::View>();
+ pip_button_separator_stroke->SetPreferredSize(
+ gfx::Size(1, kPipButtonSeparatorViewSize.height()));
+
+ pip_button_separator_view->AddChildView(
+ std::move(pip_button_separator_stroke));
+ pip_button_separator_view_ =
+ button_row_->AddChildView(std::move(pip_button_separator_view));
+
+ auto picture_in_picture_button = views::CreateVectorToggleImageButton(this);
+ picture_in_picture_button->set_tag(
+ static_cast<int>(MediaSessionAction::kEnterPictureInPicture));
+ picture_in_picture_button->SetPreferredSize(kMediaButtonSize);
+ 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_ =
+ button_row_->AddChildView(std::move(picture_in_picture_button));
+
SetBackground(std::make_unique<MediaNotificationBackground>(
message_center::kNotificationCornerRadius,
message_center::kNotificationCornerRadius, kMediaImageMaxWidthPct));
@@ -303,6 +343,16 @@ void MediaNotificationViewImpl::UpdateWithMediaSessionInfo(
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);
@@ -390,19 +440,40 @@ void MediaNotificationViewImpl::UpdateWithFavicon(const gfx::ImageSkia& icon) {
SchedulePaint();
}
+void MediaNotificationViewImpl::UpdateWithVectorIcon(
+ const gfx::VectorIcon& vector_icon) {
+ vector_header_icon_ = &vector_icon;
+ const SkColor foreground =
+ GetMediaNotificationBackground()->GetForegroundColor(*this);
+ header_row_->SetAppIcon(gfx::CreateVectorIcon(
+ *vector_header_icon_, message_center::kSmallImageSizeMD, foreground));
+ header_row_->SetAppIconVisible(true);
+ header_row_->SetProperty(views::kMarginsKey,
+ kIconMediaNotificationHeaderInsets);
+}
+
views::Button* MediaNotificationViewImpl::GetHeaderRowForTesting() const {
return header_row_;
}
+base::string16 MediaNotificationViewImpl::GetSourceTitleForTesting() const {
+ return header_row_->app_name_for_testing();
+}
+
void MediaNotificationViewImpl::UpdateActionButtonsVisibility() {
base::flat_set<MediaSessionAction> ignored_actions = {
- GetPlayPauseIgnoredAction(GetActionFromButtonTag(*play_pause_button_))};
+ GetPlayPauseIgnoredAction(GetActionFromButtonTag(*play_pause_button_)),
+ GetPictureInPictureIgnoredAction(
+ GetActionFromButtonTag(*picture_in_picture_button_))};
base::flat_set<MediaSessionAction> visible_actions =
GetTopVisibleActions(enabled_actions_, ignored_actions,
GetMaxNumActions(IsActuallyExpanded()));
for (auto* view : button_row_->children()) {
+ if (view == pip_button_separator_view_)
+ continue;
+
views::Button* action_button = views::Button::AsButton(view);
bool should_show =
base::Contains(visible_actions, GetActionFromButtonTag(*action_button));
@@ -412,6 +483,13 @@ void MediaNotificationViewImpl::UpdateActionButtonsVisibility() {
if (should_invalidate)
action_button->InvalidateLayout();
+
+ if (action_button == picture_in_picture_button_) {
+ pip_button_separator_view_->SetVisible(should_show);
+
+ if (should_invalidate)
+ pip_button_separator_view_->InvalidateLayout();
+ }
}
// We want to give the container a list of all possibly visible actions, and
@@ -490,7 +568,9 @@ bool MediaNotificationViewImpl::IsExpandable() const {
return false;
base::flat_set<MediaSessionAction> ignored_actions = {
- GetPlayPauseIgnoredAction(GetActionFromButtonTag(*play_pause_button_))};
+ GetPlayPauseIgnoredAction(GetActionFromButtonTag(*play_pause_button_)),
+ GetPictureInPictureIgnoredAction(
+ GetActionFromButtonTag(*picture_in_picture_button_))};
// If we can show more notifications if we were expanded then we should be
// expandable.
@@ -514,11 +594,18 @@ void MediaNotificationViewImpl::UpdateForegroundColor() {
title_label_->SetEnabledColor(foreground);
artist_label_->SetEnabledColor(foreground);
header_row_->SetAccentColor(foreground);
+ if (vector_header_icon_) {
+ header_row_->SetAppIcon(gfx::CreateVectorIcon(
+ *vector_header_icon_, message_center::kSmallImageSizeMD, foreground));
+ }
title_label_->SetBackgroundColor(background);
artist_label_->SetBackgroundColor(background);
header_row_->SetBackgroundColor(background);
+ pip_button_separator_view_->children().front()->SetBackground(
+ views::CreateSolidBackground(foreground));
+
// Update play/pause button images.
views::SetImageFromVectorIconWithColor(
play_pause_button_,
@@ -529,12 +616,24 @@ void MediaNotificationViewImpl::UpdateForegroundColor() {
*GetVectorIconForMediaAction(MediaSessionAction::kPause),
kMediaButtonIconSize, foreground);
+ views::SetImageFromVectorIconWithColor(
+ picture_in_picture_button_,
+ *GetVectorIconForMediaAction(MediaSessionAction::kEnterPictureInPicture),
+ kMediaButtonIconSize, foreground);
+ views::SetToggledImageFromVectorIconWithColor(
+ picture_in_picture_button_,
+ *GetVectorIconForMediaAction(MediaSessionAction::kExitPictureInPicture),
+ kMediaButtonIconSize, foreground);
+
// Update action buttons.
for (views::View* child : button_row_->children()) {
// Skip the play pause button since it is a special case.
if (child == play_pause_button_)
continue;
+ if (child == picture_in_picture_button_)
+ continue;
+
// Skip if the view is not an image button.
if (child->GetClassName() != views::ImageButton::kViewClassName)
continue;
diff --git a/chromium/components/media_message_center/media_notification_view_impl.h b/chromium/components/media_message_center/media_notification_view_impl.h
index e433515f691..52db6cb00df 100644
--- a/chromium/components/media_message_center/media_notification_view_impl.h
+++ b/chromium/components/media_message_center/media_notification_view_impl.h
@@ -48,7 +48,8 @@ class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaNotificationViewImpl
kArtist,
kAlbum,
kCount,
- kMaxValue = kCount,
+ kSource,
+ kMaxValue = kSource,
};
MediaNotificationViewImpl(
@@ -79,12 +80,14 @@ class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaNotificationViewImpl
override;
void UpdateWithMediaArtwork(const gfx::ImageSkia& image) override;
void UpdateWithFavicon(const gfx::ImageSkia& icon) override;
+ void UpdateWithVectorIcon(const gfx::VectorIcon& vector_icon) override;
const views::Label* title_label_for_testing() const { return title_label_; }
const views::Label* artist_label_for_testing() const { return artist_label_; }
views::Button* GetHeaderRowForTesting() const;
+ base::string16 GetSourceTitleForTesting() const;
private:
friend class MediaNotificationViewImplTest;
@@ -140,7 +143,9 @@ class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaNotificationViewImpl
// Container views directly attached to this view.
message_center::NotificationHeaderView* header_row_ = nullptr;
views::View* button_row_ = nullptr;
+ views::View* pip_button_separator_view_ = nullptr;
views::ToggleImageButton* play_pause_button_ = nullptr;
+ views::ToggleImageButton* picture_in_picture_button_ = nullptr;
views::View* title_artist_row_ = nullptr;
views::Label* title_label_ = nullptr;
views::Label* artist_label_ = nullptr;
@@ -148,6 +153,7 @@ class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaNotificationViewImpl
views::View* main_row_ = nullptr;
views::BoxLayout* title_artist_row_layout_ = nullptr;
+ const gfx::VectorIcon* vector_header_icon_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(MediaNotificationViewImpl);
};
diff --git a/chromium/components/media_message_center/media_notification_view_impl_unittest.cc b/chromium/components/media_message_center/media_notification_view_impl_unittest.cc
index 15eef9fa372..44b9b4e947c 100644
--- a/chromium/components/media_message_center/media_notification_view_impl_unittest.cc
+++ b/chromium/components/media_message_center/media_notification_view_impl_unittest.cc
@@ -7,10 +7,12 @@
#include <memory>
#include "base/bind.h"
+#include "base/bind_helpers.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
+#include "base/test/mock_callback.h"
#include "base/test/task_environment.h"
#include "base/unguessable_token.h"
#include "build/build_config.h"
@@ -78,7 +80,9 @@ class MockMediaNotificationController : public MediaNotificationController {
scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() const override {
return nullptr;
}
- MOCK_METHOD1(LogMediaSessionActionButtonPressed, void(const std::string& id));
+ MOCK_METHOD2(LogMediaSessionActionButtonPressed,
+ void(const std::string& id,
+ media_session::mojom::MediaSessionAction action));
private:
DISALLOW_COPY_AND_ASSIGN(MockMediaNotificationController);
@@ -182,6 +186,8 @@ class MediaNotificationViewImplTest : public views::ViewsTestBase {
actions_.insert(MediaSessionAction::kSeekBackward);
actions_.insert(MediaSessionAction::kSeekForward);
actions_.insert(MediaSessionAction::kStop);
+ actions_.insert(MediaSessionAction::kEnterPictureInPicture);
+ actions_.insert(MediaSessionAction::kExitPictureInPicture);
NotifyUpdatedActions();
}
@@ -231,7 +237,9 @@ class MediaNotificationViewImplTest : public views::ViewsTestBase {
const auto& children = button_row()->children();
const auto i = std::find_if(
children.begin(), children.end(), [action](const views::View* v) {
- return views::Button::AsButton(v)->tag() == static_cast<int>(action);
+ return (IsMediaButtonType(v->GetClassName()) &&
+ views::Button::AsButton(v)->tag() ==
+ static_cast<int>(action));
});
return (i == children.end()) ? nullptr : views::Button::AsButton(*i);
}
@@ -298,8 +306,7 @@ class MediaNotificationViewImplTest : public views::ViewsTestBase {
}
void AdvanceClockMilliseconds(int milliseconds) {
- ASSERT_TRUE(task_environment_.has_value());
- task_environment_->FastForwardBy(
+ task_environment()->FastForwardBy(
base::TimeDelta::FromMilliseconds(milliseconds));
}
@@ -360,10 +367,11 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, ButtonsSanityCheck) {
EXPECT_GT(button_row()->width(), 0);
EXPECT_GT(button_row()->height(), 0);
- EXPECT_EQ(5u, button_row()->children().size());
+ EXPECT_EQ(7u, button_row()->children().size());
for (auto* child : button_row()->children()) {
- ASSERT_TRUE(IsMediaButtonType(child->GetClassName()));
+ if (!IsMediaButtonType(child->GetClassName()))
+ continue;
EXPECT_TRUE(child->GetVisible());
EXPECT_LT(kMediaButtonIconSize, child->width());
@@ -376,9 +384,11 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, ButtonsSanityCheck) {
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekBackward));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekForward));
+ EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kEnterPictureInPicture));
// |kPause| cannot be present if |kPlay| is.
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+ EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kExitPictureInPicture));
}
#if defined(OS_WIN)
@@ -440,7 +450,8 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, PlayPauseButtonTooltipCheck) {
}
TEST_F(MAYBE_MediaNotificationViewImplTest, NextTrackButtonClick) {
- EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(_));
+ EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
+ _, MediaSessionAction::kNextTrack));
EnableAction(MediaSessionAction::kNextTrack);
EXPECT_EQ(0, media_controller()->next_track_count());
@@ -453,7 +464,8 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, NextTrackButtonClick) {
}
TEST_F(MAYBE_MediaNotificationViewImplTest, PlayButtonClick) {
- EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(_));
+ EXPECT_CALL(controller(),
+ LogMediaSessionActionButtonPressed(_, MediaSessionAction::kPlay));
EnableAction(MediaSessionAction::kPlay);
EXPECT_EQ(0, media_controller()->resume_count());
@@ -466,7 +478,8 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, PlayButtonClick) {
}
TEST_F(MAYBE_MediaNotificationViewImplTest, PauseButtonClick) {
- EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(_));
+ EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
+ _, MediaSessionAction::kPause));
EnableAction(MediaSessionAction::kPause);
EXPECT_CALL(container(), OnMediaSessionInfoChanged(_));
@@ -487,7 +500,8 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, PauseButtonClick) {
}
TEST_F(MAYBE_MediaNotificationViewImplTest, PreviousTrackButtonClick) {
- EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(_));
+ EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
+ _, MediaSessionAction::kPreviousTrack));
EnableAction(MediaSessionAction::kPreviousTrack);
EXPECT_EQ(0, media_controller()->previous_track_count());
@@ -500,7 +514,8 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, PreviousTrackButtonClick) {
}
TEST_F(MAYBE_MediaNotificationViewImplTest, SeekBackwardButtonClick) {
- EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(_));
+ EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
+ _, MediaSessionAction::kSeekBackward));
EnableAction(MediaSessionAction::kSeekBackward);
EXPECT_EQ(0, media_controller()->seek_backward_count());
@@ -513,7 +528,8 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, SeekBackwardButtonClick) {
}
TEST_F(MAYBE_MediaNotificationViewImplTest, SeekForwardButtonClick) {
- EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(_));
+ EXPECT_CALL(controller(), LogMediaSessionActionButtonPressed(
+ _, MediaSessionAction::kSeekForward));
EnableAction(MediaSessionAction::kSeekForward);
EXPECT_EQ(0, media_controller()->seek_forward_count());
@@ -678,7 +694,8 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, Buttons_WhenCollapsed) {
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
- MediaSessionAction::kSeekForward})));
+ MediaSessionAction::kSeekForward,
+ MediaSessionAction::kEnterPictureInPicture})));
EnableAllActions();
view()->SetExpanded(false);
testing::Mock::VerifyAndClearExpectations(&container());
@@ -695,7 +712,8 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, Buttons_WhenCollapsed) {
container(),
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kSeekBackward,
- MediaSessionAction::kNextTrack, MediaSessionAction::kSeekForward})));
+ MediaSessionAction::kNextTrack, MediaSessionAction::kSeekForward,
+ MediaSessionAction::kEnterPictureInPicture})));
DisableAction(MediaSessionAction::kPreviousTrack);
testing::Mock::VerifyAndClearExpectations(&container());
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
@@ -705,7 +723,8 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, Buttons_WhenCollapsed) {
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
- MediaSessionAction::kSeekForward})));
+ MediaSessionAction::kSeekForward,
+ MediaSessionAction::kEnterPictureInPicture})));
EnableAction(MediaSessionAction::kPreviousTrack);
testing::Mock::VerifyAndClearExpectations(&container());
EXPECT_TRUE(IsActionButtonVisible(MediaSessionAction::kPreviousTrack));
@@ -714,8 +733,8 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, Buttons_WhenCollapsed) {
container(),
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
- MediaSessionAction::kNextTrack,
- MediaSessionAction::kSeekBackward})));
+ MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
+ MediaSessionAction::kEnterPictureInPicture})));
DisableAction(MediaSessionAction::kSeekForward);
testing::Mock::VerifyAndClearExpectations(&container());
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
@@ -725,7 +744,8 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, Buttons_WhenCollapsed) {
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
- MediaSessionAction::kSeekForward})));
+ MediaSessionAction::kSeekForward,
+ MediaSessionAction::kEnterPictureInPicture})));
EnableAction(MediaSessionAction::kSeekForward);
testing::Mock::VerifyAndClearExpectations(&container());
EXPECT_FALSE(IsActionButtonVisible(MediaSessionAction::kSeekForward));
@@ -737,7 +757,8 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, Buttons_WhenExpanded) {
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
- MediaSessionAction::kSeekForward})));
+ MediaSessionAction::kSeekForward,
+ MediaSessionAction::kEnterPictureInPicture})));
EnableAllActions();
testing::Mock::VerifyAndClearExpectations(&container());
@@ -746,7 +767,8 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, Buttons_WhenExpanded) {
OnVisibleActionsChanged(base::flat_set<MediaSessionAction>(
{MediaSessionAction::kPlay, MediaSessionAction::kPreviousTrack,
MediaSessionAction::kNextTrack, MediaSessionAction::kSeekBackward,
- MediaSessionAction::kSeekForward})));
+ MediaSessionAction::kSeekForward,
+ MediaSessionAction::kEnterPictureInPicture})));
view()->SetExpanded(true);
testing::Mock::VerifyAndClearExpectations(&container());
@@ -803,7 +825,7 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, UpdateArtworkFromItem) {
SkBitmap image;
image.allocN32Pixels(10, 10);
- image.eraseColor(SK_ColorMAGENTA);
+ image.eraseColor(SK_ColorGREEN);
EXPECT_TRUE(GetArtworkImage().isNull());
@@ -1005,7 +1027,7 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DoNotUpdateMetadata) {
metadata.album = base::ASCIIToUTF16("album");
EXPECT_CALL(container(), OnMediaSessionMetadataChanged()).Times(0);
- GetItem()->Freeze();
+ GetItem()->Freeze(base::DoNothing());
GetItem()->MediaSessionMetadataChanged(metadata);
EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
@@ -1019,7 +1041,7 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DoNotUpdateImage) {
EXPECT_CALL(container(), OnMediaArtworkChanged(_)).Times(0);
EXPECT_CALL(container(), OnColorsChanged(_, _)).Times(0);
- GetItem()->Freeze();
+ GetItem()->Freeze(base::DoNothing());
GetItem()->MediaControllerImageChanged(
media_session::mojom::MediaSessionImageType::kArtwork, image);
@@ -1032,7 +1054,7 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DoNotUpdatePlaybackState) {
EXPECT_CALL(container(), OnMediaSessionInfoChanged(_)).Times(0);
- GetItem()->Freeze();
+ GetItem()->Freeze(base::DoNothing());
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
@@ -1051,7 +1073,7 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DoNotUpdateActions) {
EXPECT_FALSE(
GetButtonForAction(MediaSessionAction::kSeekForward)->GetVisible());
- GetItem()->Freeze();
+ GetItem()->Freeze(base::DoNothing());
EnableAction(MediaSessionAction::kSeekForward);
EXPECT_FALSE(
@@ -1063,7 +1085,7 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, Freezing_DisableInteraction) {
EXPECT_EQ(0, media_controller()->next_track_count());
- GetItem()->Freeze();
+ GetItem()->Freeze(base::DoNothing());
SimulateButtonClick(MediaSessionAction::kNextTrack);
GetItem()->FlushForTesting();
@@ -1076,7 +1098,9 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingDoesntMissUpdates) {
EnableAction(MediaSessionAction::kPause);
// Freeze the item and clear the metadata.
- GetItem()->Freeze();
+ base::MockOnceClosure unfrozen_callback;
+ EXPECT_CALL(unfrozen_callback, Run).Times(0);
+ GetItem()->Freeze(unfrozen_callback.Get());
GetItem()->MediaSessionInfoChanged(nullptr);
GetItem()->MediaSessionMetadataChanged(base::nullopt);
@@ -1102,12 +1126,14 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingDoesntMissUpdates) {
// The item should still be frozen, and the view should contain the old data.
EXPECT_TRUE(GetItem()->frozen());
+ testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
// Update the metadata.
+ EXPECT_CALL(unfrozen_callback, Run);
media_session::MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("title2");
metadata.artist = base::ASCIIToUTF16("artist2");
@@ -1115,6 +1141,7 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingDoesntMissUpdates) {
// The item should no longer be frozen, and we should see the updated data.
EXPECT_FALSE(GetItem()->frozen());
+ testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(base::ASCIIToUTF16("title2"), title_label()->GetText());
@@ -1134,7 +1161,9 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingWaitsForArtwork_Timeout) {
EXPECT_FALSE(GetArtworkImage().isNull());
// Freeze the item and clear the metadata.
- GetItem()->Freeze();
+ base::MockOnceClosure unfrozen_callback;
+ EXPECT_CALL(unfrozen_callback, Run).Times(0);
+ GetItem()->Freeze(unfrozen_callback.Get());
GetItem()->MediaSessionInfoChanged(nullptr);
GetItem()->MediaSessionMetadataChanged(base::nullopt);
GetItem()->MediaControllerImageChanged(
@@ -1177,6 +1206,7 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingWaitsForArtwork_Timeout) {
// The item should still be frozen, and waiting for a new image.
EXPECT_TRUE(GetItem()->frozen());
+ testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
@@ -1185,9 +1215,11 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingWaitsForArtwork_Timeout) {
// Once the freeze timer fires, the item should unfreeze even if there's no
// artwork.
+ EXPECT_CALL(unfrozen_callback, Run);
AdvanceClockMilliseconds(2600);
EXPECT_FALSE(GetItem()->frozen());
+ testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(base::ASCIIToUTF16("title2"), title_label()->GetText());
@@ -1195,6 +1227,87 @@ TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingWaitsForArtwork_Timeout) {
EXPECT_TRUE(GetArtworkImage().isNull());
}
+TEST_F(MAYBE_MediaNotificationViewImplTest, UnfreezingWaitsForActions) {
+ EnableAction(MediaSessionAction::kPlay);
+ EnableAction(MediaSessionAction::kPause);
+ EnableAction(MediaSessionAction::kNextTrack);
+ EnableAction(MediaSessionAction::kPreviousTrack);
+
+ // Freeze the item and clear the metadata and actions.
+ base::MockOnceClosure unfrozen_callback;
+ EXPECT_CALL(unfrozen_callback, Run).Times(0);
+ GetItem()->Freeze(unfrozen_callback.Get());
+ GetItem()->MediaSessionInfoChanged(nullptr);
+ GetItem()->MediaSessionMetadataChanged(base::nullopt);
+ DisableAction(MediaSessionAction::kPlay);
+ DisableAction(MediaSessionAction::kPause);
+ DisableAction(MediaSessionAction::kNextTrack);
+ DisableAction(MediaSessionAction::kPreviousTrack);
+
+ // The item should be frozen and the view should contain the old data.
+ EXPECT_TRUE(GetItem()->frozen());
+ EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+ EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+ EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
+ EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
+ EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+ EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
+
+ // Bind the item to a new controller that's playing instead of paused.
+ auto new_media_controller = std::make_unique<TestMediaController>();
+ media_session::mojom::MediaSessionInfoPtr session_info(
+ media_session::mojom::MediaSessionInfo::New());
+ session_info->playback_state =
+ media_session::mojom::MediaPlaybackState::kPlaying;
+ session_info->is_controllable = true;
+ GetItem()->SetController(new_media_controller->CreateMediaControllerRemote(),
+ session_info.Clone());
+
+ // The item will receive a MediaSessionInfoChanged.
+ GetItem()->MediaSessionInfoChanged(session_info.Clone());
+
+ // The item should still be frozen, and the view should contain the old data.
+ EXPECT_TRUE(GetItem()->frozen());
+ EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+ EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+ EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
+ EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
+ EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+ EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
+
+ // Update the metadata.
+ media_session::MediaMetadata metadata;
+ metadata.title = base::ASCIIToUTF16("title2");
+ metadata.artist = base::ASCIIToUTF16("artist2");
+ GetItem()->MediaSessionMetadataChanged(metadata);
+
+ // The item should still be frozen, and waiting for new actions.
+ EXPECT_TRUE(GetItem()->frozen());
+ testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
+ EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
+ EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
+ EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kNextTrack));
+ EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPreviousTrack));
+ EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
+ EXPECT_EQ(base::ASCIIToUTF16("artist"), artist_label()->GetText());
+
+ // Once we receive actions, the item should unfreeze.
+ EXPECT_CALL(unfrozen_callback, Run);
+ EnableAction(MediaSessionAction::kPlay);
+ EnableAction(MediaSessionAction::kPause);
+ EnableAction(MediaSessionAction::kSeekForward);
+ EnableAction(MediaSessionAction::kSeekBackward);
+
+ EXPECT_FALSE(GetItem()->frozen());
+ testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
+ EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
+ EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
+ EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekForward));
+ EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kSeekBackward));
+ EXPECT_EQ(base::ASCIIToUTF16("title2"), title_label()->GetText());
+ EXPECT_EQ(base::ASCIIToUTF16("artist2"), artist_label()->GetText());
+}
+
TEST_F(MAYBE_MediaNotificationViewImplTest,
UnfreezingWaitsForArtwork_ReceiveArtwork) {
EnableAction(MediaSessionAction::kPlay);
@@ -1209,7 +1322,9 @@ TEST_F(MAYBE_MediaNotificationViewImplTest,
EXPECT_FALSE(GetArtworkImage().isNull());
// Freeze the item and clear the metadata.
- GetItem()->Freeze();
+ base::MockOnceClosure unfrozen_callback;
+ EXPECT_CALL(unfrozen_callback, Run).Times(0);
+ GetItem()->Freeze(unfrozen_callback.Get());
GetItem()->MediaSessionInfoChanged(nullptr);
GetItem()->MediaSessionMetadataChanged(base::nullopt);
GetItem()->MediaControllerImageChanged(
@@ -1252,6 +1367,7 @@ TEST_F(MAYBE_MediaNotificationViewImplTest,
// The item should still be frozen, and waiting for a new image.
EXPECT_TRUE(GetItem()->frozen());
+ testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(base::ASCIIToUTF16("title"), title_label()->GetText());
@@ -1259,6 +1375,7 @@ TEST_F(MAYBE_MediaNotificationViewImplTest,
EXPECT_FALSE(GetArtworkImage().isNull());
// Once we receive artwork, the item should unfreeze.
+ EXPECT_CALL(unfrozen_callback, Run);
SkBitmap new_image;
new_image.allocN32Pixels(10, 10);
new_image.eraseColor(SK_ColorYELLOW);
@@ -1266,6 +1383,7 @@ TEST_F(MAYBE_MediaNotificationViewImplTest,
media_session::mojom::MediaSessionImageType::kArtwork, new_image);
EXPECT_FALSE(GetItem()->frozen());
+ testing::Mock::VerifyAndClearExpectations(&unfrozen_callback);
EXPECT_FALSE(GetButtonForAction(MediaSessionAction::kPlay));
EXPECT_TRUE(GetButtonForAction(MediaSessionAction::kPause));
EXPECT_EQ(base::ASCIIToUTF16("title2"), title_label()->GetText());
diff --git a/chromium/components/media_message_center/media_session_notification_item.cc b/chromium/components/media_message_center/media_session_notification_item.cc
index 7ffe9d29355..88d5f4c2244 100644
--- a/chromium/components/media_message_center/media_session_notification_item.cc
+++ b/chromium/components/media_message_center/media_session_notification_item.cc
@@ -11,7 +11,6 @@
#include "components/media_message_center/media_notification_controller.h"
#include "components/media_message_center/media_notification_view.h"
#include "services/media_session/public/cpp/util.h"
-#include "services/media_session/public/mojom/constants.mojom.h"
#include "services/media_session/public/mojom/media_controller.mojom.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
#include "ui/gfx/favicon_size.h"
@@ -105,6 +104,8 @@ void MediaSessionNotificationItem::MediaSessionActionsChanged(
if (view_ && !frozen_) {
DCHECK(view_);
view_->UpdateWithMediaActions(session_actions_);
+ } else if (waiting_for_actions_) {
+ MaybeUnfreeze();
}
}
@@ -154,7 +155,7 @@ void MediaSessionNotificationItem::OnMediaSessionActionButtonPressed(
if (frozen_)
return;
- controller_->LogMediaSessionActionButtonPressed(request_id_);
+ controller_->LogMediaSessionActionButtonPressed(request_id_, action);
media_session::PerformMediaSessionAction(action, media_controller_remote_);
}
@@ -197,13 +198,15 @@ void MediaSessionNotificationItem::SetController(
MaybeHideOrShowNotification();
}
-void MediaSessionNotificationItem::Freeze() {
+void MediaSessionNotificationItem::Freeze(base::OnceClosure unfrozen_callback) {
is_bound_ = false;
+ unfrozen_callback_ = std::move(unfrozen_callback);
if (frozen_)
return;
frozen_ = true;
+ frozen_with_actions_ = HasActions();
frozen_with_artwork_ = HasArtwork();
freeze_timer_.Start(
@@ -233,12 +236,23 @@ void MediaSessionNotificationItem::MaybeUnfreeze() {
if (!frozen_)
return;
+ if (waiting_for_actions_ && !HasActions())
+ return;
+
if (waiting_for_artwork_ && !HasArtwork())
return;
if (!ShouldShowNotification() || !is_bound_)
return;
+ // If the currently frozen view has actions and the new session currently has
+ // no actions, then wait until either the freeze timer ends or the new actions
+ // are received.
+ if (frozen_with_actions_ && !HasActions()) {
+ waiting_for_actions_ = true;
+ return;
+ }
+
// If the currently frozen view has artwork and the new session currently has
// no artwork, then wait until either the freeze timer ends or the new artwork
// is downloaded.
@@ -252,6 +266,8 @@ void MediaSessionNotificationItem::MaybeUnfreeze() {
void MediaSessionNotificationItem::Unfreeze() {
frozen_ = false;
+ waiting_for_actions_ = false;
+ frozen_with_actions_ = false;
waiting_for_artwork_ = false;
frozen_with_artwork_ = false;
freeze_timer_.Stop();
@@ -269,6 +285,12 @@ void MediaSessionNotificationItem::Unfreeze() {
if (session_favicon_.has_value())
view_->UpdateWithFavicon(*session_favicon_);
}
+
+ std::move(unfrozen_callback_).Run();
+}
+
+bool MediaSessionNotificationItem::HasActions() const {
+ return !session_actions_.empty();
}
bool MediaSessionNotificationItem::HasArtwork() const {
@@ -278,9 +300,10 @@ bool MediaSessionNotificationItem::HasArtwork() const {
void MediaSessionNotificationItem::OnFreezeTimerFired() {
DCHECK(frozen_);
- // If we've just been waiting for artwork, stop waiting and just show what we
- // have.
- if (waiting_for_artwork_ && ShouldShowNotification() && is_bound_) {
+ // If we've just been waiting for actions or artwork, stop waiting and just
+ // show what we have.
+ if ((waiting_for_actions_ || waiting_for_artwork_) &&
+ ShouldShowNotification() && is_bound_) {
Unfreeze();
return;
}
diff --git a/chromium/components/media_message_center/media_session_notification_item.h b/chromium/components/media_message_center/media_session_notification_item.h
index 8e8a01bb1d1..e122a9f1d5a 100644
--- a/chromium/components/media_message_center/media_session_notification_item.h
+++ b/chromium/components/media_message_center/media_session_notification_item.h
@@ -7,6 +7,7 @@
#include <string>
+#include "base/callback.h"
#include "base/component_export.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
@@ -77,8 +78,10 @@ class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaSessionNotificationItem
media_session::mojom::MediaSessionInfoPtr session_info);
// This will freeze the item and start a timer to destroy the item after
- // some time has passed.
- void Freeze();
+ // some time has passed. If and when the item unfreezes, |unfrozen_callback|
+ // will be run. If the item does not unfreeze before timing out, then
+ // |unfrozen_callback| will not be called.
+ void Freeze(base::OnceClosure unfrozen_callback);
bool frozen() const { return frozen_; }
@@ -96,6 +99,8 @@ class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaSessionNotificationItem
void Unfreeze();
+ bool HasActions() const;
+
bool HasArtwork() const;
void OnFreezeTimerFired();
@@ -136,6 +141,14 @@ class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaSessionNotificationItem
// data and no actions will be executed.
bool frozen_ = false;
+ // True if we're currently frozen and the frozen view contains at least 1
+ // action.
+ bool frozen_with_actions_ = false;
+
+ // True if we have the necessary metadata to unfreeze, but we're waiting for
+ // new actions.
+ bool waiting_for_actions_ = false;
+
// True if we're currently frozen and the frozen view contains non-null
// artwork.
bool frozen_with_artwork_ = false;
@@ -148,6 +161,9 @@ class COMPONENT_EXPORT(MEDIA_MESSAGE_CENTER) MediaSessionNotificationItem
// has been frozen for a certain period of time.
base::OneShotTimer freeze_timer_;
+ // Called when the item unfreezes.
+ base::OnceClosure unfrozen_callback_;
+
mojo::Receiver<media_session::mojom::MediaControllerObserver>
observer_receiver_{this};