Commit df36d379 authored by Takumi Fujimoto's avatar Takumi Fujimoto Committed by Commit Bot

Show Global Media Controls button when there are Cast sessions

This CL makes the GMC toolbar button appear when there is an active
Cast session. GMC dialog contents for Cast will be added in a future CL.

The functionality is put behind a kGlobalMediaControlsForCast feature
flag.

Bug: 987479
Change-Id: Ie97f8d1e9ca6cd94fc982d2d60c490aa777499d5
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1873200
Commit-Queue: Takumi Fujimoto <takumif@chromium.org>
Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Reviewed-by: default avatarPeter Boström <pbos@chromium.org>
Reviewed-by: default avatarMounir Lamouri <mlamouri@chromium.org>
Cr-Commit-Position: refs/heads/master@{#710158}
parent 3b57a5e8
......@@ -64,13 +64,47 @@ void MediaToolbarButtonController::Session::WebContentsDestroyed() {
owner_->RemoveItem(id_);
}
MediaToolbarButtonController::MediaRoutesObserver::MediaRoutesObserver(
media_router::MediaRouter* router,
base::RepeatingClosure routes_changed_callback)
: media_router::MediaRoutesObserver(router),
routes_changed_callback_(std::move(routes_changed_callback)) {}
MediaToolbarButtonController::MediaRoutesObserver::~MediaRoutesObserver() =
default;
void MediaToolbarButtonController::MediaRoutesObserver::OnRoutesUpdated(
const std::vector<media_router::MediaRoute>& routes,
const std::vector<media_router::MediaRoute::Id>& joinable_route_ids) {
bool has_routes_now =
std::find_if(routes.begin(), routes.end(),
[](const media_router::MediaRoute& route) {
return route.for_display() &&
route.controller_type() ==
media_router::RouteControllerType::kGeneric;
}) != routes.end();
if (has_routes_ != has_routes_now) {
has_routes_ = has_routes_now;
routes_changed_callback_.Run();
}
}
MediaToolbarButtonController::MediaToolbarButtonController(
const base::UnguessableToken& source_id,
service_manager::Connector* connector,
MediaToolbarButtonControllerDelegate* delegate)
MediaToolbarButtonControllerDelegate* delegate,
media_router::MediaRouter* media_router)
: connector_(connector), delegate_(delegate) {
DCHECK(delegate_);
if (media_router) {
media_routes_observer_ = std::make_unique<MediaRoutesObserver>(
media_router,
base::BindRepeating(
&MediaToolbarButtonController::UpdateToolbarButtonState,
base::Unretained(this)));
}
// |connector| can be null in tests.
if (!connector_)
return;
......@@ -281,7 +315,8 @@ void MediaToolbarButtonController::OnReceivedAudioFocusRequests(
}
void MediaToolbarButtonController::UpdateToolbarButtonState() {
if (!active_controllable_session_ids_.empty()) {
if (!active_controllable_session_ids_.empty() ||
(media_routes_observer_ && media_routes_observer_->has_routes())) {
if (delegate_display_state_ != DisplayState::kShown) {
delegate_->Enable();
delegate_->Show();
......
......@@ -11,6 +11,7 @@
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/media/router/media_routes_observer.h"
#include "chrome/browser/ui/global_media_controls/media_notification_container_observer.h"
#include "components/media_message_center/media_notification_controller.h"
#include "content/public/browser/web_contents_observer.h"
......@@ -27,6 +28,10 @@ namespace media_message_center {
class MediaNotificationItem;
} // namespace media_message_center
namespace media_router {
class MediaRouter;
} // namespace media_router
namespace service_manager {
class Connector;
} // namespace service_manager
......@@ -45,7 +50,8 @@ class MediaToolbarButtonController
public:
MediaToolbarButtonController(const base::UnguessableToken& source_id,
service_manager::Connector* connector,
MediaToolbarButtonControllerDelegate* delegate);
MediaToolbarButtonControllerDelegate* delegate,
media_router::MediaRouter* media_router);
~MediaToolbarButtonController() override;
// media_session::mojom::AudioFocusObserver implementation.
......@@ -86,6 +92,8 @@ class MediaToolbarButtonController
const std::string& id,
std::unique_ptr<media_message_center::MediaNotificationItem> item,
content::WebContents* web_contents);
Session(const Session&) = delete;
Session& operator=(const Session&) = delete;
~Session() override;
// content::WebContentsObserver implementation.
......@@ -97,8 +105,29 @@ class MediaToolbarButtonController
MediaToolbarButtonController* owner_;
const std::string id_;
std::unique_ptr<media_message_center::MediaNotificationItem> item_;
};
class MediaRoutesObserver : public media_router::MediaRoutesObserver {
public:
MediaRoutesObserver(media_router::MediaRouter* router,
base::RepeatingClosure routes_changed_callback);
MediaRoutesObserver(const MediaRoutesObserver&) = delete;
MediaRoutesObserver& operator=(const MediaRoutesObserver&) = delete;
~MediaRoutesObserver() override;
DISALLOW_COPY_AND_ASSIGN(Session);
// media_router::MediaRoutesObserver implementation.
void OnRoutesUpdated(const std::vector<media_router::MediaRoute>& routes,
const std::vector<media_router::MediaRoute::Id>&
joinable_route_ids) override;
bool has_routes() const { return has_routes_; }
private:
bool has_routes_ = false;
// Called when the presence of media routes has changed.
base::RepeatingClosure routes_changed_callback_;
};
void OnReceivedAudioFocusRequests(
......@@ -137,6 +166,9 @@ class MediaToolbarButtonController
mojo::Receiver<media_session::mojom::AudioFocusObserver>
audio_focus_observer_receiver_{this};
// Observes for active Cast sessions that can be controlled.
std::unique_ptr<MediaRoutesObserver> media_routes_observer_;
base::WeakPtrFactory<MediaToolbarButtonController> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(MediaToolbarButtonController);
......
......@@ -8,12 +8,13 @@
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/task_environment.h"
#include "base/unguessable_token.h"
#include "chrome/browser/media/router/test/mock_media_router.h"
#include "chrome/browser/ui/global_media_controls/media_dialog_delegate.h"
#include "chrome/browser/ui/global_media_controls/media_toolbar_button_controller_delegate.h"
#include "components/media_message_center/media_notification_item.h"
#include "components/media_message_center/media_notification_util.h"
#include "content/public/test/browser_task_environment.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
#include "testing/gmock/include/gmock/gmock.h"
......@@ -84,7 +85,7 @@ class MediaToolbarButtonControllerTest : public testing::Test {
void SetUp() override {
controller_ = std::make_unique<MediaToolbarButtonController>(
base::UnguessableToken::Create(), nullptr, &delegate_);
base::UnguessableToken::Create(), nullptr, &delegate_, &media_router_);
}
protected:
......@@ -174,11 +175,17 @@ class MediaToolbarButtonControllerTest : public testing::Test {
media_message_center::kCountHistogramName, count, size);
}
void SimulateMediaRoutesUpdate(
const std::vector<media_router::MediaRoute>& routes) {
controller_->media_routes_observer_->OnRoutesUpdated(routes, {});
}
MockMediaToolbarButtonControllerDelegate& delegate() { return delegate_; }
private:
base::test::TaskEnvironment task_environment_;
content::BrowserTaskEnvironment task_environment_;
MockMediaToolbarButtonControllerDelegate delegate_;
media_router::MockMediaRouter media_router_;
std::unique_ptr<MediaToolbarButtonController> controller_;
base::HistogramTester histogram_tester_;
......@@ -399,3 +406,18 @@ TEST_F(MediaToolbarButtonControllerTest, DismissesMediaSession) {
SimulateDismissButtonClicked(id);
testing::Mock::VerifyAndClearExpectations(&delegate());
}
TEST_F(MediaToolbarButtonControllerTest, ShowButtonForCastSession) {
media_router::MediaRoute media_route("id",
media_router::MediaSource("source_id"),
"sink_id", "description", true, true);
media_route.set_controller_type(media_router::RouteControllerType::kGeneric);
EXPECT_CALL(delegate(), Enable());
EXPECT_CALL(delegate(), Show());
SimulateMediaRoutesUpdate({media_route});
testing::Mock::VerifyAndClearExpectations(&delegate());
EXPECT_CALL(delegate(), Hide());
SimulateMediaRoutesUpdate({});
}
......@@ -6,6 +6,9 @@
#include "build/build_config.h"
#include "chrome/app/vector_icons/vector_icons.h"
#include "chrome/browser/media/router/media_router_factory.h"
#include "chrome/browser/media/router/media_router_feature.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/themes/theme_properties.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h"
......@@ -14,6 +17,8 @@
#include "chrome/browser/ui/views/feature_promos/global_media_controls_promo_controller.h"
#include "chrome/browser/ui/views/global_media_controls/media_dialog_view.h"
#include "chrome/grit/generated_resources.h"
#include "components/vector_icons/vector_icons.h"
#include "media/base/media_switches.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/material_design/material_design_controller.h"
#include "ui/base/theme_provider.h"
......@@ -23,15 +28,30 @@
#include "ui/views/animation/ink_drop.h"
#include "ui/views/controls/button/button_controller.h"
namespace {
media_router::MediaRouter* GetMediaRouter(Profile* profile) {
bool supports_cast =
base::FeatureList::IsEnabled(media::kGlobalMediaControlsForCast) &&
media_router::MediaRouterEnabled(profile);
if (!supports_cast)
return nullptr;
return media_router::MediaRouterFactory::GetApiForBrowserContext(profile);
}
} // namespace
MediaToolbarButtonView::MediaToolbarButtonView(
const base::UnguessableToken& source_id,
service_manager::Connector* connector,
const Browser* browser)
: ToolbarButton(this),
connector_(connector),
controller_(std::make_unique<MediaToolbarButtonController>(source_id,
connector_,
this)),
controller_(std::make_unique<MediaToolbarButtonController>(
source_id,
connector_,
this,
GetMediaRouter(browser->profile()))),
browser_(browser) {
GlobalMediaControlsInProductHelp* in_product_help =
GlobalMediaControlsInProductHelpFactory::GetForProfile(
......
......@@ -312,6 +312,11 @@ const base::Feature kFallbackAfterDecodeError{"FallbackAfterDecodeError",
const base::Feature kGlobalMediaControls{"GlobalMediaControls",
base::FEATURE_DISABLED_BY_DEFAULT};
// Show Cast sessions in Global Media Controls. It is no-op if
// kGlobalMediaControls is not enabled.
const base::Feature kGlobalMediaControlsForCast{
"GlobalMediaControlsForCast", base::FEATURE_DISABLED_BY_DEFAULT};
// Enable new cpu load estimator. Intended for evaluation in local
// testing and origin-trial.
// TODO(nisse): Delete once we have switched over to always using the
......
......@@ -112,6 +112,7 @@ MEDIA_EXPORT extern const base::Feature kFFmpegDecodeOpaqueVP8;
MEDIA_EXPORT extern const base::Feature kFailUrlProvisionFetcherForTesting;
MEDIA_EXPORT extern const base::Feature kFallbackAfterDecodeError;
MEDIA_EXPORT extern const base::Feature kGlobalMediaControls;
MEDIA_EXPORT extern const base::Feature kGlobalMediaControlsForCast;
MEDIA_EXPORT extern const base::Feature kHardwareMediaKeyHandling;
MEDIA_EXPORT extern const base::Feature kHardwareSecureDecryption;
MEDIA_EXPORT extern const base::Feature kInternalMediaSession;
......
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