Commit 6fd4f25d authored by Tommy Steimel's avatar Tommy Steimel Committed by Commit Bot

GMC: Combine MediaToolbarButtonController and MediaDialogController

While working on crrev.com/c/1771937 to split by profile, it was clear
that MediaToolbarButtonController was going to have to be refactored to
be an AudioFocusObserver and would essentially have to do the same
tracking that MediaDialogController currently does. Instead of having
this tracking in two places, I've instead refactored the two
controllers into one.

Bug: 992463
Change-Id: Ie424044e8ee678effe869a131347ba7869157624
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1772421Reviewed-by: default avatarBecca Hughes <beccahughes@chromium.org>
Commit-Queue: Tommy Steimel <steimel@chromium.org>
Cr-Commit-Position: refs/heads/master@{#690769}
parent 70207f3b
...@@ -929,10 +929,8 @@ jumbo_split_static_library("ui") { ...@@ -929,10 +929,8 @@ jumbo_split_static_library("ui") {
"global_error/global_error_service.h", "global_error/global_error_service.h",
"global_error/global_error_service_factory.cc", "global_error/global_error_service_factory.cc",
"global_error/global_error_service_factory.h", "global_error/global_error_service_factory.h",
"global_media_controls/media_dialog_controller.cc", "global_media_controls/media_dialog_delegate.cc",
"global_media_controls/media_dialog_controller.h", "global_media_controls/media_dialog_delegate.h",
"global_media_controls/media_dialog_controller_delegate.cc",
"global_media_controls/media_dialog_controller_delegate.h",
"global_media_controls/media_toolbar_button_controller.cc", "global_media_controls/media_toolbar_button_controller.cc",
"global_media_controls/media_toolbar_button_controller.h", "global_media_controls/media_toolbar_button_controller.h",
"global_media_controls/media_toolbar_button_controller_delegate.cc", "global_media_controls/media_toolbar_button_controller_delegate.cc",
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/global_media_controls/media_dialog_controller.h"
#include "base/bind.h"
#include "base/containers/adapters.h"
#include "chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.h"
#include "components/media_message_center/media_notification_item.h"
#include "services/media_session/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
using media_session::mojom::MediaSessionAction;
MediaDialogController::MediaDialogController(
service_manager::Connector* connector,
MediaDialogControllerDelegate* delegate)
: connector_(connector), delegate_(delegate) {
DCHECK(delegate_);
}
MediaDialogController::~MediaDialogController() = default;
void MediaDialogController::Initialize() {
// |connector| can be null in tests.
if (!connector_)
return;
// Connect to the controller manager so we can create media controllers for
// media sessions.
connector_->Connect(media_session::mojom::kServiceName,
controller_manager_remote_.BindNewPipeAndPassReceiver());
// Connect to receive audio focus events.
connector_->Connect(media_session::mojom::kServiceName,
audio_focus_remote_.BindNewPipeAndPassReceiver());
audio_focus_remote_->AddObserver(
audio_focus_observer_receiver_.BindNewPipeAndPassRemote());
audio_focus_remote_->GetFocusRequests(
base::BindOnce(&MediaDialogController::OnReceivedAudioFocusRequests,
weak_ptr_factory_.GetWeakPtr()));
}
void MediaDialogController::OnFocusGained(
media_session::mojom::AudioFocusRequestStatePtr session) {
const std::string id = session->request_id->ToString();
// If we have an existing unfrozen item then this is a duplicate call and
// we should ignore it.
auto it = sessions_.find(id);
if (it != sessions_.end() && !it->second.frozen())
return;
mojo::Remote<media_session::mojom::MediaController> controller;
// |controller_manager_remote_| may be null in tests where connector is
// unavailable.
if (controller_manager_remote_) {
controller_manager_remote_->CreateMediaControllerForSession(
controller.BindNewPipeAndPassReceiver(), *session->request_id);
}
if (it != sessions_.end()) {
// If the notification was previously frozen then we should reset the
// controller because the mojo pipe would have been reset.
it->second.SetController(std::move(controller),
std::move(session->session_info));
} else {
sessions_.emplace(
std::piecewise_construct, std::forward_as_tuple(id),
std::forward_as_tuple(
this, id, session->source_name.value_or(std::string()),
std::move(controller), std::move(session->session_info)));
}
}
void MediaDialogController::OnFocusLost(
media_session::mojom::AudioFocusRequestStatePtr session) {
auto it = sessions_.find(session->request_id->ToString());
if (it == sessions_.end())
return;
it->second.Freeze();
}
void MediaDialogController::ShowNotification(const std::string& id) {
base::WeakPtr<media_message_center::MediaNotificationItem> item;
auto it = sessions_.find(id);
if (it != sessions_.end())
item = it->second.GetWeakPtr();
delegate_->ShowMediaSession(id, item);
}
void MediaDialogController::HideNotification(const std::string& id) {
delegate_->HideMediaSession(id);
}
void MediaDialogController::RemoveItem(const std::string& id) {
sessions_.erase(id);
}
scoped_refptr<base::SequencedTaskRunner> MediaDialogController::GetTaskRunner()
const {
return task_runner_for_testing_;
}
void MediaDialogController::OnReceivedAudioFocusRequests(
std::vector<media_session::mojom::AudioFocusRequestStatePtr> sessions) {
for (auto& session : sessions)
OnFocusGained(std::move(session));
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_CONTROLLER_H_
#define CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_CONTROLLER_H_
#include <vector>
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "components/media_message_center/media_notification_controller.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "services/media_session/public/mojom/media_controller.mojom.h"
namespace media_message_center {
class MediaNotificationItem;
} // namespace media_message_center
namespace service_manager {
class Connector;
} // namespace service_manager
class MediaDialogControllerDelegate;
// Controller for the MediaDialogView that updates the view when the active
// media session changes.
class MediaDialogController
: public media_session::mojom::AudioFocusObserver,
public media_message_center::MediaNotificationController {
public:
MediaDialogController(service_manager::Connector* connector,
MediaDialogControllerDelegate* delegate);
~MediaDialogController() override;
void Initialize();
// media_session::mojom::AudioFocusObserver implementation.
void OnFocusGained(
media_session::mojom::AudioFocusRequestStatePtr session) override;
void OnFocusLost(
media_session::mojom::AudioFocusRequestStatePtr session) override;
// media_message_center::MediaNotificationController implementation.
void ShowNotification(const std::string& id) override;
void HideNotification(const std::string& id) override;
void RemoveItem(const std::string& id) override;
scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() const override;
private:
friend class MediaDialogControllerTest;
// Called when we receive all currently active media sessions for the audio
// focus manager. Used to initialize the list of sessions.
void OnReceivedAudioFocusRequests(
std::vector<media_session::mojom::AudioFocusRequestStatePtr> sessions);
service_manager::Connector* const connector_;
MediaDialogControllerDelegate* const delegate_;
// Stores a |media_message_center::MediaNotificationItem| for each media
// session keyed by its |request_id| in string format.
std::map<const std::string, media_message_center::MediaNotificationItem>
sessions_;
mojo::Remote<media_session::mojom::AudioFocusManager> audio_focus_remote_;
mojo::Remote<media_session::mojom::MediaControllerManager>
controller_manager_remote_;
// Used to receive updates to the active media controller.
mojo::Receiver<media_session::mojom::AudioFocusObserver>
audio_focus_observer_receiver_{this};
// Task runner used for testing.
scoped_refptr<base::SequencedTaskRunner> task_runner_for_testing_;
base::WeakPtrFactory<MediaDialogController> weak_ptr_factory_{this};
DISALLOW_COPY_AND_ASSIGN(MediaDialogController);
};
#endif // CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_CONTROLLER_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/global_media_controls/media_dialog_controller.h"
#include <memory>
#include "base/strings/utf_string_conversions.h"
#include "base/test/test_mock_time_task_runner.h"
#include "base/unguessable_token.h"
#include "chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.h"
#include "components/media_message_center/media_notification_item.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"
#include "testing/gtest/include/gtest/gtest.h"
using media_session::mojom::AudioFocusRequestState;
using media_session::mojom::AudioFocusRequestStatePtr;
using media_session::mojom::MediaSessionInfo;
using media_session::mojom::MediaSessionInfoPtr;
using testing::_;
namespace {
class MockMediaDialogControllerDelegate : public MediaDialogControllerDelegate {
public:
MockMediaDialogControllerDelegate() = default;
~MockMediaDialogControllerDelegate() override = default;
// MediaDialogControllerDelegate implementation.
MOCK_METHOD2(
ShowMediaSession,
void(const std::string& id,
base::WeakPtr<media_message_center::MediaNotificationItem> item));
MOCK_METHOD1(HideMediaSession, void(const std::string& id));
};
} // anonymous namespace
class MediaDialogControllerTest : public testing::Test {
public:
MediaDialogControllerTest()
: task_runner_(new base::TestMockTimeTaskRunner(
base::TestMockTimeTaskRunner::Type::kStandalone)) {}
~MediaDialogControllerTest() override = default;
void SetUp() override {
controller_ = std::make_unique<MediaDialogController>(nullptr, &delegate_);
controller_->task_runner_for_testing_ = task_runner_.get();
}
protected:
AudioFocusRequestStatePtr CreateFocusRequest(const base::UnguessableToken& id,
bool controllable) {
MediaSessionInfoPtr session_info(MediaSessionInfo::New());
session_info->is_controllable = controllable;
AudioFocusRequestStatePtr focus(AudioFocusRequestState::New());
focus->request_id = id;
focus->session_info = std::move(session_info);
return focus;
}
void SimulateFocusGained(const base::UnguessableToken& id,
bool controllable) {
controller_->OnFocusGained(CreateFocusRequest(id, controllable));
}
void SimulateFocusLost(const base::UnguessableToken& id) {
AudioFocusRequestStatePtr focus(AudioFocusRequestState::New());
focus->request_id = id;
controller_->OnFocusLost(std::move(focus));
}
void SimulateNecessaryMetadata(const base::UnguessableToken& id) {
// In order for the MediaNotificationItem to tell the MediaDialogController
// to show a media session, that session needs a title and artist. Typically
// this would happen through the media session service, but since the
// service doesn't run for this test, we'll manually grab the
// MediaNotificationItem from the MediaDialogController and set the
// metadata.
auto item_itr = controller_->sessions_.find(id.ToString());
ASSERT_NE(controller_->sessions_.end(), item_itr);
media_session::MediaMetadata metadata;
metadata.title = base::ASCIIToUTF16("title");
metadata.artist = base::ASCIIToUTF16("artist");
item_itr->second.MediaSessionMetadataChanged(std::move(metadata));
}
void SimulateReceivedAudioFocusRequests(
std::vector<AudioFocusRequestStatePtr> requests) {
controller_->OnReceivedAudioFocusRequests(std::move(requests));
}
void SimulateFreezeTimerExpired() {
task_runner_->FastForwardBy(base::TimeDelta::FromMilliseconds(2500));
}
bool IsSessionFrozen(const base::UnguessableToken& id) const {
auto item_itr = controller_->sessions_.find(id.ToString());
EXPECT_NE(controller_->sessions_.end(), item_itr);
return item_itr->second.frozen();
}
MockMediaDialogControllerDelegate& delegate() { return delegate_; }
private:
scoped_refptr<base::TestMockTimeTaskRunner> task_runner_;
MockMediaDialogControllerDelegate delegate_;
std::unique_ptr<MediaDialogController> controller_;
DISALLOW_COPY_AND_ASSIGN(MediaDialogControllerTest);
};
TEST_F(MediaDialogControllerTest, ShowControllableOnGainAndHideOnLoss) {
base::UnguessableToken id = base::UnguessableToken::Create();
EXPECT_CALL(delegate(), ShowMediaSession(id.ToString(), _));
SimulateFocusGained(id, true);
SimulateNecessaryMetadata(id);
EXPECT_FALSE(IsSessionFrozen(id));
// Ensure that the session was shown.
testing::Mock::VerifyAndClearExpectations(&delegate());
EXPECT_CALL(delegate(), HideMediaSession(id.ToString())).Times(0);
SimulateFocusLost(id);
EXPECT_TRUE(IsSessionFrozen(id));
// Ensure that the session was not hidden.
testing::Mock::VerifyAndClearExpectations(&delegate());
EXPECT_CALL(delegate(), HideMediaSession(id.ToString()));
SimulateFreezeTimerExpired();
}
TEST_F(MediaDialogControllerTest, DoesNotShowUncontrollableSession) {
base::UnguessableToken id = base::UnguessableToken::Create();
EXPECT_CALL(delegate(), ShowMediaSession(_, _)).Times(0);
SimulateFocusGained(id, false);
SimulateNecessaryMetadata(id);
}
TEST_F(MediaDialogControllerTest, ShowsAllInitialControllableSessions) {
base::UnguessableToken controllable1_id = base::UnguessableToken::Create();
base::UnguessableToken uncontrollable_id = base::UnguessableToken::Create();
base::UnguessableToken controllable2_id = base::UnguessableToken::Create();
EXPECT_CALL(delegate(), ShowMediaSession(controllable1_id.ToString(), _));
EXPECT_CALL(delegate(), ShowMediaSession(uncontrollable_id.ToString(), _))
.Times(0);
EXPECT_CALL(delegate(), ShowMediaSession(controllable2_id.ToString(), _));
std::vector<AudioFocusRequestStatePtr> requests;
requests.push_back(CreateFocusRequest(controllable1_id, true));
requests.push_back(CreateFocusRequest(uncontrollable_id, false));
requests.push_back(CreateFocusRequest(controllable2_id, true));
SimulateReceivedAudioFocusRequests(std::move(requests));
SimulateNecessaryMetadata(controllable1_id);
SimulateNecessaryMetadata(uncontrollable_id);
SimulateNecessaryMetadata(controllable2_id);
}
...@@ -2,6 +2,6 @@ ...@@ -2,6 +2,6 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.h" #include "chrome/browser/ui/global_media_controls/media_dialog_delegate.h"
MediaDialogControllerDelegate::~MediaDialogControllerDelegate() = default; MediaDialogDelegate::~MediaDialogDelegate() = default;
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_CONTROLLER_DELEGATE_H_ #ifndef CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_DELEGATE_H_
#define CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_CONTROLLER_DELEGATE_H_ #define CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_DELEGATE_H_
#include <string> #include <string>
...@@ -13,9 +13,9 @@ namespace media_message_center { ...@@ -13,9 +13,9 @@ namespace media_message_center {
class MediaNotificationItem; class MediaNotificationItem;
} // namespace media_message_center } // namespace media_message_center
// Delegate for MediaDialogController that is told when to display or hide a // Delegate for MediaToolbarButtonController that is told when to display or
// media session. // hide a media session.
class MediaDialogControllerDelegate { class MediaDialogDelegate {
public: public:
virtual void ShowMediaSession( virtual void ShowMediaSession(
const std::string& id, const std::string& id,
...@@ -23,7 +23,7 @@ class MediaDialogControllerDelegate { ...@@ -23,7 +23,7 @@ class MediaDialogControllerDelegate {
virtual void HideMediaSession(const std::string& id) = 0; virtual void HideMediaSession(const std::string& id) = 0;
protected: protected:
virtual ~MediaDialogControllerDelegate(); virtual ~MediaDialogDelegate();
}; };
#endif // CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_CONTROLLER_DELEGATE_H_ #endif // CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_DELEGATE_H_
...@@ -4,18 +4,13 @@ ...@@ -4,18 +4,13 @@
#include "chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h" #include "chrome/browser/ui/global_media_controls/media_toolbar_button_controller.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 "chrome/browser/ui/global_media_controls/media_toolbar_button_controller_delegate.h"
#include "components/media_message_center/media_notification_item.h"
#include "services/media_session/public/mojom/constants.mojom.h" #include "services/media_session/public/mojom/constants.mojom.h"
#include "services/media_session/public/mojom/media_session.mojom.h" #include "services/media_session/public/mojom/media_session.mojom.h"
#include "services/service_manager/public/cpp/connector.h" #include "services/service_manager/public/cpp/connector.h"
namespace {
constexpr base::TimeDelta kHideTimerDelay =
base::TimeDelta::FromMilliseconds(2500);
} // anonymous namespace
MediaToolbarButtonController::MediaToolbarButtonController( MediaToolbarButtonController::MediaToolbarButtonController(
service_manager::Connector* connector, service_manager::Connector* connector,
MediaToolbarButtonControllerDelegate* delegate) MediaToolbarButtonControllerDelegate* delegate)
...@@ -26,37 +21,161 @@ MediaToolbarButtonController::MediaToolbarButtonController( ...@@ -26,37 +21,161 @@ MediaToolbarButtonController::MediaToolbarButtonController(
if (!connector_) if (!connector_)
return; return;
// Connect to the MediaControllerManager and create a MediaController that // Connect to the controller manager so we can create media controllers for
// controls the active session so we can observe it. // media sessions.
media_session::mojom::MediaControllerManagerPtr controller_manager_ptr; connector_->Connect(media_session::mojom::kServiceName,
connector_->BindInterface(media_session::mojom::kServiceName, controller_manager_remote_.BindNewPipeAndPassReceiver());
mojo::MakeRequest(&controller_manager_ptr));
controller_manager_ptr->CreateActiveMediaController( // Connect to receive audio focus events.
mojo::MakeRequest(&media_controller_ptr_)); connector_->Connect(media_session::mojom::kServiceName,
audio_focus_remote_.BindNewPipeAndPassReceiver());
audio_focus_remote_->AddObserver(
audio_focus_observer_receiver_.BindNewPipeAndPassRemote());
// Observe the active media controller for changes to playback state and audio_focus_remote_->GetFocusRequests(base::BindOnce(
// supported actions. &MediaToolbarButtonController::OnReceivedAudioFocusRequests,
media_controller_ptr_->AddObserver( weak_ptr_factory_.GetWeakPtr()));
media_controller_observer_receiver_.BindNewPipeAndPassRemote());
} }
MediaToolbarButtonController::~MediaToolbarButtonController() = default; MediaToolbarButtonController::~MediaToolbarButtonController() = default;
void MediaToolbarButtonController::MediaSessionInfoChanged( void MediaToolbarButtonController::OnFocusGained(
media_session::mojom::MediaSessionInfoPtr session_info) { media_session::mojom::AudioFocusRequestStatePtr session) {
if (session_info && session_info->is_controllable) { const std::string id = session->request_id->ToString();
hide_icon_timer_.Stop();
delegate_->Enable(); // If we have an existing unfrozen item then this is a duplicate call and
delegate_->Show(); // we should ignore it.
auto it = sessions_.find(id);
if (it != sessions_.end() && !it->second.frozen())
return;
mojo::Remote<media_session::mojom::MediaController> controller;
// |controller_manager_remote_| may be null in tests where connector is
// unavailable.
if (controller_manager_remote_) {
controller_manager_remote_->CreateMediaControllerForSession(
controller.BindNewPipeAndPassReceiver(), *session->request_id);
}
if (it != sessions_.end()) {
// If the notification was previously frozen then we should reset the
// controller because the mojo pipe would have been reset.
it->second.SetController(std::move(controller),
std::move(session->session_info));
active_controllable_session_ids_.insert(id);
frozen_session_ids_.erase(id);
UpdateToolbarButtonState();
} else { } else {
delegate_->Disable(); sessions_.emplace(
hide_icon_timer_.Start( std::piecewise_construct, std::forward_as_tuple(id),
FROM_HERE, kHideTimerDelay, std::forward_as_tuple(
base::BindOnce(&MediaToolbarButtonController::OnHideTimerFired, this, id, session->source_name.value_or(std::string()),
base::Unretained(this))); std::move(controller), std::move(session->session_info)));
}
}
void MediaToolbarButtonController::OnFocusLost(
media_session::mojom::AudioFocusRequestStatePtr session) {
const std::string id = session->request_id->ToString();
auto it = sessions_.find(id);
if (it == sessions_.end())
return;
it->second.Freeze();
active_controllable_session_ids_.erase(id);
frozen_session_ids_.insert(id);
UpdateToolbarButtonState();
}
void MediaToolbarButtonController::ShowNotification(const std::string& id) {
active_controllable_session_ids_.insert(id);
UpdateToolbarButtonState();
if (!dialog_delegate_)
return;
base::WeakPtr<media_message_center::MediaNotificationItem> item;
auto it = sessions_.find(id);
if (it != sessions_.end())
item = it->second.GetWeakPtr();
dialog_delegate_->ShowMediaSession(id, item);
}
void MediaToolbarButtonController::HideNotification(const std::string& id) {
active_controllable_session_ids_.erase(id);
frozen_session_ids_.erase(id);
UpdateToolbarButtonState();
if (!dialog_delegate_)
return;
dialog_delegate_->HideMediaSession(id);
}
scoped_refptr<base::SequencedTaskRunner>
MediaToolbarButtonController::GetTaskRunner() const {
return nullptr;
}
void MediaToolbarButtonController::RemoveItem(const std::string& id) {
active_controllable_session_ids_.erase(id);
frozen_session_ids_.erase(id);
sessions_.erase(id);
UpdateToolbarButtonState();
}
void MediaToolbarButtonController::SetDialogDelegate(
MediaDialogDelegate* delegate) {
DCHECK(!delegate || !dialog_delegate_);
dialog_delegate_ = delegate;
UpdateToolbarButtonState();
if (!dialog_delegate_)
return;
for (const std::string& id : active_controllable_session_ids_) {
base::WeakPtr<media_message_center::MediaNotificationItem> item;
auto it = sessions_.find(id);
if (it != sessions_.end())
item = it->second.GetWeakPtr();
dialog_delegate_->ShowMediaSession(id, item);
} }
} }
void MediaToolbarButtonController::OnHideTimerFired() { void MediaToolbarButtonController::OnReceivedAudioFocusRequests(
delegate_->Hide(); std::vector<media_session::mojom::AudioFocusRequestStatePtr> sessions) {
for (auto& session : sessions)
OnFocusGained(std::move(session));
}
void MediaToolbarButtonController::UpdateToolbarButtonState() {
if (!active_controllable_session_ids_.empty()) {
if (delegate_display_state_ != DisplayState::kShown) {
delegate_->Enable();
delegate_->Show();
}
delegate_display_state_ = DisplayState::kShown;
return;
}
if (frozen_session_ids_.empty()) {
if (delegate_display_state_ != DisplayState::kHidden)
delegate_->Hide();
delegate_display_state_ = DisplayState::kHidden;
return;
}
if (!dialog_delegate_) {
if (delegate_display_state_ != DisplayState::kDisabled)
delegate_->Disable();
delegate_display_state_ = DisplayState::kDisabled;
}
} }
...@@ -5,55 +5,99 @@ ...@@ -5,55 +5,99 @@
#ifndef CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_TOOLBAR_BUTTON_CONTROLLER_H_ #ifndef CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_TOOLBAR_BUTTON_CONTROLLER_H_
#define CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_TOOLBAR_BUTTON_CONTROLLER_H_ #define CHROME_BROWSER_UI_GLOBAL_MEDIA_CONTROLS_MEDIA_TOOLBAR_BUTTON_CONTROLLER_H_
#include <map>
#include <string>
#include <vector>
#include "base/macros.h" #include "base/macros.h"
#include "base/timer/timer.h" #include "base/memory/weak_ptr.h"
#include "components/media_message_center/media_notification_controller.h"
#include "mojo/public/cpp/bindings/receiver.h" #include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "services/media_session/public/mojom/audio_focus.mojom.h"
#include "services/media_session/public/mojom/media_controller.mojom.h" #include "services/media_session/public/mojom/media_controller.mojom.h"
namespace media_message_center {
class MediaNotificationItem;
} // namespace media_message_center
namespace service_manager { namespace service_manager {
class Connector; class Connector;
} // namespace service_manager } // namespace service_manager
class MediaDialogDelegate;
class MediaToolbarButtonControllerDelegate; class MediaToolbarButtonControllerDelegate;
// Controller for the MediaToolbarButtonView that decides when to show or hide // Controller for the MediaToolbarButtonView that decides when to show or hide
// the icon from the toolbar. // the icon from the toolbar. Also passes MediaNotificationItems to the
// MediaDialogView to display.
class MediaToolbarButtonController class MediaToolbarButtonController
: public media_session::mojom::MediaControllerObserver { : public media_session::mojom::AudioFocusObserver,
public media_message_center::MediaNotificationController {
public: public:
MediaToolbarButtonController(service_manager::Connector* connector, MediaToolbarButtonController(service_manager::Connector* connector,
MediaToolbarButtonControllerDelegate* delegate); MediaToolbarButtonControllerDelegate* delegate);
~MediaToolbarButtonController() override; ~MediaToolbarButtonController() override;
// media_session::mojom::MediaControllerObserver implementation. // media_session::mojom::AudioFocusObserver implementation.
void MediaSessionInfoChanged( void OnFocusGained(
media_session::mojom::MediaSessionInfoPtr session_info) override; media_session::mojom::AudioFocusRequestStatePtr session) override;
void MediaSessionMetadataChanged( void OnFocusLost(
const base::Optional<media_session::MediaMetadata>& metadata) override {} media_session::mojom::AudioFocusRequestStatePtr session) override;
void MediaSessionActionsChanged(
const std::vector<media_session::mojom::MediaSessionAction>& actions) // media_message_center::MediaNotificationController implementation.
override {} void ShowNotification(const std::string& id) override;
void MediaSessionChanged( void HideNotification(const std::string& id) override;
const base::Optional<base::UnguessableToken>& request_id) override {} void RemoveItem(const std::string& id) override;
void MediaSessionPositionChanged( scoped_refptr<base::SequencedTaskRunner> GetTaskRunner() const override;
const base::Optional<media_session::MediaPosition>& position) override {}
void SetDialogDelegate(MediaDialogDelegate* delegate);
private: private:
void OnHideTimerFired(); friend class MediaToolbarButtonControllerTest;
// Tracks the current display state of the toolbar button delegate.
enum class DisplayState {
kShown,
kDisabled,
kHidden,
};
void OnReceivedAudioFocusRequests(
std::vector<media_session::mojom::AudioFocusRequestStatePtr> sessions);
void UpdateToolbarButtonState();
service_manager::Connector* const connector_; service_manager::Connector* const connector_;
MediaToolbarButtonControllerDelegate* const delegate_; MediaToolbarButtonControllerDelegate* const delegate_;
MediaDialogDelegate* dialog_delegate_ = nullptr;
// The delegate starts hidden and isn't shown until media playback starts.
DisplayState delegate_display_state_ = DisplayState::kHidden;
// Used to track whether there are any active controllable media sessions. If
// not, then there's nothing to show in the dialog and we can hide the toolbar
// icon.
std::unordered_set<std::string> active_controllable_session_ids_;
// Tracks the sessions that are currently frozen. If there are only frozen
// sessions, we will disable the toolbar icon and wait to hide it.
std::unordered_set<std::string> frozen_session_ids_;
// We hide the toolbar button when there's no active and controllable media // Stores a |media_message_center::MediaNotificationItem| for each media
// session. We use this timer to avoid flashing when a media session changes. // session keyed by its |request_id| in string format.
base::OneShotTimer hide_icon_timer_; std::map<const std::string, media_message_center::MediaNotificationItem>
sessions_;
// Tracks current media session state/metadata. // Connections with the media session service to listen for audio focus
media_session::mojom::MediaControllerPtr media_controller_ptr_; // updates and control media sessions.
mojo::Remote<media_session::mojom::AudioFocusManager> audio_focus_remote_;
mojo::Remote<media_session::mojom::MediaControllerManager>
controller_manager_remote_;
mojo::Receiver<media_session::mojom::AudioFocusObserver>
audio_focus_observer_receiver_{this};
// Used to receive updates to the active media controller. base::WeakPtrFactory<MediaToolbarButtonController> weak_ptr_factory_{this};
mojo::Receiver<media_session::mojom::MediaControllerObserver>
media_controller_observer_receiver_{this};
DISALLOW_COPY_AND_ASSIGN(MediaToolbarButtonController); DISALLOW_COPY_AND_ASSIGN(MediaToolbarButtonController);
}; };
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include "chrome/browser/ui/views/global_media_controls/media_dialog_view.h" #include "chrome/browser/ui/views/global_media_controls/media_dialog_view.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h" #include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl.h" #include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl.h"
#include "chrome/browser/ui/views/global_media_controls/media_notification_list_view.h" #include "chrome/browser/ui/views/global_media_controls/media_notification_list_view.h"
...@@ -27,9 +28,11 @@ MediaDialogView* MediaDialogView::instance_ = nullptr; ...@@ -27,9 +28,11 @@ MediaDialogView* MediaDialogView::instance_ = nullptr;
// static // static
void MediaDialogView::ShowDialog(views::View* anchor_view, void MediaDialogView::ShowDialog(views::View* anchor_view,
MediaToolbarButtonController* controller,
service_manager::Connector* connector) { service_manager::Connector* connector) {
DCHECK(!instance_); DCHECK(!instance_);
instance_ = new MediaDialogView(anchor_view, connector); DCHECK(controller);
instance_ = new MediaDialogView(anchor_view, controller, connector);
views::Widget* widget = views::Widget* widget =
views::BubbleDialogDelegateView::CreateBubble(instance_); views::BubbleDialogDelegateView::CreateBubble(instance_);
...@@ -38,8 +41,10 @@ void MediaDialogView::ShowDialog(views::View* anchor_view, ...@@ -38,8 +41,10 @@ void MediaDialogView::ShowDialog(views::View* anchor_view,
// static // static
void MediaDialogView::HideDialog() { void MediaDialogView::HideDialog() {
if (IsShowing()) if (IsShowing()) {
instance_->controller_->SetDialogDelegate(nullptr);
instance_->GetWidget()->Close(); instance_->GetWidget()->Close();
}
// Set |instance_| to nullptr so that |IsShowing()| returns false immediately. // Set |instance_| to nullptr so that |IsShowing()| returns false immediately.
// We also set to nullptr in |WindowClosing()| (which happens asynchronously), // We also set to nullptr in |WindowClosing()| (which happens asynchronously),
...@@ -77,6 +82,8 @@ void MediaDialogView::AddedToWidget() { ...@@ -77,6 +82,8 @@ void MediaDialogView::AddedToWidget() {
views::BubbleFrameView* frame = GetBubbleFrameView(); views::BubbleFrameView* frame = GetBubbleFrameView();
if (frame) if (frame)
frame->SetCornerRadius(kMediaDialogCornerRadius); frame->SetCornerRadius(kMediaDialogCornerRadius);
controller_->SetDialogDelegate(this);
} }
gfx::Size MediaDialogView::CalculatePreferredSize() const { gfx::Size MediaDialogView::CalculatePreferredSize() const {
...@@ -91,11 +98,14 @@ gfx::Size MediaDialogView::CalculatePreferredSize() const { ...@@ -91,11 +98,14 @@ gfx::Size MediaDialogView::CalculatePreferredSize() const {
} }
MediaDialogView::MediaDialogView(views::View* anchor_view, MediaDialogView::MediaDialogView(views::View* anchor_view,
MediaToolbarButtonController* controller,
service_manager::Connector* connector) service_manager::Connector* connector)
: BubbleDialogDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT), : BubbleDialogDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
controller_(connector, this), controller_(controller),
active_sessions_view_( active_sessions_view_(
AddChildView(std::make_unique<MediaNotificationListView>())) {} AddChildView(std::make_unique<MediaNotificationListView>())) {
DCHECK(controller_);
}
MediaDialogView::~MediaDialogView() = default; MediaDialogView::~MediaDialogView() = default;
...@@ -104,11 +114,11 @@ void MediaDialogView::Init() { ...@@ -104,11 +114,11 @@ void MediaDialogView::Init() {
set_margins(gfx::Insets()); set_margins(gfx::Insets());
SetLayoutManager(std::make_unique<views::FillLayout>()); SetLayoutManager(std::make_unique<views::FillLayout>());
controller_.Initialize();
} }
void MediaDialogView::WindowClosing() { void MediaDialogView::WindowClosing() {
if (instance_ == this) if (instance_ == this) {
instance_ = nullptr; instance_ = nullptr;
controller_->SetDialogDelegate(nullptr);
}
} }
...@@ -6,8 +6,7 @@ ...@@ -6,8 +6,7 @@
#define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_VIEW_H_ #define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_DIALOG_VIEW_H_
#include "base/optional.h" #include "base/optional.h"
#include "chrome/browser/ui/global_media_controls/media_dialog_controller.h" #include "chrome/browser/ui/global_media_controls/media_dialog_delegate.h"
#include "chrome/browser/ui/global_media_controls/media_dialog_controller_delegate.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h" #include "ui/views/bubble/bubble_dialog_delegate_view.h"
namespace service_manager { namespace service_manager {
...@@ -15,17 +14,19 @@ class Connector; ...@@ -15,17 +14,19 @@ class Connector;
} // namespace service_manager } // namespace service_manager
class MediaNotificationListView; class MediaNotificationListView;
class MediaToolbarButtonController;
// Dialog that shows media controls that control the active media session. // Dialog that shows media controls that control the active media session.
class MediaDialogView : public views::BubbleDialogDelegateView, class MediaDialogView : public views::BubbleDialogDelegateView,
public MediaDialogControllerDelegate { public MediaDialogDelegate {
public: public:
static void ShowDialog(views::View* anchor_view, static void ShowDialog(views::View* anchor_view,
MediaToolbarButtonController* controller,
service_manager::Connector* connector); service_manager::Connector* connector);
static void HideDialog(); static void HideDialog();
static bool IsShowing(); static bool IsShowing();
// MediaDialogControllerDelegate implementation. // MediaDialogDelegate implementation.
void ShowMediaSession( void ShowMediaSession(
const std::string& id, const std::string& id,
base::WeakPtr<media_message_center::MediaNotificationItem> item) override; base::WeakPtr<media_message_center::MediaNotificationItem> item) override;
...@@ -41,6 +42,7 @@ class MediaDialogView : public views::BubbleDialogDelegateView, ...@@ -41,6 +42,7 @@ class MediaDialogView : public views::BubbleDialogDelegateView,
private: private:
explicit MediaDialogView(views::View* anchor_view, explicit MediaDialogView(views::View* anchor_view,
MediaToolbarButtonController* controller,
service_manager::Connector* connector); service_manager::Connector* connector);
~MediaDialogView() override; ~MediaDialogView() override;
...@@ -50,7 +52,7 @@ class MediaDialogView : public views::BubbleDialogDelegateView, ...@@ -50,7 +52,7 @@ class MediaDialogView : public views::BubbleDialogDelegateView,
void Init() override; void Init() override;
void WindowClosing() override; void WindowClosing() override;
MediaDialogController controller_; MediaToolbarButtonController* const controller_;
MediaNotificationListView* const active_sessions_view_; MediaNotificationListView* const active_sessions_view_;
......
...@@ -38,7 +38,7 @@ void MediaToolbarButtonView::ButtonPressed(views::Button* sender, ...@@ -38,7 +38,7 @@ void MediaToolbarButtonView::ButtonPressed(views::Button* sender,
if (MediaDialogView::IsShowing()) if (MediaDialogView::IsShowing())
MediaDialogView::HideDialog(); MediaDialogView::HideDialog();
else else
MediaDialogView::ShowDialog(this, connector_); MediaDialogView::ShowDialog(this, &controller_, connector_);
} }
void MediaToolbarButtonView::Show() { void MediaToolbarButtonView::Show() {
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_TOOLBAR_BUTTON_VIEW_H_ #define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_TOOLBAR_BUTTON_VIEW_H_
#include "base/macros.h" #include "base/macros.h"
#include "base/unguessable_token.h"
#include "chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h" #include "chrome/browser/ui/global_media_controls/media_toolbar_button_controller.h"
#include "chrome/browser/ui/global_media_controls/media_toolbar_button_controller_delegate.h" #include "chrome/browser/ui/global_media_controls/media_toolbar_button_controller_delegate.h"
#include "chrome/browser/ui/views/toolbar/toolbar_button.h" #include "chrome/browser/ui/views/toolbar/toolbar_button.h"
...@@ -37,7 +38,7 @@ class MediaToolbarButtonView : public ToolbarButton, ...@@ -37,7 +38,7 @@ class MediaToolbarButtonView : public ToolbarButton,
void UpdateIcon(); void UpdateIcon();
private: private:
service_manager::Connector* connector_; service_manager::Connector* const connector_;
MediaToolbarButtonController controller_; MediaToolbarButtonController controller_;
DISALLOW_COPY_AND_ASSIGN(MediaToolbarButtonView); DISALLOW_COPY_AND_ASSIGN(MediaToolbarButtonView);
......
...@@ -3763,7 +3763,6 @@ test("unit_tests") { ...@@ -3763,7 +3763,6 @@ test("unit_tests") {
"../browser/ui/extensions/extension_action_view_controller_unittest.cc", "../browser/ui/extensions/extension_action_view_controller_unittest.cc",
"../browser/ui/extensions/extension_message_bubble_bridge_unittest.cc", "../browser/ui/extensions/extension_message_bubble_bridge_unittest.cc",
"../browser/ui/global_error/global_error_service_unittest.cc", "../browser/ui/global_error/global_error_service_unittest.cc",
"../browser/ui/global_media_controls/media_dialog_controller_unittest.cc",
"../browser/ui/global_media_controls/media_toolbar_button_controller_unittest.cc", "../browser/ui/global_media_controls/media_toolbar_button_controller_unittest.cc",
"../browser/ui/hid/hid_chooser_controller_unittest.cc", "../browser/ui/hid/hid_chooser_controller_unittest.cc",
"../browser/ui/in_product_help/active_tab_tracker_unittest.cc", "../browser/ui/in_product_help/active_tab_tracker_unittest.cc",
......
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