Commit a9e209f6 authored by Tommy Steimel's avatar Tommy Steimel Committed by Commit Bot

GMC: Go back to tab when notification is clicked

This CL makes GMC notifications clickable. When clicked, the tab that
originated the media is focused.

Bug: 1011052
Change-Id: I4f14cf8f5d19e12afbb5eaf11c1b8e7ac1a042c8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1862868
Commit-Queue: Tommy Steimel <steimel@chromium.org>
Reviewed-by: default avatarJazz Xu <jazzhsu@chromium.org>
Cr-Commit-Position: refs/heads/master@{#706728}
parent d3a4647f
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Global Media Controls-specific strings (included from generated_resources.grd). --> <!-- Global Media Controls-specific strings (included from generated_resources.grd). -->
<grit-part> <grit-part>
<message name="IDS_GLOBAL_MEDIA_CONTROLS_BACK_TO_TAB" desc="A11y text for the Global Media Controls container describing how clicking it takes you back to the tab.">
Back to tab
</message>
<message name="IDS_GLOBAL_MEDIA_CONTROLS_ICON_TOOLTIP_TEXT" desc="Tooltip for the Global Media Controls icon, which appears in the toolbar. The tooltip appears on mouseover of the icon."> <message name="IDS_GLOBAL_MEDIA_CONTROLS_ICON_TOOLTIP_TEXT" desc="Tooltip for the Global Media Controls icon, which appears in the toolbar. The tooltip appears on mouseover of the icon.">
Control your music, videos, and more Control your music, videos, and more
</message> </message>
......
c19525fb78d22d1e8351eac5871b1ff939f32c36
\ No newline at end of file
...@@ -15,6 +15,9 @@ class MediaNotificationContainerObserver : public base::CheckedObserver { ...@@ -15,6 +15,9 @@ class MediaNotificationContainerObserver : public base::CheckedObserver {
// Called when the metadata displayed in the container changes. // Called when the metadata displayed in the container changes.
virtual void OnContainerMetadataChanged() = 0; virtual void OnContainerMetadataChanged() = 0;
// Called when the container is clicked.
virtual void OnContainerClicked(const std::string& id) = 0;
// Called when the container is dismissed from the dialog. // Called when the container is dismissed from the dialog.
virtual void OnContainerDismissed(const std::string& id) = 0; virtual void OnContainerDismissed(const std::string& id) = 0;
......
...@@ -213,6 +213,22 @@ void MediaToolbarButtonController::LogMediaSessionActionButtonPressed( ...@@ -213,6 +213,22 @@ void MediaToolbarButtonController::LogMediaSessionActionButtonPressed(
IsWebContentsFocused(web_contents)); IsWebContentsFocused(web_contents));
} }
void MediaToolbarButtonController::OnContainerClicked(const std::string& id) {
auto it = sessions_.find(id);
if (it == sessions_.end())
return;
content::WebContents* web_contents = it->second.web_contents();
if (!web_contents)
return;
content::WebContentsDelegate* delegate = web_contents->GetDelegate();
if (!delegate)
return;
delegate->ActivateContents(web_contents);
}
void MediaToolbarButtonController::OnContainerDismissed(const std::string& id) { void MediaToolbarButtonController::OnContainerDismissed(const std::string& id) {
auto it = sessions_.find(id); auto it = sessions_.find(id);
if (it != sessions_.end()) if (it != sessions_.end())
......
...@@ -64,6 +64,7 @@ class MediaToolbarButtonController ...@@ -64,6 +64,7 @@ class MediaToolbarButtonController
// MediaNotificationContainerObserver implementation. // MediaNotificationContainerObserver implementation.
void OnContainerExpanded(bool expanded) override {} void OnContainerExpanded(bool expanded) override {}
void OnContainerMetadataChanged() override {} void OnContainerMetadataChanged() override {}
void OnContainerClicked(const std::string& id) override;
void OnContainerDismissed(const std::string& id) override; void OnContainerDismissed(const std::string& id) override;
void OnContainerDestroyed(const std::string& id) override; void OnContainerDestroyed(const std::string& id) override;
......
...@@ -50,6 +50,7 @@ class MediaDialogView : public views::BubbleDialogDelegateView, ...@@ -50,6 +50,7 @@ class MediaDialogView : public views::BubbleDialogDelegateView,
// MediaNotificationContainerObserver implementation. // MediaNotificationContainerObserver implementation.
void OnContainerExpanded(bool expanded) override; void OnContainerExpanded(bool expanded) override;
void OnContainerMetadataChanged() override; void OnContainerMetadataChanged() override;
void OnContainerClicked(const std::string& id) override {}
void OnContainerDismissed(const std::string& id) override {} void OnContainerDismissed(const std::string& id) override {}
void OnContainerDestroyed(const std::string& id) override; void OnContainerDestroyed(const std::string& id) override;
......
...@@ -201,19 +201,19 @@ class MediaDialogViewBrowserTest : public InProcessBrowserTest { ...@@ -201,19 +201,19 @@ class MediaDialogViewBrowserTest : public InProcessBrowserTest {
void StartPlayback() { void StartPlayback() {
// The test HTML files used in these tests contain "play()" functions that // The test HTML files used in these tests contain "play()" functions that
// play the video. // play the video.
GetWebContents()->GetMainFrame()->ExecuteJavaScriptForTests( GetActiveWebContents()->GetMainFrame()->ExecuteJavaScriptForTests(
base::ASCIIToUTF16("play()"), base::NullCallback()); base::ASCIIToUTF16("play()"), base::NullCallback());
} }
void WaitForStart() { void WaitForStart() {
content::MediaStartStopObserver observer( content::MediaStartStopObserver observer(
GetWebContents(), content::MediaStartStopObserver::Type::kStart); GetActiveWebContents(), content::MediaStartStopObserver::Type::kStart);
observer.Wait(); observer.Wait();
} }
void WaitForStop() { void WaitForStop() {
content::MediaStartStopObserver observer( content::MediaStartStopObserver observer(
GetWebContents(), content::MediaStartStopObserver::Type::kStop); GetActiveWebContents(), content::MediaStartStopObserver::Type::kStop);
observer.Wait(); observer.Wait();
} }
...@@ -240,6 +240,18 @@ class MediaDialogViewBrowserTest : public InProcessBrowserTest { ...@@ -240,6 +240,18 @@ class MediaDialogViewBrowserTest : public InProcessBrowserTest {
ClickButton(GetButtonForAction(MediaSessionAction::kPlay)); ClickButton(GetButtonForAction(MediaSessionAction::kPlay));
} }
void ClickNotificationByTitle(const base::string16& title) {
ASSERT_TRUE(MediaDialogView::IsShowing());
MediaNotificationContainerImplView* notification =
GetNotificationByTitle(title);
ASSERT_NE(nullptr, notification);
ClickButton(notification);
}
content::WebContents* GetActiveWebContents() {
return browser()->tab_strip_model()->GetActiveWebContents();
}
private: private:
void ClickButton(views::Button* button) { void ClickButton(views::Button* button) {
base::RunLoop closure_loop; base::RunLoop closure_loop;
...@@ -275,8 +287,18 @@ class MediaDialogViewBrowserTest : public InProcessBrowserTest { ...@@ -275,8 +287,18 @@ class MediaDialogViewBrowserTest : public InProcessBrowserTest {
return nullptr; return nullptr;
} }
content::WebContents* GetWebContents() { // Finds a MediaNotificationContainerImplView by title.
return browser()->tab_strip_model()->GetActiveWebContents(); MediaNotificationContainerImplView* GetNotificationByTitle(
const base::string16& title) {
for (const auto notification_pair :
MediaDialogView::GetDialogViewForTesting()
->GetNotificationsForTesting()) {
const media_message_center::MediaNotificationView* view =
notification_pair.second->view_for_testing();
if (view->title_label_for_testing()->GetText() == title)
return notification_pair.second;
}
return nullptr;
} }
base::test::ScopedFeatureList feature_list_; base::test::ScopedFeatureList feature_list_;
...@@ -350,3 +372,39 @@ IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest, ShowsMultipleMediaSessions) { ...@@ -350,3 +372,39 @@ IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest, ShowsMultipleMediaSessions) {
WaitForDialogToContainText(base::ASCIIToUTF16("Different Title")); WaitForDialogToContainText(base::ASCIIToUTF16("Different Title"));
WaitForDialogToContainText(base::ASCIIToUTF16("Another Artist")); WaitForDialogToContainText(base::ASCIIToUTF16("Another Artist"));
} }
IN_PROC_BROWSER_TEST_F(MediaDialogViewBrowserTest,
ClickingOnNotificationGoesBackToTab) {
// Open a tab and play media.
OpenTestURL();
StartPlayback();
WaitForStart();
// Pointer to the first tab.
content::WebContents* first_web_contents = GetActiveWebContents();
// Open another tab and play different media.
OpenDifferentMetadataURLInNewTab();
StartPlayback();
WaitForStart();
// Now the active web contents is the second tab.
content::WebContents* second_web_contents = GetActiveWebContents();
ASSERT_NE(first_web_contents, second_web_contents);
// Open the media dialog.
ClickToolbarIcon();
WaitForDialogOpened();
EXPECT_TRUE(IsDialogVisible());
// Wait for the dialog to be populated.
WaitForDialogToContainText(base::ASCIIToUTF16("Big Buck Bunny"));
WaitForDialogToContainText(base::ASCIIToUTF16("Different Title"));
// The second tab should be the active tab.
EXPECT_EQ(second_web_contents, GetActiveWebContents());
// Clicking the first notification should make the first tab active.
ClickNotificationByTitle(base::ASCIIToUTF16("Big Buck Bunny"));
EXPECT_EQ(first_web_contents, GetActiveWebContents());
}
...@@ -57,12 +57,16 @@ class MediaNotificationContainerImplView::DismissButton ...@@ -57,12 +57,16 @@ class MediaNotificationContainerImplView::DismissButton
MediaNotificationContainerImplView::MediaNotificationContainerImplView( MediaNotificationContainerImplView::MediaNotificationContainerImplView(
const std::string& id, const std::string& id,
base::WeakPtr<media_message_center::MediaNotificationItem> item) base::WeakPtr<media_message_center::MediaNotificationItem> item)
: id_(id), : views::Button(this),
id_(id),
foreground_color_(kDefaultForegroundColor), foreground_color_(kDefaultForegroundColor),
background_color_(kDefaultBackgroundColor) { background_color_(kDefaultBackgroundColor) {
SetLayoutManager(std::make_unique<views::FillLayout>()); SetLayoutManager(std::make_unique<views::FillLayout>());
SetPreferredSize(kNormalSize); SetPreferredSize(kNormalSize);
set_notify_enter_exit_on_child(true); set_notify_enter_exit_on_child(true);
SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
SetTooltipText(
l10n_util::GetStringUTF16(IDS_GLOBAL_MEDIA_CONTROLS_BACK_TO_TAB));
swipeable_container_ = std::make_unique<views::View>(); swipeable_container_ = std::make_unique<views::View>();
swipeable_container_->set_owned_by_client(); swipeable_container_->set_owned_by_client();
...@@ -179,8 +183,14 @@ void MediaNotificationContainerImplView::OnSlideOut() { ...@@ -179,8 +183,14 @@ void MediaNotificationContainerImplView::OnSlideOut() {
void MediaNotificationContainerImplView::ButtonPressed(views::Button* sender, void MediaNotificationContainerImplView::ButtonPressed(views::Button* sender,
const ui::Event& event) { const ui::Event& event) {
DCHECK_EQ(dismiss_button_, sender); if (sender == dismiss_button_) {
DismissNotification(); DismissNotification();
} else if (sender == this) {
for (auto& observer : observers_)
observer.OnContainerClicked(id_);
} else {
NOTREACHED();
}
} }
void MediaNotificationContainerImplView::AddObserver( void MediaNotificationContainerImplView::AddObserver(
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#include "ui/views/animation/slide_out_controller_delegate.h" #include "ui/views/animation/slide_out_controller_delegate.h"
#include "ui/views/controls/button/button.h" #include "ui/views/controls/button/button.h"
#include "ui/views/focus/focus_manager.h" #include "ui/views/focus/focus_manager.h"
#include "ui/views/view.h"
namespace media_message_center { namespace media_message_center {
class MediaNotificationItem; class MediaNotificationItem;
...@@ -31,7 +30,7 @@ class MediaNotificationContainerObserver; ...@@ -31,7 +30,7 @@ class MediaNotificationContainerObserver;
// within the MediaDialogView. The media notification shows metadata for a media // within the MediaDialogView. The media notification shows metadata for a media
// session and can control playback. // session and can control playback.
class MediaNotificationContainerImplView class MediaNotificationContainerImplView
: public views::View, : public views::Button,
public media_message_center::MediaNotificationContainer, public media_message_center::MediaNotificationContainer,
public MediaNotificationContainerImpl, public MediaNotificationContainerImpl,
public views::SlideOutControllerDelegate, public views::SlideOutControllerDelegate,
...@@ -43,7 +42,7 @@ class MediaNotificationContainerImplView ...@@ -43,7 +42,7 @@ class MediaNotificationContainerImplView
base::WeakPtr<media_message_center::MediaNotificationItem> item); base::WeakPtr<media_message_center::MediaNotificationItem> item);
~MediaNotificationContainerImplView() override; ~MediaNotificationContainerImplView() override;
// views::View: // views::Button:
void AddedToWidget() override; void AddedToWidget() override;
void RemovedFromWidget() override; void RemovedFromWidget() override;
void OnMouseEntered(const ui::MouseEvent& event) override; void OnMouseEntered(const ui::MouseEvent& event) override;
......
...@@ -35,6 +35,7 @@ class MockMediaNotificationContainerObserver ...@@ -35,6 +35,7 @@ class MockMediaNotificationContainerObserver
// MediaNotificationContainerObserver implementation. // MediaNotificationContainerObserver implementation.
MOCK_METHOD1(OnContainerExpanded, void(bool expanded)); MOCK_METHOD1(OnContainerExpanded, void(bool expanded));
MOCK_METHOD0(OnContainerMetadataChanged, void()); MOCK_METHOD0(OnContainerMetadataChanged, void());
MOCK_METHOD1(OnContainerClicked, void(const std::string& id));
MOCK_METHOD1(OnContainerDismissed, void(const std::string& id)); MOCK_METHOD1(OnContainerDismissed, void(const std::string& id));
MOCK_METHOD1(OnContainerDestroyed, void(const std::string& id)); MOCK_METHOD1(OnContainerDestroyed, void(const std::string& id));
...@@ -124,6 +125,12 @@ class MediaNotificationContainerImplViewTest : public views::ViewsTestBase { ...@@ -124,6 +125,12 @@ class MediaNotificationContainerImplViewTest : public views::ViewsTestBase {
notification_container_->OnMouseExited(event); notification_container_->OnMouseExited(event);
} }
void SimulateContainerClicked() {
ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), 0, 0);
views::test::ButtonTestApi(notification_container_).NotifyClick(event);
}
void SimulateDismissButtonClicked() { void SimulateDismissButtonClicked() {
ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(), ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), 0, 0); ui::EventTimeForNow(), 0, 0);
...@@ -346,3 +353,8 @@ TEST_F(MediaNotificationContainerImplViewTest, SendsDestroyedUpdates) { ...@@ -346,3 +353,8 @@ TEST_F(MediaNotificationContainerImplViewTest, SendsDestroyedUpdates) {
container.reset(); container.reset();
testing::Mock::VerifyAndClearExpectations(&observer); testing::Mock::VerifyAndClearExpectations(&observer);
} }
TEST_F(MediaNotificationContainerImplViewTest, SendsClicks) {
EXPECT_CALL(observer(), OnContainerClicked(kTestNotificationId));
SimulateContainerClicked();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment