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 @@
#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/histogram_functions.h"
#include "base/util/ranges/algorithm.h"
#include "chrome/browser/media/router/media_router_feature.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
......@@ -159,6 +161,21 @@ void MediaNotificationService::Session::MediaSessionInfoChanged(
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(
const base::Optional<media_session::MediaPosition>& position) {
OnSessionInteractedWith();
......@@ -226,6 +243,15 @@ void MediaNotificationService::Session::SetAudioSinkId(const std::string& 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
void MediaNotificationService::Session::RecordDismissReason(
GlobalMediaControlsDismissReason reason) {
......@@ -732,6 +758,17 @@ MediaNotificationService::RegisterAudioOutputDeviceDescriptionsCallback(
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(
std::unique_ptr<MediaNotificationDeviceProvider> device_provider) {
device_provider_ = std::move(device_provider);
......
......@@ -115,6 +115,14 @@ class MediaNotificationService
RegisterAudioOutputDeviceDescriptionsCallback(
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(
std::unique_ptr<MediaNotificationDeviceProvider> device_provider);
......@@ -167,7 +175,7 @@ class MediaNotificationService
}
void MediaSessionActionsChanged(
const std::vector<media_session::mojom::MediaSessionAction>& actions)
override {}
override;
void MediaSessionChanged(
const base::Optional<base::UnguessableToken>& request_id) override {}
void MediaSessionPositionChanged(
......@@ -201,6 +209,10 @@ class MediaNotificationService
void SetAudioSinkId(const std::string& id);
std::unique_ptr<base::RepeatingCallbackList<void(bool)>::Subscription>
RegisterIsAudioDeviceSwitchingSupportedCallback(
base::RepeatingCallback<void(bool)> callback);
private:
static void RecordDismissReason(GlobalMediaControlsDismissReason reason);
......@@ -233,6 +245,13 @@ class MediaNotificationService
// True if we're in an overlay notification.
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.
mojo::Receiver<media_session::mojom::MediaControllerObserver>
observer_receiver_{this};
......
......@@ -281,8 +281,9 @@ void MediaNotificationContainerImplView::OnMediaSessionInfoChanged(
if (session_info) {
audio_sink_id_ = session_info->audio_sink_id.value_or(
media::AudioDeviceDescription::kDefaultDeviceId);
if (audio_device_selector_view_)
if (audio_device_selector_view_) {
audio_device_selector_view_->UpdateCurrentAudioDevice(audio_sink_id_);
}
}
}
......@@ -362,6 +363,14 @@ MediaNotificationContainerImplView::
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() {
return swipeable_container_->layer();
}
......
......@@ -101,6 +101,9 @@ class MediaNotificationContainerImplView
RegisterAudioOutputDeviceDescriptionsCallback(
MediaNotificationDeviceProvider::GetOutputDevicesCallbackList::
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
// the dialog.
......
......@@ -213,12 +213,19 @@ MediaNotificationDeviceSelectorView::MediaNotificationDeviceSelectorView(
// This view will become visible when devices are discovered.
SetVisible(false);
// Get a list of the connected audio output devices
// Get a list of the connected audio output devices.
audio_device_subscription_ =
delegate->RegisterAudioOutputDeviceDescriptionsCallback(
base::BindRepeating(
&MediaNotificationDeviceSelectorView::UpdateAvailableAudioDevices,
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(
......@@ -273,8 +280,7 @@ void MediaNotificationDeviceSelectorView::UpdateAvailableAudioDevices(
? current_device_id_
: media::AudioDeviceDescription::kDefaultDeviceId);
SetVisible(ShouldBeVisible(device_descriptions));
delegate_->OnDeviceSelectorViewSizeChanged();
UpdateVisibility();
}
void MediaNotificationDeviceSelectorView::OnColorsChanged(
......@@ -344,8 +350,15 @@ void MediaNotificationDeviceSelectorView::HideDevices() {
PreferredSizeChanged();
}
bool MediaNotificationDeviceSelectorView::ShouldBeVisible(
const media::AudioDeviceDescriptions& device_descriptions) {
void MediaNotificationDeviceSelectorView::UpdateVisibility() {
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
// when:
// * There are at least three devices
......@@ -361,5 +374,14 @@ bool MediaNotificationDeviceSelectorView::ShouldBeVisible(
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 @@
#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_
#include "base/callback_list.h"
#include "chrome/browser/ui/global_media_controls/media_notification_device_provider.h"
#include "media/audio/audio_device_description.h"
#include "ui/views/controls/button/image_button.h"
......@@ -35,7 +36,10 @@ class MediaNotificationDeviceSelectorView : public views::View,
void OnColorsChanged(const SkColor& foreground_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;
static std::string get_entry_label_for_testing(views::View* entry_view);
......@@ -55,13 +59,15 @@ class MediaNotificationDeviceSelectorView : public views::View,
FRIEND_TEST_ALL_PREFIXES(MediaNotificationDeviceSelectorViewTest,
DeviceButtonsChange);
bool ShouldBeVisible(
const media::AudioDeviceDescriptions& device_descriptions);
void UpdateVisibility();
bool ShouldBeVisible();
void ShowDevices();
void HideDevices();
bool is_expanded_ = false;
bool is_audio_device_switching_enabled_ = false;
MediaNotificationDeviceSelectorViewDelegate* const delegate_;
std::string current_device_id_;
SkColor foreground_color_, background_color_;
......@@ -76,6 +82,9 @@ class MediaNotificationDeviceSelectorView : public views::View,
GetOutputDevicesCallbackList::Subscription>
audio_device_subscription_;
std::unique_ptr<base::RepeatingCallbackList<void(bool)>::Subscription>
is_device_switching_enabled_subscription_;
base::WeakPtrFactory<MediaNotificationDeviceSelectorView> weak_ptr_factory_{
this};
};
......
......@@ -4,6 +4,7 @@
#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_
#include "base/callback_list.h"
#include "chrome/browser/ui/global_media_controls/media_notification_device_provider.h"
class MediaNotificationDeviceSelectorViewDelegate {
......@@ -15,6 +16,9 @@ class MediaNotificationDeviceSelectorViewDelegate {
RegisterAudioOutputDeviceDescriptionsCallback(
MediaNotificationDeviceProvider::GetOutputDevicesCallbackList::
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_
......@@ -4,6 +4,7 @@
#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/time/time.h"
#include "base/util/ranges/algorithm.h"
......@@ -80,8 +81,23 @@ class MockMediaNotificationDeviceSelectorViewDelegate
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:
std::unique_ptr<MockMediaNotificationDeviceProvider> provider_;
base::RepeatingCallback<void(bool)> supports_switching_callback_;
};
} // anonymous namespace
......@@ -269,12 +285,10 @@ TEST_F(MediaNotificationDeviceSelectorViewTest, VisibilityChanges) {
provider->AddDevice(media::AudioDeviceDescription::GetDefaultDeviceName(),
media::AudioDeviceDescription::kDefaultDeviceId);
EXPECT_CALL(delegate, OnDeviceSelectorViewSizeChanged).Times(2);
view_ = std::make_unique<MediaNotificationDeviceSelectorView>(
&delegate, "1", gfx::kPlaceholderColor, gfx::kPlaceholderColor);
EXPECT_CALL(delegate, OnDeviceSelectorViewSizeChanged).Times(1);
view_ = std::make_unique<MediaNotificationDeviceSelectorView>(
&delegate, "1", gfx::kPlaceholderColor, gfx::kPlaceholderColor);
&delegate, media::AudioDeviceDescription::kDefaultDeviceId,
gfx::kPlaceholderColor, gfx::kPlaceholderColor);
EXPECT_FALSE(view_->GetVisible());
testing::Mock::VerifyAndClearExpectations(&delegate);
......@@ -298,3 +312,20 @@ TEST_F(MediaNotificationDeviceSelectorViewTest, VisibilityChanges) {
EXPECT_TRUE(view_->GetVisible());
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