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

GMC: Show a list of all media sessions instead of just the active one

This CL updates the global media controls UI to show all media sessions
instead of just the most recently active media session.

Bug: 973492
Change-Id: Ib0943d4044ee3c036a1b783785695ebba09eb0f4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1695989Reviewed-by: default avatarBecca Hughes <beccahughes@chromium.org>
Commit-Queue: Tommy Steimel <steimel@chromium.org>
Cr-Commit-Position: refs/heads/master@{#676119}
parent 07a7b98b
......@@ -2738,6 +2738,8 @@ jumbo_split_static_library("ui") {
"views/global_media_controls/media_dialog_view.h",
"views/global_media_controls/media_notification_container_impl.cc",
"views/global_media_controls/media_notification_container_impl.h",
"views/global_media_controls/media_notification_list_view.cc",
"views/global_media_controls/media_notification_list_view.h",
"views/global_media_controls/media_toolbar_button_view.cc",
"views/global_media_controls/media_toolbar_button_view.h",
"views/hover_button.cc",
......
......@@ -88,16 +88,6 @@ void MediaDialogController::HideNotification(const std::string& id) {
void MediaDialogController::OnReceivedAudioFocusRequests(
std::vector<media_session::mojom::AudioFocusRequestStatePtr> sessions) {
// TODO(steimel): Show all controllable sessions.
media_session::mojom::AudioFocusRequestStatePtr active_session;
for (auto& session : base::Reversed(sessions)) {
if (!session->session_info->is_controllable)
continue;
active_session = session.Clone();
break;
}
if (active_session)
OnFocusGained(std::move(active_session));
for (auto& session : sessions)
OnFocusGained(std::move(session));
}
......@@ -48,15 +48,20 @@ class MediaDialogControllerTest : public testing::Test {
}
protected:
void SimulateFocusGained(const base::UnguessableToken& id,
bool controllable) {
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);
controller_->OnFocusGained(std::move(focus));
return focus;
}
void SimulateFocusGained(const base::UnguessableToken& id,
bool controllable) {
controller_->OnFocusGained(CreateFocusRequest(id, controllable));
}
void SimulateFocusLost(const base::UnguessableToken& id) {
......@@ -81,6 +86,11 @@ class MediaDialogControllerTest : public testing::Test {
item_itr->second.MediaSessionMetadataChanged(std::move(metadata));
}
void SimulateReceivedAudioFocusRequests(
std::vector<AudioFocusRequestStatePtr> requests) {
controller_->OnReceivedAudioFocusRequests(std::move(requests));
}
MockMediaDialogControllerDelegate& delegate() { return delegate_; }
private:
......@@ -113,3 +123,25 @@ TEST_F(MediaDialogControllerTest, DoesNotShowUncontrollableSession) {
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);
}
......@@ -7,6 +7,7 @@
#include "base/strings/utf_string_conversions.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_list_view.h"
#include "services/media_session/public/mojom/media_session.mojom.h"
#include "ui/views/background.h"
#include "ui/views/controls/label.h"
......@@ -48,27 +49,13 @@ bool MediaDialogView::IsShowing() {
void MediaDialogView::ShowMediaSession(
const std::string& id,
base::WeakPtr<media_message_center::MediaNotificationItem> item) {
// Do nothing if we're already showing this media session.
if (active_session_id_.has_value() && *active_session_id_ == id)
return;
active_session_id_ = id;
auto active_session_container =
std::make_unique<MediaNotificationContainerImpl>(this, item);
active_session_container_ = AddChildView(std::move(active_session_container));
active_sessions_view_->ShowNotification(
id, std::make_unique<MediaNotificationContainerImpl>(this, item));
OnAnchorBoundsChanged();
}
void MediaDialogView::HideMediaSession(const std::string& id) {
// Do nothing if we're not showing this media session.
if (!active_session_id_.has_value() || active_session_id_ != id)
return;
active_session_id_ = base::nullopt;
RemoveChildView(active_session_container_);
active_session_container_ = nullptr;
active_sessions_view_->HideNotification(id);
OnAnchorBoundsChanged();
}
......@@ -86,7 +73,7 @@ bool MediaDialogView::Close() {
gfx::Size MediaDialogView::CalculatePreferredSize() const {
// If we have an active session, then fit to it.
if (active_session_container_)
if (!active_sessions_view_->empty())
return views::BubbleDialogDelegateView::CalculatePreferredSize();
// Otherwise, use a standard size for bubble dialogs.
......@@ -98,7 +85,9 @@ gfx::Size MediaDialogView::CalculatePreferredSize() const {
MediaDialogView::MediaDialogView(views::View* anchor_view,
service_manager::Connector* connector)
: BubbleDialogDelegateView(anchor_view, views::BubbleBorder::TOP_RIGHT),
controller_(connector, this) {}
controller_(connector, this),
active_sessions_view_(
AddChildView(std::make_unique<MediaNotificationListView>())) {}
MediaDialogView::~MediaDialogView() = default;
......
......@@ -14,7 +14,7 @@ namespace service_manager {
class Connector;
} // namespace service_manager
class MediaNotificationContainerImpl;
class MediaNotificationListView;
// Dialog that shows media controls that control the active media session.
class MediaDialogView : public views::BubbleDialogDelegateView,
......@@ -54,11 +54,7 @@ class MediaDialogView : public views::BubbleDialogDelegateView,
MediaDialogController controller_;
// TODO(steimel): We should support showing multiple sessions instead of just
// the active one.
MediaNotificationContainerImpl* active_session_container_ = nullptr;
base::Optional<std::string> active_session_id_;
MediaNotificationListView* const active_sessions_view_;
DISALLOW_COPY_AND_ASSIGN(MediaDialogView);
};
......
// 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/views/global_media_controls/media_notification_list_view.h"
#include "chrome/browser/ui/views/global_media_controls/media_notification_container_impl.h"
#include "ui/views/layout/box_layout.h"
MediaNotificationListView::MediaNotificationListView() {
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical));
}
MediaNotificationListView::~MediaNotificationListView() = default;
void MediaNotificationListView::ShowNotification(
const std::string& id,
std::unique_ptr<MediaNotificationContainerImpl> notification) {
DCHECK(!base::Contains(notifications_, id));
DCHECK_NE(nullptr, notification.get());
notifications_[id] = AddChildView(std::move(notification));
PreferredSizeChanged();
}
void MediaNotificationListView::HideNotification(const std::string& id) {
if (!base::Contains(notifications_, id))
return;
RemoveChildView(notifications_[id]);
notifications_.erase(id);
PreferredSizeChanged();
}
// 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_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_LIST_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_LIST_VIEW_H_
#include <map>
#include <memory>
#include "ui/views/view.h"
class MediaNotificationContainerImpl;
// MediaNotificationListView is a container that holds a list of active media
// sessions.
class MediaNotificationListView : public views::View {
public:
MediaNotificationListView();
~MediaNotificationListView() override;
void ShowNotification(
const std::string& id,
std::unique_ptr<MediaNotificationContainerImpl> notification);
void HideNotification(const std::string& id);
bool empty() { return notifications_.empty(); }
private:
std::map<const std::string, MediaNotificationContainerImpl*> notifications_;
DISALLOW_COPY_AND_ASSIGN(MediaNotificationListView);
};
#endif // CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_LIST_VIEW_H_
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