Skip to content

Commit

Permalink
GMC: Hide the audio device picker when the device cannot be changed
Browse files Browse the repository at this point in the history
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: Tommy Steimel <steimel@chromium.org>
Cr-Commit-Position: refs/heads/master@{#803145}
  • Loading branch information
Noah Rose Ledesma authored and Commit Bot committed Aug 31, 2020
1 parent 8b2e68d commit 4a256d8
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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_);
}
}
}

Expand Down Expand Up @@ -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();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -273,8 +280,7 @@ void MediaNotificationDeviceSelectorView::UpdateAvailableAudioDevices(
? current_device_id_
: media::AudioDeviceDescription::kDefaultDeviceId);

SetVisible(ShouldBeVisible(device_descriptions));
delegate_->OnDeviceSelectorViewSizeChanged();
UpdateVisibility();
}

void MediaNotificationDeviceSelectorView::OnColorsChanged(
Expand Down Expand Up @@ -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
Expand All @@ -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();
}
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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);
Expand All @@ -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_;
Expand All @@ -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};
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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_
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand All @@ -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());
}

0 comments on commit 4a256d8

Please sign in to comment.