[AGC] Add AGC information row in quick settings.
Add a row in audio input in quick setting page to notify users that the ui gains is currently auto adjusted for specific apps. Screenshots: Meet app: https://screenshot.googleplex.com/6rFCkVVBxmGu2ti.png Meet in chrome: https://screenshot.googleplex.com/AB8eoXPBAfEG3Yi.png Zoom in chrome: https://screenshot.googleplex.com/4YVhbgwS8aeHHuC.png Design doc: https://docs.google.com/document/d/1K9elzEmCj3H9RabJPM0_uSAhdaQlQxwSpqki5hvjp3Q/edit#heading=h.nusylhai1f36 launch bug: launch/4226468 Cq-Depend: chromium:4577520, chromium:4576491 BUG=b:242548161 TEST=ash_unittests, chromeos_unittests, built and deploy to dut Change-Id: I04a319fba9b8a12a16bcea62f990ea7b47ada96d Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4504413 Reviewed-by: Tim Sergeant <tsergeant@chromium.org> Reviewed-by: Alex Newcomer <newcomer@chromium.org> Reviewed-by: Li-Yu Yu <aaronyu@google.com> Commit-Queue: Eddy Hsu <eddyhsu@chromium.org> Cr-Commit-Position: refs/heads/main@{#1159666}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
170c3d4377
commit
f69cfb413f
ash
ash_strings.grd
ash_strings_grd
IDS_ASH_STATUS_TRAY_AUDIO_INPUT_AGC_INFO.png.sha1IDS_ASH_STATUS_TRAY_AUDIO_SETTINGS_SHORT_STRING.png.sha1
system
chromeos/ash/components
@ -3011,6 +3011,12 @@ Connect your device to power.
|
||||
Your Chromebook or Bluetooth device is using an older version of Bluetooth. Use another input source for better audio quality.
|
||||
</message>
|
||||
|
||||
<message name="IDS_ASH_STATUS_TRAY_AUDIO_INPUT_AGC_INFO" desc="label used for the information of who is controlling mic input gain. [ICU Syntax]">
|
||||
{NUM_APPS, plural,
|
||||
=1 {Mic input controlled by <ph name="app_name">$1<ex>App</ex></ph>}
|
||||
other {Mic input controlled by # apps}}
|
||||
</message>
|
||||
|
||||
<!-- Status tray Live Caption strings. -->
|
||||
<message name="IDS_ASH_STATUS_TRAY_LIVE_CAPTION" desc="The label used in the accessibility menu of the system tray to toggle on/off Live Caption feature.">
|
||||
Live Caption
|
||||
@ -3062,6 +3068,9 @@ Connect your device to power.
|
||||
<message name="IDS_ASH_STATUS_TRAY_AUDIO_SETTINGS" desc="The label used in audio detailed page for the button that launches the OS audio settings page.">
|
||||
Audio settings
|
||||
</message>
|
||||
<message name="IDS_ASH_STATUS_TRAY_AUDIO_SETTINGS_SHORT_STRING" desc="The label used in audio detailed page for the button that launches the OS audio settings page.">
|
||||
Settings
|
||||
</message>
|
||||
<message name="IDS_ASH_STATUS_TRAY_DISPLAY" desc="The label used for the button in the status tray to show the display detailed page.">
|
||||
Display
|
||||
</message>
|
||||
|
@ -0,0 +1 @@
|
||||
4250ca7845e492b728d721e7f20043c20e6d1ad7
|
@ -0,0 +1 @@
|
||||
e393620c76a82aeabfdc8ff87c87f5f4b412c11e
|
@ -37,7 +37,10 @@
|
||||
#include "chromeos/constants/chromeos_features.h"
|
||||
#include "components/live_caption/caption_util.h"
|
||||
#include "components/live_caption/pref_names.h"
|
||||
#include "components/services/app_service/public/cpp/app_capability_access_cache_wrapper.h"
|
||||
#include "components/services/app_service/public/cpp/app_registry_cache_wrapper.h"
|
||||
#include "components/vector_icons/vector_icons.h"
|
||||
#include "media/base/media_switches.h"
|
||||
#include "ui/base/l10n/l10n_util.h"
|
||||
#include "ui/base/metadata/metadata_impl_macros.h"
|
||||
#include "ui/chromeos/styles/cros_tokens_color_mappings.h"
|
||||
@ -51,6 +54,7 @@
|
||||
#include "ui/views/accessibility/view_accessibility.h"
|
||||
#include "ui/views/background.h"
|
||||
#include "ui/views/border.h"
|
||||
#include "ui/views/controls/button/label_button.h"
|
||||
#include "ui/views/controls/button/toggle_button.h"
|
||||
#include "ui/views/controls/focus_ring.h"
|
||||
#include "ui/views/controls/highlight_path_generator.h"
|
||||
@ -160,10 +164,43 @@ class DeviceNameContainerHighlightPathGenerator
|
||||
const raw_ptr<QuickSettingsSlider, ExperimentalAsh> slider_;
|
||||
};
|
||||
|
||||
std::vector<std::string> GetNamesOfAppsAccessingMic(
|
||||
apps::AppRegistryCache* app_registry_cache,
|
||||
apps::AppCapabilityAccessCache* app_capability_access_cache) {
|
||||
if (!app_registry_cache || !app_capability_access_cache) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> app_names;
|
||||
for (const std::string& app :
|
||||
app_capability_access_cache->GetAppsAccessingMicrophone()) {
|
||||
std::string name;
|
||||
app_registry_cache->ForOneApp(app, [&name](const apps::AppUpdate& update) {
|
||||
name = update.ShortName();
|
||||
});
|
||||
if (!name.empty()) {
|
||||
app_names.push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
return app_names;
|
||||
}
|
||||
|
||||
std::u16string GetTextForAgcInfo(const std::vector<std::string>& app_names) {
|
||||
std::u16string agc_info_string = l10n_util::GetPluralStringFUTF16(
|
||||
IDS_ASH_STATUS_TRAY_AUDIO_INPUT_AGC_INFO, app_names.size());
|
||||
return app_names.size() == 1
|
||||
? l10n_util::FormatString(
|
||||
agc_info_string, {base::UTF8ToUTF16(app_names[0])}, nullptr)
|
||||
: agc_info_string;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AudioDetailedView::AudioDetailedView(DetailedViewDelegate* delegate)
|
||||
: TrayDetailedView(delegate) {
|
||||
: TrayDetailedView(delegate),
|
||||
num_stream_ignore_ui_gains_(
|
||||
CrasAudioHandler::Get()->num_stream_ignore_ui_gains()) {
|
||||
CreateItems();
|
||||
|
||||
Shell::Get()->accessibility_controller()->AddObserver(this);
|
||||
@ -176,6 +213,16 @@ AudioDetailedView::AudioDetailedView(DetailedViewDelegate* delegate)
|
||||
soda_installer->AddObserver(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Session state observer currently only used for monitoring the microphone
|
||||
// usage which is only for the information for showing AGC control.
|
||||
if (base::FeatureList::IsEnabled(media::kIgnoreUiGains)) {
|
||||
session_observation_.Observe(Shell::Get()->session_controller());
|
||||
|
||||
// Initialize with current session state.
|
||||
OnSessionStateChanged(
|
||||
Shell::Get()->session_controller()->GetSessionState());
|
||||
}
|
||||
}
|
||||
|
||||
AudioDetailedView::~AudioDetailedView() {
|
||||
@ -221,6 +268,56 @@ void AudioDetailedView::OnAccessibilityStatusChanged() {
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDetailedView::OnCapabilityAccessUpdate(
|
||||
const apps::CapabilityAccessUpdate& update) {
|
||||
if (!features::IsQsRevampEnabled()) {
|
||||
UpdateAgcInfoRow();
|
||||
} else {
|
||||
UpdateQsAgcInfoRow();
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDetailedView::OnAppCapabilityAccessCacheWillBeDestroyed(
|
||||
apps::AppCapabilityAccessCache* cache) {
|
||||
app_capability_observation_.Reset();
|
||||
app_capability_access_cache_ = nullptr;
|
||||
}
|
||||
|
||||
void AudioDetailedView::OnSessionStateChanged(
|
||||
session_manager::SessionState state) {
|
||||
// Session state observer currently only used for monitoring the microphone
|
||||
// usage which is only for the information for showing AGC control.
|
||||
if (!base::FeatureList::IsEnabled(media::kIgnoreUiGains)) {
|
||||
return;
|
||||
}
|
||||
app_capability_observation_.Reset();
|
||||
app_registry_cache_ = nullptr;
|
||||
app_capability_access_cache_ = nullptr;
|
||||
if (state != session_manager::SessionState::ACTIVE) {
|
||||
return;
|
||||
}
|
||||
auto* session_controller = Shell::Get()->session_controller();
|
||||
if (!session_controller) {
|
||||
return;
|
||||
}
|
||||
|
||||
AccountId active_user_account_id = session_controller->GetActiveAccountId();
|
||||
if (!active_user_account_id.is_valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
app_registry_cache_ =
|
||||
apps::AppRegistryCacheWrapper::Get().GetAppRegistryCache(
|
||||
active_user_account_id);
|
||||
app_capability_access_cache_ =
|
||||
apps::AppCapabilityAccessCacheWrapper::Get().GetAppCapabilityAccessCache(
|
||||
active_user_account_id);
|
||||
|
||||
if (app_capability_access_cache_) {
|
||||
app_capability_observation_.Observe(app_capability_access_cache_);
|
||||
}
|
||||
}
|
||||
|
||||
void AudioDetailedView::AddAudioSubHeader(views::View* container,
|
||||
const gfx::VectorIcon& icon,
|
||||
const int text_id) {
|
||||
@ -497,6 +594,73 @@ AudioDetailedView::CreateQsNoiseCancellationToggleRow(
|
||||
return noise_cancellation_view;
|
||||
}
|
||||
|
||||
views::Builder<views::BoxLayoutView> AudioDetailedView::CreateAgcInfoRow(
|
||||
const AudioDevice& device) {
|
||||
return views::Builder<views::BoxLayoutView>()
|
||||
.SetID(AudioDetailedViewID::kAgcInfoRow)
|
||||
.SetDefaultFlex(1)
|
||||
.SetLayoutManager(std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kHorizontal,
|
||||
kToggleButtonRowViewPadding, kToggleButtonRowViewSpacing))
|
||||
.AddChild(views::Builder<views::ImageView>().SetImage(
|
||||
ui::ImageModel::FromVectorIcon(kUnifiedMenuInfoIcon,
|
||||
cros_tokens::kCrosSysOnSurface,
|
||||
kQsSliderIconSize)))
|
||||
.AddChild(
|
||||
views::Builder<views::Label>()
|
||||
.SetText(std::u16string())
|
||||
.SetEnabledColorId(kColorAshTextColorPrimary)
|
||||
.SetHorizontalAlignment(gfx::ALIGN_LEFT)
|
||||
.SetFontList(
|
||||
gfx::FontList().DeriveWithSizeDelta(kLabelFontSizeDelta))
|
||||
.SetAutoColorReadabilityEnabled(false)
|
||||
.SetSubpixelRenderingEnabled(false)
|
||||
.SetBorder(views::CreateEmptyBorder(kToggleButtonRowLabelPadding))
|
||||
.SetID(AudioDetailedViewID::kAgcInfoLabel))
|
||||
.AddChild(views::Builder<views::LabelButton>(
|
||||
std::make_unique<views::LabelButton>(
|
||||
base::BindRepeating(&AudioDetailedView::OnSettingsButtonClicked,
|
||||
weak_factory_.GetWeakPtr()),
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_ASH_STATUS_TRAY_AUDIO_SETTINGS_SHORT_STRING))));
|
||||
}
|
||||
|
||||
std::unique_ptr<HoverHighlightView> AudioDetailedView::CreateQsAgcInfoRow(
|
||||
const AudioDevice& device) {
|
||||
auto agc_info_view = std::make_unique<HoverHighlightView>(/*listener=*/this);
|
||||
agc_info_view->SetID(AudioDetailedViewID::kAgcInfoView);
|
||||
|
||||
auto info_icon =
|
||||
std::make_unique<views::ImageView>(ui::ImageModel::FromVectorIcon(
|
||||
kUnifiedMenuInfoIcon, cros_tokens::kCrosSysOnSurface,
|
||||
kQsSliderIconSize));
|
||||
agc_info_view->AddViewAndLabel(
|
||||
std::move(info_icon),
|
||||
l10n_util::GetStringFUTF16(IDS_ASH_STATUS_TRAY_AUDIO_INPUT_AGC_INFO,
|
||||
std::u16string()));
|
||||
|
||||
// Add settings button to link to the audio settings page.
|
||||
auto settings = std::make_unique<views::LabelButton>(
|
||||
base::BindRepeating(&AudioDetailedView::OnSettingsButtonClicked,
|
||||
weak_factory_.GetWeakPtr()),
|
||||
l10n_util::GetStringUTF16(
|
||||
IDS_ASH_STATUS_TRAY_AUDIO_SETTINGS_SHORT_STRING));
|
||||
if (!TrayPopupUtils::CanOpenWebUISettings()) {
|
||||
settings->SetEnabled(false);
|
||||
}
|
||||
agc_info_view->AddRightView(settings.release());
|
||||
|
||||
agc_info_view->tri_view()->SetInsets(kQsToggleButtonRowViewPadding);
|
||||
agc_info_view->tri_view()->SetContainerLayout(
|
||||
TriView::Container::CENTER, std::make_unique<views::BoxLayout>(
|
||||
views::BoxLayout::Orientation::kVertical,
|
||||
kQsToggleButtonRowLabelPadding));
|
||||
agc_info_view->SetPreferredSize(kQsToggleButtonRowPreferredSize);
|
||||
agc_info_view->SetProperty(views::kMarginsKey, kQsToggleButtonRowMargins);
|
||||
|
||||
return agc_info_view;
|
||||
}
|
||||
|
||||
void AudioDetailedView::MaybeShowSodaMessage(speech::LanguageCode language_code,
|
||||
std::u16string message) {
|
||||
AccessibilityControllerImpl* controller =
|
||||
@ -706,6 +870,17 @@ void AudioDetailedView::UpdateScrollableList() {
|
||||
/*is_output_device=*/false);
|
||||
}
|
||||
|
||||
// AGC info row is only meaningful when UI gains is going to be ignored.
|
||||
if (base::FeatureList::IsEnabled(media::kIgnoreUiGains)) {
|
||||
if (audio_handler->GetPrimaryActiveInputNode() == device.id) {
|
||||
if (features::IsQsRevampEnabled()) {
|
||||
container->AddChildView(
|
||||
AudioDetailedView::CreateQsAgcInfoRow(device));
|
||||
UpdateQsAgcInfoRow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adds the input noise cancellation toggle.
|
||||
if (audio_handler->GetPrimaryActiveInputNode() == device.id &&
|
||||
audio_handler->IsNoiseCancellationSupportedForDevice(device.id)) {
|
||||
@ -730,6 +905,13 @@ void AudioDetailedView::UpdateScrollableList() {
|
||||
if (!features::IsQsRevampEnabled()) {
|
||||
scroll_content()->AddChildView(mic_gain_controller_->CreateMicGainSlider(
|
||||
device.id, device.IsInternalMic()));
|
||||
// AGC info row is only meaningful when UI gains is going to be ignored.
|
||||
if (base::FeatureList::IsEnabled(media::kIgnoreUiGains)) {
|
||||
if (audio_handler->GetPrimaryActiveInputNode() == device.id) {
|
||||
container->AddChildView(CreateAgcInfoRow(device).Build());
|
||||
UpdateAgcInfoRow();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a warning message if NBS is selected.
|
||||
@ -815,6 +997,70 @@ void AudioDetailedView::UpdateActiveDeviceColor(bool is_input, bool is_muted) {
|
||||
is_muted);
|
||||
}
|
||||
|
||||
void AudioDetailedView::UpdateAgcInfoRow() {
|
||||
if (!base::FeatureList::IsEnabled(media::kIgnoreUiGains)) {
|
||||
return;
|
||||
}
|
||||
if (!scroll_content()) {
|
||||
return;
|
||||
}
|
||||
views::Label* label = static_cast<views::Label*>(
|
||||
scroll_content()->GetViewByID(AudioDetailedViewID::kAgcInfoLabel));
|
||||
if (!label) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::string> app_names = GetNamesOfAppsAccessingMic(
|
||||
app_registry_cache_, app_capability_access_cache_);
|
||||
label->SetText(GetTextForAgcInfo(app_names));
|
||||
|
||||
views::View* agc_info_row =
|
||||
scroll_content()->GetViewByID(AudioDetailedViewID::kAgcInfoRow);
|
||||
CHECK(agc_info_row);
|
||||
agc_info_row->SetVisible(ShowAgcInfoRow() && !app_names.empty());
|
||||
}
|
||||
|
||||
void AudioDetailedView::UpdateQsAgcInfoRow() {
|
||||
if (!base::FeatureList::IsEnabled(media::kIgnoreUiGains)) {
|
||||
return;
|
||||
}
|
||||
if (!scroll_content()) {
|
||||
return;
|
||||
}
|
||||
HoverHighlightView* agc_info_view = static_cast<HoverHighlightView*>(
|
||||
scroll_content()->GetViewByID(AudioDetailedViewID::kAgcInfoView));
|
||||
if (!agc_info_view) {
|
||||
return;
|
||||
}
|
||||
views::Label* text_label = agc_info_view->text_label();
|
||||
CHECK(text_label);
|
||||
|
||||
std::vector<std::string> app_names = GetNamesOfAppsAccessingMic(
|
||||
app_registry_cache_, app_capability_access_cache_);
|
||||
text_label->SetText(GetTextForAgcInfo(app_names));
|
||||
agc_info_view->SetVisible(ShowAgcInfoRow() && !app_names.empty());
|
||||
}
|
||||
|
||||
bool AudioDetailedView::ShowAgcInfoRow() {
|
||||
CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
|
||||
CHECK(audio_handler);
|
||||
|
||||
// If UI gains is not going to be ignored.
|
||||
if (!base::FeatureList::IsEnabled(media::kIgnoreUiGains)) {
|
||||
return false;
|
||||
}
|
||||
// If UI gains is to be force respected.
|
||||
if (audio_handler->GetForceRespectUiGainsState()) {
|
||||
return false;
|
||||
}
|
||||
// If there's no stream ignoring UI gains.
|
||||
if (num_stream_ignore_ui_gains_ == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AudioDetailedView::HandleViewClicked(views::View* view) {
|
||||
if (live_caption_view_ && view == live_caption_view_) {
|
||||
ToggleLiveCaptionState();
|
||||
@ -907,6 +1153,15 @@ void AudioDetailedView::OnInputMutedByMicrophoneMuteSwitchChanged(bool muted) {
|
||||
UpdateActiveDeviceColor(/*is_input=*/true, muted);
|
||||
}
|
||||
|
||||
void AudioDetailedView::OnNumStreamIgnoreUiGainsChanged(int32_t num) {
|
||||
num_stream_ignore_ui_gains_ = num;
|
||||
if (!features::IsQsRevampEnabled()) {
|
||||
UpdateAgcInfoRow();
|
||||
} else {
|
||||
UpdateQsAgcInfoRow();
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_METADATA(AudioDetailedView, views::View)
|
||||
END_METADATA
|
||||
|
||||
|
@ -11,15 +11,20 @@
|
||||
|
||||
#include "ash/accessibility/accessibility_observer.h"
|
||||
#include "ash/ash_export.h"
|
||||
#include "ash/public/cpp/session/session_controller.h"
|
||||
#include "ash/public/cpp/session/session_observer.h"
|
||||
#include "ash/style/switch.h"
|
||||
#include "ash/system/tray/hover_highlight_view.h"
|
||||
#include "ash/system/tray/tray_detailed_view.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "chromeos/ash/components/audio/audio_device.h"
|
||||
#include "chromeos/ash/components/audio/cras_audio_handler.h"
|
||||
#include "components/services/app_service/public/cpp/app_capability_access_cache.h"
|
||||
#include "components/services/app_service/public/cpp/app_registry_cache.h"
|
||||
#include "components/soda/soda_installer.h"
|
||||
#include "ui/base/metadata/metadata_header_macros.h"
|
||||
#include "ui/views/controls/button/button.h"
|
||||
#include "ui/views/layout/box_layout_view.h"
|
||||
#include "ui/views/view.h"
|
||||
|
||||
namespace gfx {
|
||||
@ -32,10 +37,13 @@ class UnifiedAudioDetailedViewControllerSodaTest;
|
||||
class UnifiedAudioDetailedViewControllerTest;
|
||||
class UnifiedVolumeSliderController;
|
||||
|
||||
class ASH_EXPORT AudioDetailedView : public TrayDetailedView,
|
||||
public AccessibilityObserver,
|
||||
public speech::SodaInstaller::Observer,
|
||||
public CrasAudioHandler::AudioObserver {
|
||||
class ASH_EXPORT AudioDetailedView
|
||||
: public AccessibilityObserver,
|
||||
public apps::AppCapabilityAccessCache::Observer,
|
||||
public CrasAudioHandler::AudioObserver,
|
||||
public SessionObserver,
|
||||
public speech::SodaInstaller::Observer,
|
||||
public TrayDetailedView {
|
||||
public:
|
||||
METADATA_HEADER(AudioDetailedView);
|
||||
|
||||
@ -46,6 +54,19 @@ class ASH_EXPORT AudioDetailedView : public TrayDetailedView,
|
||||
|
||||
~AudioDetailedView() override;
|
||||
|
||||
// IDs used for the views that compose the Audio UI.
|
||||
// Note that these IDs are only guaranteed to be unique inside
|
||||
// `AudioDetailedView`.
|
||||
enum AudioDetailedViewID {
|
||||
// Starts at 1000 to prevent potential overlapping.
|
||||
kAudioDetailedView = 1000,
|
||||
// Agc information row and corresponding text label.
|
||||
kAgcInfoRow,
|
||||
kAgcInfoLabel,
|
||||
// For QsRevamp: AGC information row.
|
||||
kAgcInfoView,
|
||||
};
|
||||
|
||||
using NoiseCancellationCallback =
|
||||
base::RepeatingCallback<void(uint64_t, views::View*)>;
|
||||
static void SetMapNoiseCancellationToggleCallbackForTest(
|
||||
@ -59,8 +80,21 @@ class ASH_EXPORT AudioDetailedView : public TrayDetailedView,
|
||||
// AccessibilityObserver:
|
||||
void OnAccessibilityStatusChanged() override;
|
||||
|
||||
// apps::AppCapabilityAccessCache::Observer:
|
||||
void OnCapabilityAccessUpdate(
|
||||
const apps::CapabilityAccessUpdate& update) override;
|
||||
void OnAppCapabilityAccessCacheWillBeDestroyed(
|
||||
apps::AppCapabilityAccessCache* cache) override;
|
||||
|
||||
// SessionObserver:
|
||||
void OnSessionStateChanged(session_manager::SessionState state) override;
|
||||
|
||||
// CrasAudioHandler::AudioObserver:
|
||||
void OnNumStreamIgnoreUiGainsChanged(int32_t num) override;
|
||||
|
||||
private:
|
||||
friend class AudioDetailedViewTest;
|
||||
friend class AudioDetailedViewAgcInfoTest;
|
||||
friend class UnifiedAudioDetailedViewControllerSodaTest;
|
||||
friend class UnifiedAudioDetailedViewControllerTest;
|
||||
|
||||
@ -91,6 +125,14 @@ class ASH_EXPORT AudioDetailedView : public TrayDetailedView,
|
||||
std::unique_ptr<HoverHighlightView> CreateQsNoiseCancellationToggleRow(
|
||||
const AudioDevice& device);
|
||||
|
||||
// Creates the agc info row in the input subsection.
|
||||
views::Builder<views::BoxLayoutView> CreateAgcInfoRow(
|
||||
const AudioDevice& device);
|
||||
|
||||
// For QsRevamp: Creates the agc info row in the input subsection.
|
||||
std::unique_ptr<HoverHighlightView> CreateQsAgcInfoRow(
|
||||
const AudioDevice& device);
|
||||
|
||||
// Sets the subtext for `live_caption_view_` based on whether live caption has
|
||||
// updated if this feature is enabled and visible in tray.
|
||||
void MaybeShowSodaMessage(speech::LanguageCode language_code,
|
||||
@ -124,6 +166,12 @@ class ASH_EXPORT AudioDetailedView : public TrayDetailedView,
|
||||
// called when the input/output node's mute state changes.
|
||||
void UpdateActiveDeviceColor(bool is_input, bool is_muted);
|
||||
|
||||
// Updates the label of AGC info when accessibility to microphone changed.
|
||||
// Hide AGC info row if no apps is requesting AGC stream.
|
||||
void UpdateAgcInfoRow();
|
||||
void UpdateQsAgcInfoRow();
|
||||
bool ShowAgcInfoRow();
|
||||
|
||||
// TrayDetailedView:
|
||||
void HandleViewClicked(views::View* view) override;
|
||||
void CreateExtraTitleRowButtons() override;
|
||||
@ -151,6 +199,9 @@ class ASH_EXPORT AudioDetailedView : public TrayDetailedView,
|
||||
AudioDeviceList input_devices_;
|
||||
AudioDeviceMap device_map_;
|
||||
uint64_t focused_device_id_ = -1;
|
||||
|
||||
int num_stream_ignore_ui_gains_ = 0;
|
||||
|
||||
// Owned by the views hierarchy.
|
||||
raw_ptr<HoverHighlightView, ExperimentalAsh> live_caption_view_ = nullptr;
|
||||
raw_ptr<views::ImageView, ExperimentalAsh> live_caption_icon_ = nullptr;
|
||||
@ -161,6 +212,14 @@ class ASH_EXPORT AudioDetailedView : public TrayDetailedView,
|
||||
raw_ptr<Switch, ExperimentalAsh> noise_cancellation_button_ = nullptr;
|
||||
raw_ptr<views::Button, ExperimentalAsh> settings_button_ = nullptr;
|
||||
|
||||
base::ScopedObservation<SessionController, SessionObserver>
|
||||
session_observation_{this};
|
||||
base::ScopedObservation<apps::AppCapabilityAccessCache,
|
||||
apps::AppCapabilityAccessCache::Observer>
|
||||
app_capability_observation_{this};
|
||||
raw_ptr<apps::AppRegistryCache> app_registry_cache_;
|
||||
raw_ptr<apps::AppCapabilityAccessCache> app_capability_access_cache_;
|
||||
|
||||
base::WeakPtrFactory<AudioDetailedView> weak_factory_{this};
|
||||
};
|
||||
|
||||
|
@ -8,9 +8,17 @@
|
||||
|
||||
#include "ash/constants/ash_features.h"
|
||||
#include "ash/public/cpp/test/test_system_tray_client.h"
|
||||
#include "ash/shell.h"
|
||||
#include "ash/system/tray/detailed_view_delegate.h"
|
||||
#include "ash/test/ash_test_base.h"
|
||||
#include "base/memory/raw_ptr.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "components/services/app_service/public/cpp/app_capability_access_cache.h"
|
||||
#include "components/services/app_service/public/cpp/app_capability_access_cache_wrapper.h"
|
||||
#include "components/services/app_service/public/cpp/app_registry_cache.h"
|
||||
#include "components/services/app_service/public/cpp/app_registry_cache_wrapper.h"
|
||||
#include "components/user_manager/fake_user_manager.h"
|
||||
#include "media/base/media_switches.h"
|
||||
#include "ui/views/test/views_test_utils.h"
|
||||
#include "ui/views/widget/widget.h"
|
||||
|
||||
@ -83,4 +91,137 @@ TEST_F(AudioDetailedViewTest, PressingSettingsButtonOpensSettings) {
|
||||
EXPECT_EQ(1, detailed_view_delegate_.close_bubble_count_);
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
class AudioDetailedViewAgcInfoTest
|
||||
: public AudioDetailedViewTest,
|
||||
public testing::WithParamInterface<testing::tuple<bool, bool, bool>> {
|
||||
public:
|
||||
void SetUp() override {
|
||||
scoped_feature_list_.InitWithFeatureStates(
|
||||
{{media::kIgnoreUiGains, IsIgnoreUiGainsEnabled()},
|
||||
{features::kQsRevamp, IsQsRevampEnabled()}});
|
||||
|
||||
AudioDetailedViewTest::SetUp();
|
||||
|
||||
CrasAudioHandler* audio_handler = CrasAudioHandler::Get();
|
||||
CHECK(audio_handler);
|
||||
audio_handler->SetForceRespectUiGainsState(IsForceRespectUiGainsEnabled());
|
||||
|
||||
account_id_ = Shell::Get()->session_controller()->GetActiveAccountId();
|
||||
registry_cache_.SetAccountId(account_id_);
|
||||
apps::AppRegistryCacheWrapper::Get().AddAppRegistryCache(account_id_,
|
||||
®istry_cache_);
|
||||
capability_access_cache_.SetAccountId(account_id_);
|
||||
apps::AppCapabilityAccessCacheWrapper::Get().AddAppCapabilityAccessCache(
|
||||
account_id_, &capability_access_cache_);
|
||||
|
||||
audio_detailed_view_->Update();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
AudioDetailedViewTest::TearDown();
|
||||
apps::AppRegistryCacheWrapper::Get().RemoveAppRegistryCache(
|
||||
®istry_cache_);
|
||||
apps::AppCapabilityAccessCacheWrapper::Get().RemoveAppCapabilityAccessCache(
|
||||
&capability_access_cache_);
|
||||
registry_cache_.ReinitializeForTesting();
|
||||
}
|
||||
|
||||
bool IsIgnoreUiGainsEnabled() { return std::get<0>(GetParam()); }
|
||||
bool IsForceRespectUiGainsEnabled() { return std::get<1>(GetParam()); }
|
||||
bool IsQsRevampEnabled() { return std::get<2>(GetParam()); }
|
||||
|
||||
views::View* GetAgcInfoView() {
|
||||
if (IsQsRevampEnabled()) {
|
||||
return audio_detailed_view_->GetViewByID(
|
||||
AudioDetailedView::AudioDetailedViewID::kAgcInfoView);
|
||||
} else {
|
||||
return audio_detailed_view_->GetViewByID(
|
||||
AudioDetailedView::AudioDetailedViewID::kAgcInfoRow);
|
||||
}
|
||||
}
|
||||
|
||||
static apps::AppPtr MakeApp(const char* app_id, const char* name) {
|
||||
apps::AppPtr app =
|
||||
std::make_unique<apps::App>(apps::AppType::kChromeApp, app_id);
|
||||
app->name = name;
|
||||
app->short_name = name;
|
||||
return app;
|
||||
}
|
||||
|
||||
static apps::CapabilityAccessPtr MakeCapabilityAccess(
|
||||
const char* app_id,
|
||||
absl::optional<bool> mic) {
|
||||
apps::CapabilityAccessPtr access =
|
||||
std::make_unique<apps::CapabilityAccess>(app_id);
|
||||
access->camera = false;
|
||||
access->microphone = mic;
|
||||
return access;
|
||||
}
|
||||
|
||||
void LaunchApp(const char* id,
|
||||
const char* name,
|
||||
absl::optional<bool> use_mic) {
|
||||
std::vector<apps::AppPtr> registry_deltas;
|
||||
registry_deltas.push_back(MakeApp(id, name));
|
||||
registry_cache_.OnApps(std::move(registry_deltas), apps::AppType::kUnknown,
|
||||
/* should_notify_initialized = */ false);
|
||||
|
||||
std::vector<apps::CapabilityAccessPtr> capability_access_deltas;
|
||||
capability_access_deltas.push_back(MakeCapabilityAccess(id, use_mic));
|
||||
capability_access_cache_.OnCapabilityAccesses(
|
||||
std::move(capability_access_deltas));
|
||||
}
|
||||
|
||||
AccountId account_id_;
|
||||
|
||||
apps::AppRegistryCache registry_cache_;
|
||||
apps::AppCapabilityAccessCache capability_access_cache_;
|
||||
|
||||
base::test::ScopedFeatureList scoped_feature_list_;
|
||||
};
|
||||
|
||||
TEST_P(AudioDetailedViewAgcInfoTest, AgcInfoRowShowInProperConditions) {
|
||||
const char* app_id = "app";
|
||||
const char* app_name = "App name";
|
||||
|
||||
GetSessionControllerClient()->SetSessionState(
|
||||
session_manager::SessionState::ACTIVE);
|
||||
audio_detailed_view_->OnSessionStateChanged(
|
||||
session_manager::SessionState::ACTIVE);
|
||||
|
||||
views::View* agc_info = GetAgcInfoView();
|
||||
if (!IsIgnoreUiGainsEnabled()) {
|
||||
ASSERT_EQ(agc_info, nullptr);
|
||||
return;
|
||||
}
|
||||
ASSERT_NE(agc_info, nullptr);
|
||||
|
||||
// Launch an app accessing mic and requesting ignore UI gains.
|
||||
LaunchApp(app_id, app_name, true);
|
||||
audio_detailed_view_->OnNumStreamIgnoreUiGainsChanged(1);
|
||||
EXPECT_EQ(agc_info->GetVisible(), !IsForceRespectUiGainsEnabled());
|
||||
|
||||
// Launch an app accessing mic but not requesting ignore UI gains.
|
||||
LaunchApp(app_id, app_name, true);
|
||||
audio_detailed_view_->OnNumStreamIgnoreUiGainsChanged(0);
|
||||
EXPECT_EQ(agc_info->GetVisible(), false);
|
||||
|
||||
// Launch an app not accessing mic but requesting ignore UI gains.
|
||||
// This should not happen in real cases though.
|
||||
LaunchApp(app_id, app_name, false);
|
||||
audio_detailed_view_->OnNumStreamIgnoreUiGainsChanged(1);
|
||||
EXPECT_EQ(agc_info->GetVisible(), false);
|
||||
|
||||
// Launch an app not accessing mic and not requesting ignore UI gains.
|
||||
LaunchApp(app_id, app_name, false);
|
||||
audio_detailed_view_->OnNumStreamIgnoreUiGainsChanged(0);
|
||||
EXPECT_EQ(agc_info->GetVisible(), false);
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(AudioDetailedViewAgcInfoVisibleTest,
|
||||
AudioDetailedViewAgcInfoTest,
|
||||
testing::Combine(testing::Bool(),
|
||||
testing::Bool(),
|
||||
testing::Bool()));
|
||||
|
||||
} // namespace ash
|
||||
|
@ -134,6 +134,9 @@ void CrasAudioHandler::AudioObserver::OnSurveyTriggered(
|
||||
|
||||
void CrasAudioHandler::AudioObserver::OnSpeakOnMuteDetected() {}
|
||||
|
||||
void CrasAudioHandler::AudioObserver::OnNumStreamIgnoreUiGainsChanged(
|
||||
int32_t num) {}
|
||||
|
||||
void CrasAudioHandler::NumberOfNonChromeOutputStreamsChanged() {
|
||||
GetNumberOfNonChromeOutputStreams();
|
||||
}
|
||||
@ -1262,6 +1265,13 @@ void CrasAudioHandler::SpeakOnMuteDetected() {
|
||||
}
|
||||
}
|
||||
|
||||
void CrasAudioHandler::NumStreamIgnoreUiGains(int32_t num) {
|
||||
num_stream_ignore_ui_gains_ = num;
|
||||
for (auto& observer : observers_) {
|
||||
observer.OnNumStreamIgnoreUiGainsChanged(num);
|
||||
}
|
||||
}
|
||||
|
||||
void CrasAudioHandler::ResendBluetoothBattery() {
|
||||
CrasAudioClient::Get()->ResendBluetoothBattery();
|
||||
}
|
||||
@ -1423,6 +1433,7 @@ void CrasAudioHandler::InitializeAudioAfterCrasServiceAvailable(
|
||||
GetNumberOfOutputStreams();
|
||||
GetNumberOfNonChromeOutputStreams();
|
||||
GetNumberOfInputStreamsWithPermissionInternal();
|
||||
GetNumStreamIgnoreUiGains();
|
||||
CrasAudioClient::Get()->SetFixA2dpPacketSize(
|
||||
base::FeatureList::IsEnabled(features::kBluetoothFixA2dpPacketSize));
|
||||
|
||||
@ -2474,6 +2485,10 @@ bool CrasAudioHandler::system_agc_supported() const {
|
||||
return system_agc_supported_;
|
||||
}
|
||||
|
||||
int32_t CrasAudioHandler::num_stream_ignore_ui_gains() const {
|
||||
return num_stream_ignore_ui_gains_;
|
||||
}
|
||||
|
||||
// GetSystemAgcSupported() is only called in the same thread
|
||||
// as the CrasAudioHandler constructor. We are safe here without
|
||||
// thread check, because unittest may not have the task runner
|
||||
@ -2493,6 +2508,30 @@ void CrasAudioHandler::HandleGetSystemAgcSupported(
|
||||
system_agc_supported_ = system_agc_supported.value();
|
||||
}
|
||||
|
||||
void CrasAudioHandler::GetNumStreamIgnoreUiGains() {
|
||||
CrasAudioClient::Get()->GetNumStreamIgnoreUiGains(
|
||||
base::BindOnce(&CrasAudioHandler::HandleGetNumStreamIgnoreUiGains,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
void CrasAudioHandler::HandleGetNumStreamIgnoreUiGains(
|
||||
absl::optional<int32_t> new_stream_ignore_ui_gains_count) {
|
||||
if (!new_stream_ignore_ui_gains_count.has_value()) {
|
||||
LOG(ERROR) << "Failed to retrieve number of ignore ui gains streams.";
|
||||
return;
|
||||
}
|
||||
DCHECK_GE(*new_stream_ignore_ui_gains_count, 0);
|
||||
|
||||
if (*new_stream_ignore_ui_gains_count != num_stream_ignore_ui_gains_) {
|
||||
for (auto& observer : observers_) {
|
||||
observer.OnNumStreamIgnoreUiGainsChanged(
|
||||
*new_stream_ignore_ui_gains_count);
|
||||
}
|
||||
}
|
||||
|
||||
num_stream_ignore_ui_gains_ = *new_stream_ignore_ui_gains_count;
|
||||
}
|
||||
|
||||
ScopedCrasAudioHandlerForTesting::ScopedCrasAudioHandlerForTesting() {
|
||||
CHECK(!CrasAudioClient::Get())
|
||||
<< "ScopedCrasAudioHandlerForTesting expects that there is no "
|
||||
|
@ -187,6 +187,9 @@ class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_AUDIO) CrasAudioHandler
|
||||
// Called when a speak-on-mute is detected.
|
||||
virtual void OnSpeakOnMuteDetected();
|
||||
|
||||
// Called when num-stream-ignore-ui-gains state is changed.
|
||||
virtual void OnNumStreamIgnoreUiGainsChanged(int32_t num);
|
||||
|
||||
protected:
|
||||
AudioObserver();
|
||||
virtual ~AudioObserver();
|
||||
@ -534,6 +537,9 @@ class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_AUDIO) CrasAudioHandler
|
||||
// Returns if system AGC is supported in CRAS or not.
|
||||
bool system_agc_supported() const;
|
||||
|
||||
// Returns number of streams ignoring UI gains.
|
||||
int32_t num_stream_ignore_ui_gains() const;
|
||||
|
||||
// Asks CRAS to resend BluetoothBatteryChanged signal, used in cases when
|
||||
// Chrome cleans up the stored battery information but still has the device
|
||||
// connected afterward. For example: User logout.
|
||||
@ -569,6 +575,7 @@ class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_AUDIO) CrasAudioHandler
|
||||
survey_specific_data) override;
|
||||
void SpeakOnMuteDetected() override;
|
||||
void NumberOfNonChromeOutputStreamsChanged() override;
|
||||
void NumStreamIgnoreUiGains(int32_t num) override;
|
||||
|
||||
// AudioPrefObserver overrides.
|
||||
void OnAudioPolicyPrefChanged() override;
|
||||
@ -840,6 +847,13 @@ class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_AUDIO) CrasAudioHandler
|
||||
// Handle null Metadata from MediaSession.
|
||||
void HandleMediaSessionMetadataReset();
|
||||
|
||||
// Calls CRAS over D-Bus to get the number of streams ignoring Ui Gains.
|
||||
void GetNumStreamIgnoreUiGains();
|
||||
|
||||
// Handle dbus callback for GetNumStreamIgnoreUiGains.
|
||||
void HandleGetNumStreamIgnoreUiGains(
|
||||
absl::optional<int32_t> num_stream_ignore_ui_gains);
|
||||
|
||||
mojo::Remote<media_session::mojom::MediaControllerManager>
|
||||
media_controller_manager_;
|
||||
|
||||
@ -924,6 +938,8 @@ class COMPONENT_EXPORT(CHROMEOS_ASH_COMPONENTS_AUDIO) CrasAudioHandler
|
||||
|
||||
cras::DisplayRotation display_rotation_ = cras::DisplayRotation::ROTATE_0;
|
||||
|
||||
int num_stream_ignore_ui_gains_ = 0;
|
||||
|
||||
base::WeakPtrFactory<CrasAudioHandler> weak_ptr_factory_{this};
|
||||
};
|
||||
|
||||
|
@ -162,6 +162,15 @@ class CrasAudioClientImpl : public CrasAudioClient {
|
||||
weak_ptr_factory_.GetWeakPtr()),
|
||||
base::BindOnce(&CrasAudioClientImpl::SignalConnected,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
|
||||
// Monitor the D-Bus signal for is any stream ignore ui gains changed.
|
||||
cras_proxy_->ConnectToSignal(
|
||||
cras::kCrasControlInterface, cras::kNumStreamIgnoreUiGainsChanged,
|
||||
base::BindRepeating(
|
||||
&CrasAudioClientImpl::NumStreamIgnoreUiGainsReceived,
|
||||
weak_ptr_factory_.GetWeakPtr()),
|
||||
base::BindOnce(&CrasAudioClientImpl::SignalConnected,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
}
|
||||
|
||||
CrasAudioClientImpl(const CrasAudioClientImpl&) = delete;
|
||||
@ -606,6 +615,16 @@ class CrasAudioClientImpl : public CrasAudioClient {
|
||||
base::DoNothing());
|
||||
}
|
||||
|
||||
void GetNumStreamIgnoreUiGains(
|
||||
chromeos::DBusMethodCallback<int32_t> callback) override {
|
||||
dbus::MethodCall method_call(cras::kCrasControlInterface,
|
||||
cras::kGetNumStreamIgnoreUiGains);
|
||||
cras_proxy_->CallMethod(
|
||||
&method_call, dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
|
||||
base::BindOnce(&CrasAudioClientImpl::OnGetNumStreamIgnoreUiGains,
|
||||
weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
|
||||
}
|
||||
|
||||
private:
|
||||
// Called when the cras signal is initially connected.
|
||||
void SignalConnected(const std::string& interface_name,
|
||||
@ -865,6 +884,17 @@ class CrasAudioClientImpl : public CrasAudioClient {
|
||||
}
|
||||
}
|
||||
|
||||
void NumStreamIgnoreUiGainsReceived(dbus::Signal* signal) {
|
||||
dbus::MessageReader reader(signal);
|
||||
int32_t num;
|
||||
if (!reader.PopInt32(&num)) {
|
||||
LOG(ERROR) << "Error reading signal from cras:" << signal->ToString();
|
||||
}
|
||||
for (auto& observer : observers_) {
|
||||
observer.NumStreamIgnoreUiGains(num);
|
||||
}
|
||||
}
|
||||
|
||||
void OnGetDefaultOutputBufferSize(chromeos::DBusMethodCallback<int> callback,
|
||||
dbus::Response* response) {
|
||||
if (!response) {
|
||||
@ -1250,6 +1280,25 @@ class CrasAudioClientImpl : public CrasAudioClient {
|
||||
std::move(callback).Run(speak_on_mute_detection_enabled);
|
||||
}
|
||||
|
||||
void OnGetNumStreamIgnoreUiGains(
|
||||
chromeos::DBusMethodCallback<int32_t> callback,
|
||||
dbus::Response* response) {
|
||||
if (!response) {
|
||||
LOG(ERROR) << "Error calling " << cras::kGetNumStreamIgnoreUiGains;
|
||||
std::move(callback).Run(absl::nullopt);
|
||||
return;
|
||||
}
|
||||
int32_t num_stream_ignore_ui_gains = 0;
|
||||
dbus::MessageReader reader(response);
|
||||
if (!reader.PopInt32(&num_stream_ignore_ui_gains)) {
|
||||
LOG(ERROR) << "Error reading response from cras: "
|
||||
<< response->ToString();
|
||||
std::move(callback).Run(absl::nullopt);
|
||||
return;
|
||||
}
|
||||
std::move(callback).Run(num_stream_ignore_ui_gains);
|
||||
}
|
||||
|
||||
raw_ptr<dbus::ObjectProxy, ExperimentalAsh> cras_proxy_ = nullptr;
|
||||
base::ObserverList<Observer>::Unchecked observers_;
|
||||
|
||||
@ -1299,6 +1348,8 @@ void CrasAudioClient::Observer::SpeakOnMuteDetected() {}
|
||||
|
||||
void CrasAudioClient::Observer::NumberOfNonChromeOutputStreamsChanged() {}
|
||||
|
||||
void CrasAudioClient::Observer::NumStreamIgnoreUiGains(int32_t num) {}
|
||||
|
||||
CrasAudioClient::CrasAudioClient() {
|
||||
DCHECK(!g_instance);
|
||||
g_instance = this;
|
||||
|
@ -81,6 +81,9 @@ class COMPONENT_EXPORT(DBUS_AUDIO) CrasAudioClient {
|
||||
// Called when NumberOfNonChromeOutputStreamsChanged is detected.
|
||||
virtual void NumberOfNonChromeOutputStreamsChanged();
|
||||
|
||||
// Called when num-stream-ignore-ui-gains is changed.
|
||||
virtual void NumStreamIgnoreUiGains(int32_t num);
|
||||
|
||||
protected:
|
||||
virtual ~Observer();
|
||||
};
|
||||
@ -275,6 +278,10 @@ class COMPONENT_EXPORT(DBUS_AUDIO) CrasAudioClient {
|
||||
// Sets input force respect ui gains state to |force_repsect_ui_gains| value.
|
||||
virtual void SetForceRespectUiGains(bool force_respect_ui_gains) = 0;
|
||||
|
||||
// Gets the number of streams ignoring UI Gains.
|
||||
virtual void GetNumStreamIgnoreUiGains(
|
||||
chromeos::DBusMethodCallback<int> callback) = 0;
|
||||
|
||||
protected:
|
||||
friend class CrasAudioClientTest;
|
||||
|
||||
|
@ -127,6 +127,7 @@ class MockObserver : public CrasAudioClient::Observer {
|
||||
survey_specific_data));
|
||||
MOCK_METHOD0(SpeakOnMuteDetected, void());
|
||||
MOCK_METHOD0(NumberOfNonChromeOutputStreamsChanged, void());
|
||||
MOCK_METHOD1(NumStreamIgnoreUiGains, void(int32_t num));
|
||||
};
|
||||
|
||||
// Expect the reader to be empty.
|
||||
@ -507,6 +508,15 @@ class CrasAudioClientTest : public testing::Test {
|
||||
.WillRepeatedly(
|
||||
Invoke(this, &CrasAudioClientTest::OnSpeakOnMuteDetected));
|
||||
|
||||
// Set an expectation so mock_cras_proxy's monitoring
|
||||
// SurveyTrigger ConnectToSignal will use
|
||||
// OnNumStreamIgnoreUiGains() to run the callback.
|
||||
EXPECT_CALL(*mock_cras_proxy_.get(),
|
||||
DoConnectToSignal(interface_name_,
|
||||
cras::kNumStreamIgnoreUiGainsChanged, _, _))
|
||||
.WillRepeatedly(Invoke(
|
||||
this, &CrasAudioClientTest::OnNumStreamIgnoreUiGainsChanged));
|
||||
|
||||
// Set an expectation so mock_bus's GetObjectProxy() for the given
|
||||
// service name and the object path will return mock_cras_proxy_.
|
||||
EXPECT_CALL(*mock_bus_.get(),
|
||||
@ -629,6 +639,12 @@ class CrasAudioClientTest : public testing::Test {
|
||||
number_of_non_chrome_output_streams_changed_handler_.Run(signal);
|
||||
}
|
||||
|
||||
// Send num-stream-ignore-ui-gains changed signal to the tested client.
|
||||
void SendNumStreamIgnoreUiGainsSignal(dbus::Signal* signal) {
|
||||
ASSERT_FALSE(num_stream_ignore_ui_gains_handler_.is_null());
|
||||
num_stream_ignore_ui_gains_handler_.Run(signal);
|
||||
}
|
||||
|
||||
CrasAudioClient* client() { return CrasAudioClient::Get(); }
|
||||
|
||||
// The interface name.
|
||||
@ -672,6 +688,8 @@ class CrasAudioClientTest : public testing::Test {
|
||||
// tested client.
|
||||
dbus::ObjectProxy::SignalCallback
|
||||
number_of_non_chrome_output_streams_changed_handler_;
|
||||
// The NumStreamIgnoreUiGains signal handler given by the tested client.
|
||||
dbus::ObjectProxy::SignalCallback num_stream_ignore_ui_gains_handler_;
|
||||
// The name of the method which is expected to be called.
|
||||
std::string expected_method_name_;
|
||||
// The response which the mock cras proxy returns.
|
||||
@ -871,6 +889,20 @@ class CrasAudioClientTest : public testing::Test {
|
||||
interface_name, signal_name, success));
|
||||
}
|
||||
|
||||
// Checks the requested interface name and signal name.
|
||||
// Used to implement the mock cras proxy.
|
||||
void OnNumStreamIgnoreUiGainsChanged(
|
||||
const std::string& interface_name,
|
||||
const std::string& signal_name,
|
||||
const dbus::ObjectProxy::SignalCallback& signal_callback,
|
||||
dbus::ObjectProxy::OnConnectedCallback* on_connected_callback) {
|
||||
num_stream_ignore_ui_gains_handler_ = signal_callback;
|
||||
constexpr bool success = true;
|
||||
task_environment_.GetMainThreadTaskRunner()->PostTask(
|
||||
FROM_HERE, base::BindOnce(std::move(*on_connected_callback),
|
||||
interface_name, signal_name, success));
|
||||
}
|
||||
|
||||
// Checks the content of the method call and returns the response.
|
||||
// Used to implement the mock cras proxy.
|
||||
void OnCallMethod(dbus::MethodCall* method_call,
|
||||
@ -1139,6 +1171,31 @@ TEST_F(CrasAudioClientTest, SpeakOnMuteDetected) {
|
||||
base::RunLoop().RunUntilIdle();
|
||||
}
|
||||
|
||||
TEST_F(CrasAudioClientTest, NumStreamIgnoreUiGainsChanged) {
|
||||
const int32_t kNumStream = 1;
|
||||
|
||||
dbus::Signal signal(cras::kCrasControlInterface,
|
||||
cras::kNumStreamIgnoreUiGainsChanged);
|
||||
dbus::MessageWriter writer(&signal);
|
||||
writer.AppendInt32(kNumStream);
|
||||
|
||||
MockObserver observer;
|
||||
EXPECT_CALL(observer, NumStreamIgnoreUiGains(kNumStream)).Times(1);
|
||||
|
||||
client()->AddObserver(&observer);
|
||||
|
||||
SendNumStreamIgnoreUiGainsSignal(&signal);
|
||||
|
||||
client()->RemoveObserver(&observer);
|
||||
|
||||
EXPECT_CALL(observer, NumStreamIgnoreUiGains(kNumStream)).Times(0);
|
||||
|
||||
// Run the signal callback again and make sure the observer isn't called.
|
||||
SendNumStreamIgnoreUiGainsSignal(&signal);
|
||||
|
||||
base::RunLoop().RunUntilIdle();
|
||||
}
|
||||
|
||||
TEST_F(CrasAudioClientTest, NodesChanged) {
|
||||
// Create a signal.
|
||||
dbus::Signal signal(cras::kCrasControlInterface, cras::kNodesChanged);
|
||||
|
@ -458,4 +458,9 @@ void FakeCrasAudioClient::SetForceRespectUiGains(
|
||||
force_respect_ui_gains_enabled_ = force_respect_ui_gains_enabled;
|
||||
}
|
||||
|
||||
void FakeCrasAudioClient::GetNumStreamIgnoreUiGains(
|
||||
chromeos::DBusMethodCallback<int> callback) {
|
||||
std::move(callback).Run(false);
|
||||
}
|
||||
|
||||
} // namespace ash
|
||||
|
@ -95,6 +95,8 @@ class COMPONENT_EXPORT(DBUS_AUDIO) FakeCrasAudioClient
|
||||
void WaitForServiceToBeAvailable(
|
||||
chromeos::WaitForServiceToBeAvailableCallback callback) override;
|
||||
void SetForceRespectUiGains(bool force_respect_ui_gains_enabled) override;
|
||||
void GetNumStreamIgnoreUiGains(
|
||||
chromeos::DBusMethodCallback<int> callback) override;
|
||||
|
||||
// Sets the number of non chrome audio streams in output mode.
|
||||
void SetNumberOfNonChromeOutputStreams(int32_t streams);
|
||||
|
Reference in New Issue
Block a user