Commit 45f5dc85 authored by Muyao Xu's avatar Muyao Xu Committed by Commit Bot

[Cast+Zenith] Add a "stop casting" button to the GMC dialog

A "stop casting" button will show up for cast media notifications, which can be used to stop the Cast session

Bug: b/161612403, 1107161
Change-Id: I677f32383e0ce213483333dcddf94c36bc4f2b50
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2444509
Commit-Queue: Muyao Xu <muyaoxu@google.com>
Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Cr-Commit-Position: refs/heads/master@{#814416}
parent 8d90c018
......@@ -13,4 +13,7 @@
<message name="IDS_GLOBAL_MEDIA_CONTROLS_DEVICES_BUTTON_LABEL" desc="Label for a button that opens a menu for picking an audio device to play from.">
Devices
</message>
<message name="IDS_GLOBAL_MEDIA_CONTROLS_STOP_CASTING_BUTTON_LABEL" desc="Label for a button that stops the current cast session.">
Stop casting
</message>
</grit-part>
......@@ -154,7 +154,8 @@ CastMediaNotificationItem::CastMediaNotificationItem(
profile,
base::BindRepeating(&CastMediaNotificationItem::ImageChanged,
base::Unretained(this))),
session_info_(CreateSessionInfo()) {
session_info_(CreateSessionInfo()),
profile_(profile) {
metadata_.source_title = GetSourceTitle(route);
base::UmaHistogramEnumeration(
kSourceHistogramName, route.is_local() ? Source::kLocalCastSession
......
......@@ -65,6 +65,9 @@ class CastMediaNotificationItem
mojo::PendingRemote<media_router::mojom::MediaStatusObserver>
GetObserverPendingRemote();
const media_router::MediaRoute::Id route_id() { return media_route_id_; }
Profile* profile() { return profile_; }
base::WeakPtr<CastMediaNotificationItem> GetWeakPtr() {
return weak_ptr_factory_.GetWeakPtr();
}
......@@ -128,6 +131,7 @@ class CastMediaNotificationItem
media_session::mojom::MediaSessionInfoPtr session_info_;
mojo::Receiver<media_router::mojom::MediaStatusObserver> observer_receiver_{
this};
Profile* profile_;
base::WeakPtrFactory<CastMediaNotificationItem> weak_ptr_factory_{this};
};
......
......@@ -7,6 +7,7 @@
#include "base/feature_list.h"
#include "base/metrics/field_trial_params.h"
#include "chrome/browser/media/router/media_router_feature.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/global_media_controls/media_notification_container_impl.h"
#include "chrome/browser/ui/global_media_controls/media_notification_container_observer.h"
#include "chrome/browser/ui/global_media_controls/media_notification_service.h"
......@@ -15,6 +16,8 @@
#include "chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.h"
#include "chrome/grit/generated_resources.h"
#include "components/media_message_center/media_notification_view_modern_impl.h"
#include "components/media_router/browser/media_router.h"
#include "components/media_router/browser/media_router_factory.h"
#include "components/vector_icons/vector_icons.h"
#include "media/audio/audio_device_description.h"
#include "ui/base/l10n/l10n_util.h"
......@@ -43,6 +46,9 @@ constexpr int kDismissButtonBackgroundRadius = 15;
constexpr SkColor kDefaultForegroundColor = SK_ColorBLACK;
constexpr SkColor kDefaultBackgroundColor = SK_ColorTRANSPARENT;
constexpr float kDragImageOpacity = 0.7f;
constexpr gfx::Insets kStopCastButtonStripInsets{6, 15};
constexpr gfx::Size kStopCastButtonStripSize{400, 30};
constexpr gfx::Insets kStopCastButtonBorderInsets{4, 8};
// The minimum number of enabled and visible user actions such that we should
// force the MediaNotificationView to be expanded.
......@@ -119,6 +125,9 @@ MediaNotificationContainerImplView::MediaNotificationContainerImplView(
UpdateDismissButtonIcon();
bool is_cast_notification = item ? item->SourceIsCast() : false;
if (is_cast_notification) {
cast_item_ = static_cast<CastMediaNotificationItem*>(item.get());
}
std::unique_ptr<media_message_center::MediaNotificationView> view;
if (base::FeatureList::IsEnabled(media::kGlobalMediaControlsModernUI)) {
......@@ -135,6 +144,37 @@ MediaNotificationContainerImplView::MediaNotificationContainerImplView(
}
view_ = swipeable_container_->AddChildView(std::move(view));
if (is_cast_notification &&
media_router::GlobalMediaControlsCastStartStopEnabled()) {
stop_button_strip_ = AddChildView(std::make_unique<views::View>());
auto* stop_cast_button_strip_layout =
stop_button_strip_->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal,
kStopCastButtonStripInsets));
stop_cast_button_strip_layout->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kStart);
stop_cast_button_strip_layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
stop_button_strip_->SetBackground(
views::CreateSolidBackground(background_color_));
stop_button_strip_->SetPreferredSize(kStopCastButtonStripSize);
stop_cast_button_ =
stop_button_strip_->AddChildView(std::make_unique<views::LabelButton>(
this, l10n_util::GetStringUTF16(
IDS_GLOBAL_MEDIA_CONTROLS_STOP_CASTING_BUTTON_LABEL)));
stop_cast_button_->SetInkDropMode(InkDropMode::ON);
stop_cast_button_->SetHasInkDropActionOnClick(true);
stop_cast_button_->SetInkDropBaseColor(foreground_color_);
stop_cast_button_->SetInkDropLargeCornerRadius(
kStopCastButtonStripSize.height());
stop_cast_button_->SetEnabledTextColors(foreground_color_);
stop_cast_button_->SetFocusBehavior(FocusBehavior::ALWAYS);
stop_cast_button_->SetBorder(views::CreatePaddedBorder(
views::CreateRoundedRectBorder(1, kStopCastButtonStripSize.height() / 2,
foreground_color_),
kStopCastButtonBorderInsets));
}
if (base::FeatureList::IsEnabled(
media::kGlobalMediaControlsSeamlessTransfer) &&
......@@ -347,10 +387,19 @@ void MediaNotificationContainerImplView::OnColorsChanged(SkColor foreground,
if (foreground_color_ != foreground) {
foreground_color_ = foreground;
UpdateDismissButtonIcon();
if (stop_cast_button_) {
stop_cast_button_->SetEnabledTextColors(foreground_color_);
stop_cast_button_->SetInkDropBaseColor(foreground_color_);
}
}
if (background_color_ != background) {
background_color_ = background;
UpdateDismissButtonBackground();
if (stop_button_strip_) {
stop_button_strip_->SetBackground(
views::CreateSolidBackground(background_color_));
}
}
if (audio_device_selector_view_)
audio_device_selector_view_->OnColorsChanged(foreground, background);
......@@ -404,6 +453,11 @@ void MediaNotificationContainerImplView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
if (sender == dismiss_button_) {
DismissNotification();
} else if (sender == stop_cast_button_) {
media_router::MediaRouter* router =
media_router::MediaRouterFactory::GetApiForBrowserContext(
cast_item_->profile());
router->TerminateRoute(cast_item_->route_id());
} else if (sender == this) {
// If |is_dragging_| is set, this click should be treated as a drag and not
// fire the |OnContainerClicked()| event.
......@@ -448,6 +502,11 @@ MediaNotificationContainerImplView::GetDismissButtonForTesting() {
return dismiss_button_;
}
views::Button*
MediaNotificationContainerImplView::GetStopCastingButtonForTesting() {
return stop_cast_button_;
}
void MediaNotificationContainerImplView::UpdateDismissButtonIcon() {
views::SetImageFromVectorIconWithColor(
dismiss_button_, vector_icons::kCloseRoundedIcon, kDismissButtonIconSize,
......
......@@ -9,6 +9,7 @@
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "chrome/browser/ui/global_media_controls/cast_media_notification_item.h"
#include "chrome/browser/ui/global_media_controls/media_notification_container_impl.h"
#include "chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view_delegate.h"
#include "chrome/browser/ui/views/global_media_controls/overlay_media_notification_view.h"
......@@ -26,6 +27,7 @@ class MediaNotificationItem;
} // namespace media_message_center
namespace views {
class LabelButton;
class ImageButton;
class SlideOutController;
} // namespace views
......@@ -119,6 +121,7 @@ class MediaNotificationContainerImplView
const base::string16& GetTitle();
views::ImageButton* GetDismissButtonForTesting();
views::Button* GetStopCastingButtonForTesting();
media_message_center::MediaNotificationViewImpl* view_for_testing() {
DCHECK(!base::FeatureList::IsEnabled(media::kGlobalMediaControlsModernUI));
......@@ -174,6 +177,10 @@ class MediaNotificationContainerImplView
media_message_center::MediaNotificationView* view_ = nullptr;
MediaNotificationDeviceSelectorView* audio_device_selector_view_ = nullptr;
// Only shows up for cast notifications.
views::View* stop_button_strip_ = nullptr;
views::LabelButton* stop_cast_button_ = nullptr;
SkColor foreground_color_;
SkColor background_color_;
......@@ -214,6 +221,8 @@ class MediaNotificationContainerImplView
MediaNotificationService* const service_;
CastMediaNotificationItem* cast_item_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(MediaNotificationContainerImplView);
};
......
......@@ -8,8 +8,13 @@
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "chrome/browser/media/router/media_router_feature.h"
#include "chrome/browser/ui/global_media_controls/media_notification_container_observer.h"
#include "chrome/test/base/testing_profile.h"
#include "chrome/test/views/chrome_views_test_base.h"
#include "components/media_message_center/media_notification_controller.h"
#include "components/media_router/browser/media_router_factory.h"
#include "components/media_router/browser/test/mock_media_router.h"
#include "media/base/media_switches.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -28,6 +33,7 @@ namespace {
const char kTestNotificationId[] = "testid";
const char kOtherTestNotificationId[] = "othertestid";
const char kRouteId[] = "route_id";
class MockMediaNotificationContainerObserver
: public MediaNotificationContainerObserver {
......@@ -51,6 +57,43 @@ class MockMediaNotificationContainerObserver
DISALLOW_COPY_AND_ASSIGN(MockMediaNotificationContainerObserver);
};
media_router::MediaRoute CreateMediaRoute() {
media_router::MediaRoute route(kRouteId,
media_router::MediaSource("source_id"),
"sink_id", "route_description",
/* is_local */ true, /* for_display */ true);
route.set_media_sink_name("sink_name");
return route;
}
class MockSessionController : public CastMediaSessionController {
public:
MockSessionController(
mojo::Remote<media_router::mojom::MediaController> remote)
: CastMediaSessionController(std::move(remote)) {}
MOCK_METHOD1(Send, void(media_session::mojom::MediaSessionAction));
MOCK_METHOD1(OnMediaStatusUpdated, void(media_router::mojom::MediaStatusPtr));
};
class MockMediaNotificationController
: public media_message_center::MediaNotificationController {
public:
MockMediaNotificationController() = default;
~MockMediaNotificationController() = default;
MOCK_METHOD(void, ShowNotification, (const std::string&));
MOCK_METHOD(void, HideNotification, (const std::string&));
MOCK_METHOD(void, RemoveItem, (const std::string&));
MOCK_METHOD(scoped_refptr<base::SequencedTaskRunner>,
GetTaskRunner,
(),
(const));
MOCK_METHOD(void,
LogMediaSessionActionButtonPressed,
(const std::string&, MediaSessionAction));
};
} // anonymous namespace
class MediaNotificationContainerImplViewTest : public ChromeViewsTestBase {
......@@ -61,12 +104,16 @@ class MediaNotificationContainerImplViewTest : public ChromeViewsTestBase {
// ViewsTestBase:
void SetUp() override {
ViewsTestBase::SetUp();
SetUpCommon(std::make_unique<MediaNotificationContainerImplView>(
kTestNotificationId, nullptr, nullptr));
}
void SetUpCommon(std::unique_ptr<MediaNotificationContainerImplView>
notification_container) {
widget_ = CreateTestWidget();
notification_container_ = widget_->SetContentsView(
std::make_unique<MediaNotificationContainerImplView>(
kTestNotificationId, nullptr, nullptr));
notification_container_ =
widget_->SetContentsView(std::move(notification_container));
observer_ = std::make_unique<MockMediaNotificationContainerObserver>();
notification_container_->AddObserver(observer_.get());
......@@ -290,6 +337,69 @@ class MediaNotificationContainerImplViewOverlayControlsTest
base::test::ScopedFeatureList feature_list_;
};
// TODO(b/161612403): Remove this class once
// |media_router::kGlobalMediaControlsCastStartStop| is enabled by default.
class MediaNotificationContainerImplViewCastTest
: public MediaNotificationContainerImplViewTest {
public:
void SetUp() override {
ViewsTestBase::SetUp();
feature_list_.InitWithFeatures(
{media::kGlobalMediaControlsForCast,
media_router::kGlobalMediaControlsCastStartStop,
media::kGlobalMediaControlsOverlayControls},
{});
media_router::MediaRouterFactory::GetInstance()->SetTestingFactory(
&profile_, base::BindRepeating(&media_router::MockMediaRouter::Create));
auto session_controller = std::make_unique<MockSessionController>(
mojo::Remote<media_router::mojom::MediaController>());
session_controller_ = session_controller.get();
item_ = std::make_unique<CastMediaNotificationItem>(
CreateMediaRoute(), &notification_controller_,
std::move(session_controller), &profile_);
SetUpCommon(std::make_unique<MediaNotificationContainerImplView>(
kTestNotificationId, item_->GetWeakPtr(), nullptr));
}
void TearDown() override {
// Delete |item_| before |notification_controller_|.
item_.reset();
MediaNotificationContainerImplViewTest::TearDown();
}
void SimulateStopCastingButtonClicked() {
ui::MouseEvent event(ui::ET_MOUSE_PRESSED, gfx::Point(), gfx::Point(),
ui::EventTimeForNow(), 0, 0);
views::test::ButtonTestApi(
notification_container()->GetStopCastingButtonForTesting())
.NotifyClick(event);
}
CastMediaNotificationItem* item() { return item_.get(); }
Profile* profile() { return &profile_; }
MockMediaNotificationController* notification_controller() {
return &notification_controller_;
}
private:
base::test::ScopedFeatureList feature_list_;
TestingProfile profile_;
std::unique_ptr<CastMediaNotificationItem> item_;
MockMediaNotificationController notification_controller_;
MockSessionController* session_controller_ = nullptr;
};
TEST_F(MediaNotificationContainerImplViewCastTest, StopCasting) {
auto* mock_router = static_cast<media_router::MockMediaRouter*>(
media_router::MediaRouterFactory::GetApiForBrowserContext(profile()));
EXPECT_CALL(*mock_router, TerminateRoute(kRouteId));
SimulateStopCastingButtonClicked();
}
TEST_F(MediaNotificationContainerImplViewTest, SwipeToDismiss) {
EXPECT_CALL(observer(), OnContainerDismissed(kTestNotificationId));
SimulateNotificationSwipedToDismiss();
......
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