Commit 75efc8f5 authored by Muyao Xu's avatar Muyao Xu Committed by Commit Bot

[Cast+Zenith] Show available cast devices in Global Media Controls

Global Media Controls can now show available cast devices along with
availblae audio devices for each active media session. This change is
visible when GlobalMediaControlsCastStartStop feature is enabled.

MediaNotificationAudioDeviceSelectorView implements
CastDialogController::Observer interface and owns a CastDialogController
object to listen to sink updates and update the UI.

Change-Id: Ie9488fee7183f10b4f6d71e41254223fc6d68c48
Bug: b/161610050, 1107162
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2353010
Commit-Queue: Muyao Xu <muyaoxu@google.com>
Reviewed-by: default avatarElly Fong-Jones <ellyjones@chromium.org>
Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Reviewed-by: default avatarTakumi Fujimoto <takumif@chromium.org>
Cr-Commit-Position: refs/heads/master@{#806437}
parent c09ef60c
......@@ -3526,6 +3526,8 @@ static_library("ui") {
"views/global_media_controls/media_dialog_view_observer.h",
"views/global_media_controls/media_notification_container_impl_view.cc",
"views/global_media_controls/media_notification_container_impl_view.h",
"views/global_media_controls/media_notification_device_entry_ui.cc",
"views/global_media_controls/media_notification_device_entry_ui.h",
"views/global_media_controls/media_notification_device_selector_view.cc",
"views/global_media_controls/media_notification_device_selector_view.h",
"views/global_media_controls/media_notification_device_selector_view_delegate.h",
......
......@@ -19,6 +19,7 @@
#include "chrome/browser/ui/global_media_controls/media_notification_device_provider_impl.h"
#include "chrome/browser/ui/global_media_controls/media_notification_service_observer.h"
#include "chrome/browser/ui/global_media_controls/overlay_media_notification.h"
#include "chrome/browser/ui/media_router/media_router_ui.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "components/media_message_center/media_notification_item.h"
#include "components/media_message_center/media_notification_util.h"
......@@ -790,6 +791,19 @@ void MediaNotificationService::set_device_provider_for_testing(
device_provider_ = std::move(device_provider);
}
std::unique_ptr<media_router::CastDialogController>
MediaNotificationService::CreateCastDialogControllerForSession(
const std::string& session_id) {
auto it = sessions_.find(session_id);
if (it != sessions_.end()) {
auto ui = std::make_unique<media_router::MediaRouterUI>(
it->second.web_contents());
ui->InitWithDefaultMediaSource();
return ui;
}
return nullptr;
}
void MediaNotificationService::OnItemUnfrozen(const std::string& id) {
frozen_session_ids_.erase(id);
......
......@@ -39,6 +39,10 @@ namespace media_message_center {
class MediaSessionNotificationItem;
} // namespace media_message_center
namespace media_router {
class CastDialogController;
}
class MediaDialogDelegate;
class MediaNotificationContainerImpl;
class MediaNotificationServiceObserver;
......@@ -131,6 +135,11 @@ class MediaNotificationService
void set_device_provider_for_testing(
std::unique_ptr<MediaNotificationDeviceProvider> device_provider);
// Instantiates a MediaRouterViewsUI object associated with the Session with
// the given |session_id|.
std::unique_ptr<media_router::CastDialogController>
CreateCastDialogControllerForSession(const std::string& session_id);
private:
friend class MediaNotificationServiceTest;
friend class MediaToolbarButtonControllerTest;
......@@ -261,7 +270,7 @@ class MediaNotificationService
mojo::Receiver<media_session::mojom::MediaControllerObserver>
observer_receiver_{this};
// Used to request audio output be routed to a different device
// Used to request audio output be routed to a different device.
mojo::Remote<media_session::mojom::MediaController> controller_;
base::WeakPtr<media_router::WebContentsPresentationManager>
......
......@@ -6,6 +6,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/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"
......@@ -138,9 +139,14 @@ MediaNotificationContainerImplView::MediaNotificationContainerImplView(
if (base::FeatureList::IsEnabled(
media::kGlobalMediaControlsSeamlessTransfer) &&
!is_cast_notification) {
auto cast_controller =
media_router::GlobalMediaControlsCastStartStopEnabled()
? service_->CreateCastDialogControllerForSession(id_)
: nullptr;
auto audio_device_selector_view =
std::make_unique<MediaNotificationDeviceSelectorView>(
this, audio_sink_id_, foreground_color_, background_color_);
this, std::move(cast_controller), audio_sink_id_, foreground_color_,
background_color_);
audio_device_selector_view_ =
AddChildView(std::move(audio_device_selector_view));
view_->UpdateCornerRadius(message_center::kNotificationCornerRadius, 0);
......
// Copyright 2020 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_device_entry_ui.h"
#include "base/strings/utf_string_conversions.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/views/background.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/layout/box_layout.h"
namespace {
constexpr gfx::Insets kIconContainerInsets{10, 15};
constexpr int kDeviceIconSize = 18;
constexpr gfx::Insets kLabelsContainerInsets{18, 0};
constexpr gfx::Size kDeviceEntryViewSize{400, 30};
constexpr int kEntryHighlightOpacity = 45;
} // namespace
DeviceEntryUI::DeviceEntryUI(SkColor foreground_color,
SkColor background_color,
const std::string& raw_device_id,
const std::string& device_name,
const gfx::VectorIcon* icon,
const std::string& subtext)
: foreground_color_(foreground_color),
background_color_(background_color),
raw_device_id_(raw_device_id),
device_name_(device_name),
icon_(icon) {}
std::string DeviceEntryUI::GetEntryLabelForTesting() {
return base::UTF16ToUTF8(device_name_label_->GetText());
}
AudioDeviceEntryView::AudioDeviceEntryView(SkColor foreground_color,
SkColor background_color,
const std::string& raw_device_id,
const std::string& device_name,
const std::string& subtext)
: DeviceEntryUI(foreground_color,
background_color,
raw_device_id,
device_name,
&vector_icons::kHeadsetIcon) {
SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal));
icon_container_ = AddChildView(std::make_unique<views::View>());
auto* icon_container_layout =
icon_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal, kIconContainerInsets));
icon_container_layout->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kCenter);
icon_container_layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kCenter);
device_icon_ =
icon_container_->AddChildView(std::make_unique<views::ImageView>());
device_icon_->SetImage(
gfx::CreateVectorIcon(*icon_, kDeviceIconSize, foreground_color));
labels_container_ = AddChildView(std::make_unique<views::View>());
auto* labels_container_layout =
labels_container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kVertical, kLabelsContainerInsets));
labels_container_layout->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kCenter);
labels_container_layout->set_cross_axis_alignment(
views::BoxLayout::CrossAxisAlignment::kStart);
views::Label::CustomFont device_name_label_font{
views::Label::GetDefaultFontList().DeriveWithSizeDelta(1)};
device_name_label_ =
labels_container_->AddChildView(std::make_unique<views::Label>(
base::UTF8ToUTF16(device_name_), device_name_label_font));
device_name_label_->SetEnabledColor(foreground_color);
device_name_label_->SetBackgroundColor(background_color);
if (!subtext.empty()) {
device_subtext_label_ = labels_container_->AddChildView(
std::make_unique<views::Label>(base::UTF8ToUTF16(subtext)));
device_subtext_label_->SetTextStyle(
views::style::TextStyle::STYLE_SECONDARY);
device_subtext_label_->SetEnabledColor(foreground_color);
device_subtext_label_->SetBackgroundColor(background_color);
}
// Ensures that hovering over these items also hovers this view.
icon_container_->set_can_process_events_within_subtree(false);
labels_container_->set_can_process_events_within_subtree(false);
SetFocusBehavior(views::View::FocusBehavior::ALWAYS);
SetInkDropMode(Button::InkDropMode::ON);
set_ink_drop_base_color(foreground_color);
set_has_ink_drop_action_on_click(true);
SetPreferredSize(kDeviceEntryViewSize);
}
void AudioDeviceEntryView::SetHighlighted(bool highlighted) {
is_highlighted_ = highlighted;
if (highlighted) {
SetInkDropMode(Button::InkDropMode::OFF);
set_has_ink_drop_action_on_click(false);
SetBackground(views::CreateSolidBackground(
SkColorSetA(GetInkDropBaseColor(), kEntryHighlightOpacity)));
} else {
SetInkDropMode(Button::InkDropMode::ON);
set_has_ink_drop_action_on_click(true);
SetBackground(nullptr);
}
}
void AudioDeviceEntryView::OnColorsChanged(SkColor foreground_color,
SkColor background_color) {
foreground_color_ = foreground_color;
background_color_ = background_color;
set_ink_drop_base_color(foreground_color_);
device_icon_->SetImage(
gfx::CreateVectorIcon(*icon_, kDeviceIconSize, foreground_color_));
device_name_label_->SetEnabledColor(foreground_color_);
device_name_label_->SetBackgroundColor(background_color_);
if (device_subtext_label_) {
device_subtext_label_->SetEnabledColor(foreground_color_);
device_subtext_label_->SetBackgroundColor(background_color_);
}
// Reapply highlight formatting as some effects rely on these colors.
SetHighlighted(is_highlighted_);
}
DeviceEntryUIType AudioDeviceEntryView::GetType() const {
return DeviceEntryUIType::kAudio;
}
CastDeviceEntryView::CastDeviceEntryView(SkColor foreground_color,
SkColor background_color,
const media_router::UIMediaSink& sink)
: DeviceEntryUI(foreground_color,
background_color,
sink.id,
base::UTF16ToUTF8(sink.friendly_name),
// TODO(muyaoxu): change device icon
&vector_icons::kHeadsetIcon),
CastDialogSinkButton(nullptr,
sink,
/* TODO(muyaoxu): change this to button_tag */ 0) {}
// TODO(muyaoxu): Implement this function
void CastDeviceEntryView::OnColorsChanged(SkColor foreground_color,
SkColor background_color) {}
DeviceEntryUIType CastDeviceEntryView::GetType() const {
return DeviceEntryUIType::kCast;
}
// Copyright 2020 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_DEVICE_ENTRY_UI_H_
#define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_DEVICE_ENTRY_UI_H_
#include "chrome/browser/ui/views/media_router/cast_dialog_sink_button.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/gfx/vector_icon_types.h"
#include "ui/views/controls/button/button.h"
namespace views {
class ImageView;
class Label;
} // namespace views
enum class DeviceEntryUIType {
kAudio = 0,
kCast = 1,
kMaxValue = kCast,
};
class DeviceEntryUI {
public:
DeviceEntryUI(SkColor foreground_color,
SkColor background_color,
const std::string& raw_device_id,
const std::string& device_name,
const gfx::VectorIcon* icon_,
const std::string& subtext = "");
DeviceEntryUI(const DeviceEntryUI&) = delete;
DeviceEntryUI& operator=(const DeviceEntryUI&) = delete;
virtual ~DeviceEntryUI() = default;
const std::string& raw_device_id() const { return raw_device_id_; }
const std::string& device_name() const { return device_name_; }
virtual void OnColorsChanged(SkColor foreground_color,
SkColor background_color) = 0;
virtual DeviceEntryUIType GetType() const = 0;
std::string GetEntryLabelForTesting();
bool GetEntryIsHighlightedForTesting() const { return is_highlighted_; }
protected:
SkColor foreground_color_, background_color_;
const std::string raw_device_id_;
const std::string device_name_;
bool is_highlighted_ = false;
const gfx::VectorIcon* const icon_;
views::Label* device_name_label_ = nullptr;
};
class AudioDeviceEntryView : public views::Button, public DeviceEntryUI {
public:
AudioDeviceEntryView(SkColor foreground_color,
SkColor background_color,
const std::string& raw_device_id,
const std::string& name,
const std::string& subtext = "");
~AudioDeviceEntryView() override = default;
void SetHighlighted(bool highlighted);
// DeviceEntryUI
void OnColorsChanged(SkColor foreground_color,
SkColor background_color) override;
DeviceEntryUIType GetType() const override;
private:
views::View* icon_container_ = nullptr;
views::ImageView* device_icon_ = nullptr;
views::View* labels_container_ = nullptr;
views::Label* device_subtext_label_ = nullptr;
};
class CastDeviceEntryView : public DeviceEntryUI,
public media_router::CastDialogSinkButton {
public:
CastDeviceEntryView(SkColor foreground_color,
SkColor background_color,
const media_router::UIMediaSink& sink);
~CastDeviceEntryView() override = default;
// DeviceEntryUI
void OnColorsChanged(SkColor foreground_color,
SkColor background_color) override;
DeviceEntryUIType GetType() const override;
};
#endif // CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_DEVICE_ENTRY_UI_H_
......@@ -7,6 +7,8 @@
#include "base/callback_list.h"
#include "chrome/browser/ui/global_media_controls/media_notification_device_provider.h"
#include "chrome/browser/ui/media_router/cast_dialog_controller.h"
#include "chrome/browser/ui/views/global_media_controls/media_notification_device_entry_ui.h"
#include "chrome/browser/ui/views/location_bar/icon_label_bubble_view.h"
#include "media/audio/audio_device_description.h"
#include "ui/views/controls/button/image_button.h"
......@@ -14,7 +16,6 @@
#include "ui/views/layout/box_layout.h"
namespace {
class DeviceEntryView;
class ExpandDeviceSelectorButton;
const char kAudioDevicesCountHistogramName[] =
"Media.GlobalMediaControls.NumberOfAvailableAudioDevices";
......@@ -29,10 +30,12 @@ class MediaNotificationDeviceSelectorViewDelegate;
class MediaNotificationDeviceSelectorView
: public views::View,
public views::ButtonListener,
public IconLabelBubbleView::Delegate {
public IconLabelBubbleView::Delegate,
public media_router::CastDialogController::Observer {
public:
MediaNotificationDeviceSelectorView(
MediaNotificationDeviceSelectorViewDelegate* delegate,
std::unique_ptr<media_router::CastDialogController> controller,
const std::string& current_device_id,
const SkColor& foreground_color,
const SkColor& background_color);
......@@ -56,9 +59,13 @@ class MediaNotificationDeviceSelectorView
SkColor GetIconLabelBubbleSurroundingForegroundColor() const override;
SkColor GetIconLabelBubbleBackgroundColor() const override;
views::Button* get_expand_button_for_testing();
static std::string get_entry_label_for_testing(views::View* entry_view);
static bool get_entry_is_highlighted_for_testing(views::View* entry_view);
// media_router::CastDialogController::Observer
void OnModelUpdated(const media_router::CastDialogModel& model) override;
void OnControllerInvalidated() override;
views::Button* GetExpandButtonForTesting();
std::string GetEntryLabelForTesting(views::View* entry_view);
bool GetEntryIsHighlightedForTesting(views::View* entry_view);
private:
FRIEND_TEST_ALL_PREFIXES(MediaNotificationDeviceSelectorViewTest,
......@@ -84,6 +91,8 @@ class MediaNotificationDeviceSelectorView
void ShowDevices();
void HideDevices();
void RemoveDevicesOfType(DeviceEntryUIType type);
DeviceEntryUI* GetDeviceEntryUI(views::View* view);
bool has_expand_button_been_shown_ = false;
bool have_devices_been_shown_ = false;
......@@ -93,20 +102,26 @@ class MediaNotificationDeviceSelectorView
MediaNotificationDeviceSelectorViewDelegate* const delegate_;
std::string current_device_id_;
SkColor foreground_color_, background_color_;
DeviceEntryView* current_device_entry_view_ = nullptr;
AudioDeviceEntryView* current_audio_device_entry_view_ = nullptr;
// Child views
views::View* expand_button_strip_;
ExpandDeviceSelectorButton* expand_button_;
views::View* audio_device_entries_container_;
views::View* expand_button_strip_ = nullptr;
ExpandDeviceSelectorButton* expand_button_ = nullptr;
views::View* device_entry_views_container_ = nullptr;
std::unique_ptr<MediaNotificationDeviceProvider::
GetOutputDevicesCallbackList::Subscription>
audio_device_subscription_;
std::unique_ptr<base::RepeatingCallbackList<void(bool)>::Subscription>
is_device_switching_enabled_subscription_;
std::unique_ptr<media_router::CastDialogController> cast_controller_;
// Each button has a unique tag, which is used to look up DeviceEntryUI* in
// |device_entry_ui_map_|.
int next_tag_ = 0;
std::map<int, DeviceEntryUI*> device_entry_ui_map_;
base::WeakPtrFactory<MediaNotificationDeviceSelectorView> weak_ptr_factory_{
this};
};
......
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