diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-16 11:45:35 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2020-07-17 08:59:23 +0000 |
commit | 552906b0f222c5d5dd11b9fd73829d510980461a (patch) | |
tree | 3a11e6ed0538a81dd83b20cf3a4783e297f26d91 /chromium/components/media_message_center | |
parent | 1b05827804eaf047779b597718c03e7d38344261 (diff) | |
download | qtwebengine-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')
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}; |