Commit 4a256d81 authored by Noah Rose Ledesma's avatar Noah Rose Ledesma Committed by Commit Bot

GMC: Hide the audio device picker when the device cannot be changed

This change makes the audio device picker invisible when the
MediaSessionInfo object reports that changing audio devices is not
possible.

The MediaNotificationContainerImplView will notify the device picker UI
when the support for device switching changes.

The MediaNotificationService will provide the device picker UI with
the support boolean when the UI is constructed.

Bug: 1120620
Change-Id: I09a35135569a79800fbe5ade0c36a5f37f6bee70
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2368051
Commit-Queue: Noah Rose Ledesma <noahrose@google.com>
Reviewed-by: default avatarTommy Steimel <steimel@chromium.org>
Cr-Commit-Position: refs/heads/master@{#803145}
parent 8b2e68dc
...@@ -4,8 +4,10 @@ ...@@ -4,8 +4,10 @@
#include "chrome/browser/ui/global_media_controls/media_notification_service.h" #include "chrome/browser/ui/global_media_controls/media_notification_service.h"
#include "base/callback_list.h"
#include "base/metrics/field_trial_params.h" #include "base/metrics/field_trial_params.h"
#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_functions.h"
#include "base/util/ranges/algorithm.h"
#include "chrome/browser/media/router/media_router_feature.h" #include "chrome/browser/media/router/media_router_feature.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser.h"
...@@ -159,6 +161,21 @@ void MediaNotificationService::Session::MediaSessionInfoChanged( ...@@ -159,6 +161,21 @@ void MediaNotificationService::Session::MediaSessionInfoChanged(
StartInactiveTimer(); StartInactiveTimer();
} }
void MediaNotificationService::Session::MediaSessionActionsChanged(
const std::vector<media_session::mojom::MediaSessionAction>& actions) {
bool is_audio_device_switching_supported =
util::ranges::find(
actions,
media_session::mojom::MediaSessionAction::kSwitchAudioDevice) !=
actions.end();
if (is_audio_device_switching_supported !=
is_audio_device_switching_supported_) {
is_audio_device_switching_supported_ = is_audio_device_switching_supported;
is_audio_device_switching_supported_callback_list_.Notify(
is_audio_device_switching_supported_);
}
}
void MediaNotificationService::Session::MediaSessionPositionChanged( void MediaNotificationService::Session::MediaSessionPositionChanged(
const base::Optional<media_session::MediaPosition>& position) { const base::Optional<media_session::MediaPosition>& position) {
OnSessionInteractedWith(); OnSessionInteractedWith();
...@@ -226,6 +243,15 @@ void MediaNotificationService::Session::SetAudioSinkId(const std::string& id) { ...@@ -226,6 +243,15 @@ void MediaNotificationService::Session::SetAudioSinkId(const std::string& id) {
controller_->SetAudioSinkId(id); controller_->SetAudioSinkId(id);
} }
std::unique_ptr<base::RepeatingCallbackList<void(bool)>::Subscription>
MediaNotificationService::Session::
RegisterIsAudioDeviceSwitchingSupportedCallback(
base::RepeatingCallback<void(bool)> callback) {
callback.Run(is_audio_device_switching_supported_);
return is_audio_device_switching_supported_callback_list_.Add(
std::move(callback));
}
// static // static
void MediaNotificationService::Session::RecordDismissReason( void MediaNotificationService::Session::RecordDismissReason(
GlobalMediaControlsDismissReason reason) { GlobalMediaControlsDismissReason reason) {
...@@ -732,6 +758,17 @@ MediaNotificationService::RegisterAudioOutputDeviceDescriptionsCallback( ...@@ -732,6 +758,17 @@ MediaNotificationService::RegisterAudioOutputDeviceDescriptionsCallback(
std::move(callback)); std::move(callback));
} }
std::unique_ptr<base::RepeatingCallbackList<void(bool)>::Subscription>
MediaNotificationService::RegisterIsAudioOutputDeviceSwitchingSupportedCallback(
const std::string& id,
base::RepeatingCallback<void(bool)> callback) {
auto it = sessions_.find(id);
DCHECK(it != sessions_.end());
return it->second.RegisterIsAudioDeviceSwitchingSupportedCallback(
std::move(callback));
}
void MediaNotificationService::set_device_provider_for_testing( void MediaNotificationService::set_device_provider_for_testing(
std::unique_ptr<MediaNotificationDeviceProvider> device_provider) { std::unique_ptr<MediaNotificationDeviceProvider> device_provider) {
device_provider_ = std::move(device_provider); device_provider_ = std::move(device_provider);
......
...@@ -115,6 +115,14 @@ class MediaNotificationService ...@@ -115,6 +115,14 @@ class MediaNotificationService
RegisterAudioOutputDeviceDescriptionsCallback( RegisterAudioOutputDeviceDescriptionsCallback(
MediaNotificationDeviceProvider::GetOutputDevicesCallback callback); MediaNotificationDeviceProvider::GetOutputDevicesCallback callback);
// Used by a |MediaNotificationAudioDeviceSelectorView| to become notified of
// audio device switching capabilities. The callback will be immediately run
// with the current availability.
std::unique_ptr<base::RepeatingCallbackList<void(bool)>::Subscription>
RegisterIsAudioOutputDeviceSwitchingSupportedCallback(
const std::string& id,
base::RepeatingCallback<void(bool)> callback);
void set_device_provider_for_testing( void set_device_provider_for_testing(
std::unique_ptr<MediaNotificationDeviceProvider> device_provider); std::unique_ptr<MediaNotificationDeviceProvider> device_provider);
...@@ -167,7 +175,7 @@ class MediaNotificationService ...@@ -167,7 +175,7 @@ class MediaNotificationService
} }
void MediaSessionActionsChanged( void MediaSessionActionsChanged(
const std::vector<media_session::mojom::MediaSessionAction>& actions) const std::vector<media_session::mojom::MediaSessionAction>& actions)
override {} override;
void MediaSessionChanged( void MediaSessionChanged(
const base::Optional<base::UnguessableToken>& request_id) override {} const base::Optional<base::UnguessableToken>& request_id) override {}
void MediaSessionPositionChanged( void MediaSessionPositionChanged(
...@@ -201,6 +209,10 @@ class MediaNotificationService ...@@ -201,6 +209,10 @@ class MediaNotificationService
void SetAudioSinkId(const std::string& id); void SetAudioSinkId(const std::string& id);
std::unique_ptr<base::RepeatingCallbackList<void(bool)>::Subscription>
RegisterIsAudioDeviceSwitchingSupportedCallback(
base::RepeatingCallback<void(bool)> callback);
private: private:
static void RecordDismissReason(GlobalMediaControlsDismissReason reason); static void RecordDismissReason(GlobalMediaControlsDismissReason reason);
...@@ -233,6 +245,13 @@ class MediaNotificationService ...@@ -233,6 +245,13 @@ class MediaNotificationService
// True if we're in an overlay notification. // True if we're in an overlay notification.
bool is_in_overlay_ = false; bool is_in_overlay_ = false;
// True if the audio output device can be switched.
bool is_audio_device_switching_supported_ = true;
// Used to notify changes in audio output device switching capabilities.
base::RepeatingCallbackList<void(bool)>
is_audio_device_switching_supported_callback_list_;
// Used to receive updates to the Media Session playback state. // Used to receive updates to the Media Session playback state.
mojo::Receiver<media_session::mojom::MediaControllerObserver> mojo::Receiver<media_session::mojom::MediaControllerObserver>
observer_receiver_{this}; observer_receiver_{this};
......
...@@ -281,8 +281,9 @@ void MediaNotificationContainerImplView::OnMediaSessionInfoChanged( ...@@ -281,8 +281,9 @@ void MediaNotificationContainerImplView::OnMediaSessionInfoChanged(
if (session_info) { if (session_info) {
audio_sink_id_ = session_info->audio_sink_id.value_or( audio_sink_id_ = session_info->audio_sink_id.value_or(
media::AudioDeviceDescription::kDefaultDeviceId); media::AudioDeviceDescription::kDefaultDeviceId);
if (audio_device_selector_view_) if (audio_device_selector_view_) {
audio_device_selector_view_->UpdateCurrentAudioDevice(audio_sink_id_); audio_device_selector_view_->UpdateCurrentAudioDevice(audio_sink_id_);
}
} }
} }
...@@ -362,6 +363,14 @@ MediaNotificationContainerImplView:: ...@@ -362,6 +363,14 @@ MediaNotificationContainerImplView::
std::move(callback)); std::move(callback));
} }
std::unique_ptr<base::RepeatingCallbackList<void(bool)>::Subscription>
MediaNotificationContainerImplView::
RegisterIsAudioOutputDeviceSwitchingSupportedCallback(
base::RepeatingCallback<void(bool)> callback) {
return service_->RegisterIsAudioOutputDeviceSwitchingSupportedCallback(
id_, std::move(callback));
}
ui::Layer* MediaNotificationContainerImplView::GetSlideOutLayer() { ui::Layer* MediaNotificationContainerImplView::GetSlideOutLayer() {
return swipeable_container_->layer(); return swipeable_container_->layer();
} }
......
...@@ -101,6 +101,9 @@ class MediaNotificationContainerImplView ...@@ -101,6 +101,9 @@ class MediaNotificationContainerImplView
RegisterAudioOutputDeviceDescriptionsCallback( RegisterAudioOutputDeviceDescriptionsCallback(
MediaNotificationDeviceProvider::GetOutputDevicesCallbackList:: MediaNotificationDeviceProvider::GetOutputDevicesCallbackList::
CallbackType callback) override; CallbackType callback) override;
std::unique_ptr<base::RepeatingCallbackList<void(bool)>::Subscription>
RegisterIsAudioOutputDeviceSwitchingSupportedCallback(
base::RepeatingCallback<void(bool)> callback) override;
// Sets up the notification to be ready to display in an overlay instead of // Sets up the notification to be ready to display in an overlay instead of
// the dialog. // the dialog.
......
...@@ -213,12 +213,19 @@ MediaNotificationDeviceSelectorView::MediaNotificationDeviceSelectorView( ...@@ -213,12 +213,19 @@ MediaNotificationDeviceSelectorView::MediaNotificationDeviceSelectorView(
// This view will become visible when devices are discovered. // This view will become visible when devices are discovered.
SetVisible(false); SetVisible(false);
// Get a list of the connected audio output devices // Get a list of the connected audio output devices.
audio_device_subscription_ = audio_device_subscription_ =
delegate->RegisterAudioOutputDeviceDescriptionsCallback( delegate->RegisterAudioOutputDeviceDescriptionsCallback(
base::BindRepeating( base::BindRepeating(
&MediaNotificationDeviceSelectorView::UpdateAvailableAudioDevices, &MediaNotificationDeviceSelectorView::UpdateAvailableAudioDevices,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
// Get the availability of audio output device switching.
is_device_switching_enabled_subscription_ =
delegate_->RegisterIsAudioOutputDeviceSwitchingSupportedCallback(
base::BindRepeating(&MediaNotificationDeviceSelectorView::
UpdateIsAudioDeviceSwitchingEnabled,
weak_ptr_factory_.GetWeakPtr()));
} }
void MediaNotificationDeviceSelectorView::UpdateCurrentAudioDevice( void MediaNotificationDeviceSelectorView::UpdateCurrentAudioDevice(
...@@ -273,8 +280,7 @@ void MediaNotificationDeviceSelectorView::UpdateAvailableAudioDevices( ...@@ -273,8 +280,7 @@ void MediaNotificationDeviceSelectorView::UpdateAvailableAudioDevices(
? current_device_id_ ? current_device_id_
: media::AudioDeviceDescription::kDefaultDeviceId); : media::AudioDeviceDescription::kDefaultDeviceId);
SetVisible(ShouldBeVisible(device_descriptions)); UpdateVisibility();
delegate_->OnDeviceSelectorViewSizeChanged();
} }
void MediaNotificationDeviceSelectorView::OnColorsChanged( void MediaNotificationDeviceSelectorView::OnColorsChanged(
...@@ -344,8 +350,15 @@ void MediaNotificationDeviceSelectorView::HideDevices() { ...@@ -344,8 +350,15 @@ void MediaNotificationDeviceSelectorView::HideDevices() {
PreferredSizeChanged(); PreferredSizeChanged();
} }
bool MediaNotificationDeviceSelectorView::ShouldBeVisible( void MediaNotificationDeviceSelectorView::UpdateVisibility() {
const media::AudioDeviceDescriptions& device_descriptions) { SetVisible(ShouldBeVisible());
delegate_->OnDeviceSelectorViewSizeChanged();
}
bool MediaNotificationDeviceSelectorView::ShouldBeVisible() {
if (!is_audio_device_switching_enabled_)
return false;
// The UI should be visible if there are more than one unique devices. That is // The UI should be visible if there are more than one unique devices. That is
// when: // when:
// * There are at least three devices // * There are at least three devices
...@@ -361,5 +374,14 @@ bool MediaNotificationDeviceSelectorView::ShouldBeVisible( ...@@ -361,5 +374,14 @@ bool MediaNotificationDeviceSelectorView::ShouldBeVisible(
media::AudioDeviceDescription::GetDefaultDeviceName(); media::AudioDeviceDescription::GetDefaultDeviceName();
}); });
} }
return device_descriptions.size() > 2; return audio_device_entries_container_->children().size() > 2;
}
void MediaNotificationDeviceSelectorView::UpdateIsAudioDeviceSwitchingEnabled(
bool enabled) {
if (enabled == is_audio_device_switching_enabled_)
return;
is_audio_device_switching_enabled_ = enabled;
UpdateVisibility();
} }
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#ifndef CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_DEVICE_SELECTOR_VIEW_H_ #ifndef CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_DEVICE_SELECTOR_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_DEVICE_SELECTOR_VIEW_H_ #define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_DEVICE_SELECTOR_VIEW_H_
#include "base/callback_list.h"
#include "chrome/browser/ui/global_media_controls/media_notification_device_provider.h" #include "chrome/browser/ui/global_media_controls/media_notification_device_provider.h"
#include "media/audio/audio_device_description.h" #include "media/audio/audio_device_description.h"
#include "ui/views/controls/button/image_button.h" #include "ui/views/controls/button/image_button.h"
...@@ -35,7 +36,10 @@ class MediaNotificationDeviceSelectorView : public views::View, ...@@ -35,7 +36,10 @@ class MediaNotificationDeviceSelectorView : public views::View,
void OnColorsChanged(const SkColor& foreground_color, void OnColorsChanged(const SkColor& foreground_color,
const SkColor& background_color); const SkColor& background_color);
// ButtonListener // Called when the audio device switching has become enabled or disabled.
void UpdateIsAudioDeviceSwitchingEnabled(bool enabled);
// views::ButtonListener
void ButtonPressed(views::Button* sender, const ui::Event& event) override; void ButtonPressed(views::Button* sender, const ui::Event& event) override;
static std::string get_entry_label_for_testing(views::View* entry_view); static std::string get_entry_label_for_testing(views::View* entry_view);
...@@ -55,13 +59,15 @@ class MediaNotificationDeviceSelectorView : public views::View, ...@@ -55,13 +59,15 @@ class MediaNotificationDeviceSelectorView : public views::View,
FRIEND_TEST_ALL_PREFIXES(MediaNotificationDeviceSelectorViewTest, FRIEND_TEST_ALL_PREFIXES(MediaNotificationDeviceSelectorViewTest,
DeviceButtonsChange); DeviceButtonsChange);
bool ShouldBeVisible( void UpdateVisibility();
const media::AudioDeviceDescriptions& device_descriptions);
bool ShouldBeVisible();
void ShowDevices(); void ShowDevices();
void HideDevices(); void HideDevices();
bool is_expanded_ = false; bool is_expanded_ = false;
bool is_audio_device_switching_enabled_ = false;
MediaNotificationDeviceSelectorViewDelegate* const delegate_; MediaNotificationDeviceSelectorViewDelegate* const delegate_;
std::string current_device_id_; std::string current_device_id_;
SkColor foreground_color_, background_color_; SkColor foreground_color_, background_color_;
...@@ -76,6 +82,9 @@ class MediaNotificationDeviceSelectorView : public views::View, ...@@ -76,6 +82,9 @@ class MediaNotificationDeviceSelectorView : public views::View,
GetOutputDevicesCallbackList::Subscription> GetOutputDevicesCallbackList::Subscription>
audio_device_subscription_; audio_device_subscription_;
std::unique_ptr<base::RepeatingCallbackList<void(bool)>::Subscription>
is_device_switching_enabled_subscription_;
base::WeakPtrFactory<MediaNotificationDeviceSelectorView> weak_ptr_factory_{ base::WeakPtrFactory<MediaNotificationDeviceSelectorView> weak_ptr_factory_{
this}; this};
}; };
......
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#ifndef CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_DEVICE_SELECTOR_VIEW_DELEGATE_H_ #ifndef CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_DEVICE_SELECTOR_VIEW_DELEGATE_H_
#define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_DEVICE_SELECTOR_VIEW_DELEGATE_H_ #define CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_DEVICE_SELECTOR_VIEW_DELEGATE_H_
#include "base/callback_list.h"
#include "chrome/browser/ui/global_media_controls/media_notification_device_provider.h" #include "chrome/browser/ui/global_media_controls/media_notification_device_provider.h"
class MediaNotificationDeviceSelectorViewDelegate { class MediaNotificationDeviceSelectorViewDelegate {
...@@ -15,6 +16,9 @@ class MediaNotificationDeviceSelectorViewDelegate { ...@@ -15,6 +16,9 @@ class MediaNotificationDeviceSelectorViewDelegate {
RegisterAudioOutputDeviceDescriptionsCallback( RegisterAudioOutputDeviceDescriptionsCallback(
MediaNotificationDeviceProvider::GetOutputDevicesCallbackList:: MediaNotificationDeviceProvider::GetOutputDevicesCallbackList::
CallbackType callback) = 0; CallbackType callback) = 0;
virtual std::unique_ptr<base::RepeatingCallbackList<void(bool)>::Subscription>
RegisterIsAudioOutputDeviceSwitchingSupportedCallback(
base::RepeatingCallback<void(bool)> callback) = 0;
}; };
#endif // CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_DEVICE_SELECTOR_VIEW_DELEGATE_H_ #endif // CHROME_BROWSER_UI_VIEWS_GLOBAL_MEDIA_CONTROLS_MEDIA_NOTIFICATION_DEVICE_SELECTOR_VIEW_DELEGATE_H_
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
#include "chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.h" #include "chrome/browser/ui/views/global_media_controls/media_notification_device_selector_view.h"
#include "base/callback_list.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "base/util/ranges/algorithm.h" #include "base/util/ranges/algorithm.h"
...@@ -80,8 +81,23 @@ class MockMediaNotificationDeviceSelectorViewDelegate ...@@ -80,8 +81,23 @@ class MockMediaNotificationDeviceSelectorViewDelegate
MockMediaNotificationDeviceProvider* GetProvider() { return provider_.get(); } MockMediaNotificationDeviceProvider* GetProvider() { return provider_.get(); }
std::unique_ptr<base::RepeatingCallbackList<void(bool)>::Subscription>
RegisterIsAudioOutputDeviceSwitchingSupportedCallback(
base::RepeatingCallback<void(bool)> callback) override {
callback.Run(supports_switching);
supports_switching_callback_ = std::move(callback);
return nullptr;
}
void RunSupportsDeviceSwitchingCallback() {
supports_switching_callback_.Run(supports_switching);
}
bool supports_switching = true;
private: private:
std::unique_ptr<MockMediaNotificationDeviceProvider> provider_; std::unique_ptr<MockMediaNotificationDeviceProvider> provider_;
base::RepeatingCallback<void(bool)> supports_switching_callback_;
}; };
} // anonymous namespace } // anonymous namespace
...@@ -269,12 +285,10 @@ TEST_F(MediaNotificationDeviceSelectorViewTest, VisibilityChanges) { ...@@ -269,12 +285,10 @@ TEST_F(MediaNotificationDeviceSelectorViewTest, VisibilityChanges) {
provider->AddDevice(media::AudioDeviceDescription::GetDefaultDeviceName(), provider->AddDevice(media::AudioDeviceDescription::GetDefaultDeviceName(),
media::AudioDeviceDescription::kDefaultDeviceId); media::AudioDeviceDescription::kDefaultDeviceId);
EXPECT_CALL(delegate, OnDeviceSelectorViewSizeChanged).Times(2);
view_ = std::make_unique<MediaNotificationDeviceSelectorView>( view_ = std::make_unique<MediaNotificationDeviceSelectorView>(
&delegate, "1", gfx::kPlaceholderColor, gfx::kPlaceholderColor); &delegate, media::AudioDeviceDescription::kDefaultDeviceId,
gfx::kPlaceholderColor, gfx::kPlaceholderColor);
EXPECT_CALL(delegate, OnDeviceSelectorViewSizeChanged).Times(1);
view_ = std::make_unique<MediaNotificationDeviceSelectorView>(
&delegate, "1", gfx::kPlaceholderColor, gfx::kPlaceholderColor);
EXPECT_FALSE(view_->GetVisible()); EXPECT_FALSE(view_->GetVisible());
testing::Mock::VerifyAndClearExpectations(&delegate); testing::Mock::VerifyAndClearExpectations(&delegate);
...@@ -298,3 +312,20 @@ TEST_F(MediaNotificationDeviceSelectorViewTest, VisibilityChanges) { ...@@ -298,3 +312,20 @@ TEST_F(MediaNotificationDeviceSelectorViewTest, VisibilityChanges) {
EXPECT_TRUE(view_->GetVisible()); EXPECT_TRUE(view_->GetVisible());
testing::Mock::VerifyAndClearExpectations(&delegate); testing::Mock::VerifyAndClearExpectations(&delegate);
} }
TEST_F(MediaNotificationDeviceSelectorViewTest, DeviceChangeIsNotSupported) {
MockMediaNotificationDeviceSelectorViewDelegate delegate;
auto* provider = delegate.GetProvider();
provider->AddDevice("Speaker", "1");
provider->AddDevice("Headphones", "2");
provider->AddDevice("Earbuds", "3");
delegate.supports_switching = false;
view_ = std::make_unique<MediaNotificationDeviceSelectorView>(
&delegate, "1", gfx::kPlaceholderColor, gfx::kPlaceholderColor);
EXPECT_FALSE(view_->GetVisible());
delegate.supports_switching = true;
delegate.RunSupportsDeviceSwitchingCallback();
EXPECT_TRUE(view_->GetVisible());
}
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