0

VC UI: Add nudge for both camera and microphone in use while disabled

Due to some constraint in VideoConferenceManagerAsh, when both
microphone and camera is being accessed when disabled,
`HandleDeviceUsedWhileDisabled` will be called twice for each device.
Thus, we need to wait for both 2 calls and display one nudge for both.

Fixed: b:273570886
Change-Id: Ia271a90ba0dc8e36c7501019e922b0b3667cc56d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4705990
Commit-Queue: Andre Le <leandre@chromium.org>
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Reviewed-by: James Cook <jamescook@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1174302}
This commit is contained in:
Andre Le
2023-07-24 17:51:46 +00:00
committed by Chromium LUCI CQ
parent aec792867c
commit fcacfc7827
9 changed files with 303 additions and 80 deletions

@ -1629,7 +1629,7 @@ Style notes:
<message name="IDS_ASH_VIDEO_CONFERENCE_TOAST_SPEAK_ON_MUTE_DETECTED" desc="A toast message that we show when user tries to speak while muted on system-level.">
Are you talking? Your mic is off. Select the mic to turn it on.
</message>
<message name="IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_SOFTWARE_DISABLED" desc="A toast message that we show when an app tries to access camera or microphone while it is disabled by software.">
<message name="IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_DISABLED" desc="A toast message that we show when an app tries to access camera or microphone while it is disabled by software.">
<ph name="APP_NAME">$1<ex>Meet</ex></ph> wants to use your <ph name="DEVICE_NAME">$2<ex>camera</ex></ph>
</message>
<message name="IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_HARDWARE_DISABLED" desc="A toast message that we show when an app tries to access camera or microphone while it is disabled by hardware.">
@ -1662,6 +1662,9 @@ Style notes:
<message name="IDS_ASH_VIDEO_CONFERENCE_MICROPHONE_NAME" desc="Text display for the microphone.">
microphone
</message>
<message name="IDS_ASH_VIDEO_CONFERENCE_CAMERA_MICROPHONE_NAME" desc="Text display for both the camera and microphone.">
camera and microphone
</message>
<message name="VIDEO_CONFERENCE_RETURN_TO_APP_PERIPHERALS_ACCESSIBLE_NAME" desc="Tooltip shown for the return to app button regarding peripherals in the video conference panel.">
<ph name="CAPTURE_MEDIUM">$1<ex>Camera</ex></ph> in use.
</message>

@ -0,0 +1 @@
e4271f7390f667a1a8c209f5e488999f4d8fc56d

@ -216,7 +216,8 @@ enum class NudgeCatalogName {
kVideoConferenceTraySpeakOnMuteOptIn = 16,
kVideoConferenceTraySpeakOnMuteOptInConfirmation = 17,
kScalableIphBubble = 18,
kMaxValue = kScalableIphBubble
kVideoConferenceTrayCameraMicrophoneUseWhileDisabled = 19,
kMaxValue = kVideoConferenceTrayCameraMicrophoneUseWhileDisabled
};
// A living catalog that registers toasts.

@ -66,6 +66,8 @@ constexpr char kVideoConferenceTrayCameraUseWhileHWDisabledNudgeId[] =
"video_conference_tray_nudge_ids.camera_use_while_hw_disabled";
constexpr char kVideoConferenceTrayCameraUseWhileSWDisabledNudgeId[] =
"video_conference_tray_nudge_ids.camera_use_while_sw_disabled";
constexpr char kVideoConferenceTrayBothUseWhileDisabledNudgeId[] =
"video_conference_tray_nudge_ids.camera_microphone_use_while_disabled";
// VC nudge ids vector that is iterated whenever `CloseAllVcNudges()` is
// called. Please keep in sync whenever adding/removing/updating a nudge id.
@ -82,6 +84,7 @@ const char* const kNudgeIds[] = {
constexpr int KSpeakOnMuteNotificationCoolDownDuration = 60;
constexpr auto kRepeatedShowTimerInterval = base::Milliseconds(100);
constexpr auto kHandleDeviceUsedWhileDisabledWaitTime = base::Milliseconds(200);
// The max amount of times the "Speak-on-mute opt-in" nudge can show.
constexpr int kSpeakOnMuteOptInNudgeMaxShownCount = 3;
@ -430,10 +433,15 @@ void VideoConferenceTrayController::OnCameraHWPrivacySwitchStateChanged(
// Attempt recording "Use while disabled" nudge action when camera is unmuted.
if (!camera_muted_by_hardware_switch_) {
AnchoredNudgeManager::Get()->MaybeRecordNudgeAction(
auto* nudge_manager = AnchoredNudgeManager::Get();
nudge_manager->MaybeRecordNudgeAction(
NudgeCatalogName::kVideoConferenceTrayCameraUseWhileHWDisabled);
AnchoredNudgeManager::Get()->Cancel(
kVideoConferenceTrayCameraUseWhileHWDisabledNudgeId);
nudge_manager->Cancel(kVideoConferenceTrayCameraUseWhileHWDisabledNudgeId);
nudge_manager->MaybeRecordNudgeAction(
NudgeCatalogName::kVideoConferenceTrayCameraMicrophoneUseWhileDisabled);
nudge_manager->Cancel(kVideoConferenceTrayBothUseWhileDisabledNudgeId);
}
}
@ -452,10 +460,15 @@ void VideoConferenceTrayController::OnCameraSWPrivacySwitchStateChanged(
// Attempt recording "Use while disabled" nudge action when camera is unmuted.
if (!camera_muted_by_software_switch_) {
AnchoredNudgeManager::Get()->MaybeRecordNudgeAction(
auto* nudge_manager = AnchoredNudgeManager::Get();
nudge_manager->MaybeRecordNudgeAction(
NudgeCatalogName::kVideoConferenceTrayCameraUseWhileSWDisabled);
AnchoredNudgeManager::Get()->Cancel(
kVideoConferenceTrayCameraUseWhileSWDisabledNudgeId);
nudge_manager->Cancel(kVideoConferenceTrayCameraUseWhileSWDisabledNudgeId);
nudge_manager->MaybeRecordNudgeAction(
NudgeCatalogName::kVideoConferenceTrayCameraMicrophoneUseWhileDisabled);
nudge_manager->Cancel(kVideoConferenceTrayBothUseWhileDisabledNudgeId);
}
}
@ -494,26 +507,30 @@ void VideoConferenceTrayController::OnInputMuteChanged(
// Attempt showing the speak-on-mute opt-in nudge when input is muted.
MaybeShowSpeakOnMuteOptInNudge(GetVcTrayInActiveWindow());
} else {
auto* nudge_manager = AnchoredNudgeManager::Get();
// Cancel speak-on-mute opt-in nudge if one was being shown.
AnchoredNudgeManager::Get()->Cancel(
kVideoConferenceTraySpeakOnMuteOptInNudgeId);
nudge_manager->Cancel(kVideoConferenceTraySpeakOnMuteOptInNudgeId);
// Attempt recording "Speak-on-mute" nudge action when mic is unmuted.
AnchoredNudgeManager::Get()->MaybeRecordNudgeAction(
nudge_manager->MaybeRecordNudgeAction(
NudgeCatalogName::kVideoConferenceTraySpeakOnMuteDetected);
AnchoredNudgeManager::Get()->Cancel(
kVideoConferenceTraySpeakOnMuteDetectedNudgeId);
nudge_manager->Cancel(kVideoConferenceTraySpeakOnMuteDetectedNudgeId);
// Attempt recording "Use while disabled" nudge action when mic is unmuted.
AnchoredNudgeManager::Get()->MaybeRecordNudgeAction(
nudge_manager->MaybeRecordNudgeAction(
microphone_muted_by_hardware_switch_
? NudgeCatalogName::kVideoConferenceTrayMicrophoneUseWhileHWDisabled
: NudgeCatalogName::
kVideoConferenceTrayMicrophoneUseWhileSWDisabled);
AnchoredNudgeManager::Get()->Cancel(
nudge_manager->Cancel(
microphone_muted_by_hardware_switch_
? kVideoConferenceTrayMicrophoneUseWhileHWDisabledNudgeId
: kVideoConferenceTrayMicrophoneUseWhileSWDisabledNudgeId);
nudge_manager->MaybeRecordNudgeAction(
NudgeCatalogName::kVideoConferenceTrayCameraMicrophoneUseWhileDisabled);
nudge_manager->Cancel(kVideoConferenceTrayBothUseWhileDisabledNudgeId);
}
}
@ -677,62 +694,34 @@ bool VideoConferenceTrayController::HasMicrophonePermission() const {
void VideoConferenceTrayController::HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice device,
const std::u16string& app_name) {
// Do not show "Use while disabled" nudge if another nudge is showing.
if (IsAnyVcNudgeShown()) {
if (device == crosapi::mojom::VideoConferenceMediaDevice::kUnusedDefault) {
return;
}
// TODO(b/273570886): Handle the case when both camera and microphone are
// being used while disabled.
std::u16string device_name;
int text_id;
NudgeCatalogName catalog_name;
std::string nudge_id;
views::View* anchor_view = nullptr;
switch (device) {
case crosapi::mojom::VideoConferenceMediaDevice::kMicrophone:
device_name =
l10n_util::GetStringUTF16(IDS_ASH_VIDEO_CONFERENCE_MICROPHONE_NAME);
if (microphone_muted_by_hardware_switch_) {
text_id = IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_HARDWARE_DISABLED;
nudge_id = kVideoConferenceTrayMicrophoneUseWhileHWDisabledNudgeId;
catalog_name =
NudgeCatalogName::kVideoConferenceTrayMicrophoneUseWhileHWDisabled;
} else {
text_id = IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_SOFTWARE_DISABLED;
nudge_id = kVideoConferenceTrayMicrophoneUseWhileSWDisabledNudgeId;
catalog_name =
NudgeCatalogName::kVideoConferenceTrayMicrophoneUseWhileSWDisabled;
}
anchor_view = GetVcTrayInActiveWindow()->audio_icon();
break;
case crosapi::mojom::VideoConferenceMediaDevice::kCamera:
device_name =
l10n_util::GetStringUTF16(IDS_ASH_VIDEO_CONFERENCE_CAMERA_NAME);
if (camera_muted_by_hardware_switch_) {
text_id = IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_HARDWARE_DISABLED;
nudge_id = kVideoConferenceTrayCameraUseWhileHWDisabledNudgeId;
catalog_name =
NudgeCatalogName::kVideoConferenceTrayCameraUseWhileHWDisabled;
} else {
text_id = IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_SOFTWARE_DISABLED;
nudge_id = kVideoConferenceTrayCameraUseWhileSWDisabledNudgeId;
catalog_name =
NudgeCatalogName::kVideoConferenceTrayCameraUseWhileSWDisabled;
}
anchor_view = GetVcTrayInActiveWindow()->camera_icon();
break;
default:
NOTREACHED();
return;
UsedWhileDisabledNudgeType type = GetUsedWhileDisabledNudgeType(device);
if (!use_while_disabled_signal_waiter_.IsRunning()) {
// Cache the type and starts the timer to wait for the signal of the other
// device.
use_while_disabled_nudge_on_wait_ = type;
use_while_disabled_signal_waiter_.Start(
FROM_HERE, kHandleDeviceUsedWhileDisabledWaitTime,
base::BindOnce(
&VideoConferenceTrayController::DisplayUsedWhileDisabledNudge,
weak_ptr_factory_.GetWeakPtr(), type, app_name));
return;
}
AnchoredNudgeData nudge_data(
nudge_id, catalog_name,
l10n_util::GetStringFUTF16(text_id, app_name, device_name), anchor_view);
nudge_data.anchored_to_shelf = true;
CreateNudgeRequest(
std::make_unique<AnchoredNudgeData>(std::move(nudge_data)));
if (type == use_while_disabled_nudge_on_wait_) {
return;
}
use_while_disabled_signal_waiter_.Stop();
// If we receive the signal for both camera and microphone, display the nudge
// for both.
DisplayUsedWhileDisabledNudge(UsedWhileDisabledNudgeType::kBoth, app_name);
}
void VideoConferenceTrayController::UpdateCameraIcons() {
@ -797,4 +786,94 @@ void VideoConferenceTrayController::RecordRepeatedShows() {
count_repeated_shows_ = 0;
}
void VideoConferenceTrayController::DisplayUsedWhileDisabledNudge(
VideoConferenceTrayController::UsedWhileDisabledNudgeType type,
const std::u16string& app_name) {
// Do not show "Use while disabled" nudge if another nudge is showing.
if (IsAnyVcNudgeShown()) {
return;
}
std::u16string device_name;
int text_id;
NudgeCatalogName catalog_name;
std::string nudge_id;
views::View* anchor_view = nullptr;
switch (type) {
case VideoConferenceTrayController::UsedWhileDisabledNudgeType::kMicrophone:
device_name =
l10n_util::GetStringUTF16(IDS_ASH_VIDEO_CONFERENCE_MICROPHONE_NAME);
if (microphone_muted_by_hardware_switch_) {
text_id = IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_HARDWARE_DISABLED;
nudge_id = kVideoConferenceTrayMicrophoneUseWhileHWDisabledNudgeId;
catalog_name =
NudgeCatalogName::kVideoConferenceTrayMicrophoneUseWhileHWDisabled;
} else {
text_id = IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_DISABLED;
nudge_id = kVideoConferenceTrayMicrophoneUseWhileSWDisabledNudgeId;
catalog_name =
NudgeCatalogName::kVideoConferenceTrayMicrophoneUseWhileSWDisabled;
}
anchor_view = GetVcTrayInActiveWindow()->audio_icon();
break;
case VideoConferenceTrayController::UsedWhileDisabledNudgeType::kCamera:
device_name =
l10n_util::GetStringUTF16(IDS_ASH_VIDEO_CONFERENCE_CAMERA_NAME);
if (camera_muted_by_hardware_switch_) {
text_id = IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_HARDWARE_DISABLED;
nudge_id = kVideoConferenceTrayCameraUseWhileHWDisabledNudgeId;
catalog_name =
NudgeCatalogName::kVideoConferenceTrayCameraUseWhileHWDisabled;
} else {
text_id = IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_DISABLED;
nudge_id = kVideoConferenceTrayCameraUseWhileSWDisabledNudgeId;
catalog_name =
NudgeCatalogName::kVideoConferenceTrayCameraUseWhileSWDisabled;
}
anchor_view = GetVcTrayInActiveWindow()->camera_icon();
break;
case VideoConferenceTrayController::UsedWhileDisabledNudgeType::kBoth:
device_name = l10n_util::GetStringUTF16(
IDS_ASH_VIDEO_CONFERENCE_CAMERA_MICROPHONE_NAME);
text_id = IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_DISABLED;
nudge_id = kVideoConferenceTrayBothUseWhileDisabledNudgeId;
catalog_name = NudgeCatalogName::
kVideoConferenceTrayCameraMicrophoneUseWhileDisabled;
anchor_view = GetVcTrayInActiveWindow()->audio_icon();
break;
default:
NOTREACHED();
return;
}
AnchoredNudgeData nudge_data(
nudge_id, catalog_name,
l10n_util::GetStringFUTF16(text_id, app_name, device_name), anchor_view);
nudge_data.anchored_to_shelf = true;
CreateNudgeRequest(
std::make_unique<AnchoredNudgeData>(std::move(nudge_data)));
}
VideoConferenceTrayController::UsedWhileDisabledNudgeType
VideoConferenceTrayController::GetUsedWhileDisabledNudgeType(
crosapi::mojom::VideoConferenceMediaDevice device) {
DCHECK_NE(device, crosapi::mojom::VideoConferenceMediaDevice::kUnusedDefault);
VideoConferenceTrayController::UsedWhileDisabledNudgeType type;
switch (device) {
case crosapi::mojom::VideoConferenceMediaDevice::kCamera:
type = VideoConferenceTrayController::UsedWhileDisabledNudgeType::kCamera;
break;
case crosapi::mojom::VideoConferenceMediaDevice::kMicrophone:
type = VideoConferenceTrayController::UsedWhileDisabledNudgeType::
kMicrophone;
break;
default:
NOTREACHED();
type = VideoConferenceTrayController::UsedWhileDisabledNudgeType::kCamera;
}
return type;
}
} // namespace ash

@ -201,6 +201,14 @@ class ASH_EXPORT VideoConferenceTrayController
bool initialized() const { return initialized_; }
private:
// All the types of the use while disabled nudge.
enum class UsedWhileDisabledNudgeType {
kCamera = 0,
kMicrophone = 1,
kBoth = 2,
kMaxValue = kBoth
};
// Updates the state of the camera icons across all `VideoConferenceTray`.
void UpdateCameraIcons();
@ -214,6 +222,13 @@ class ASH_EXPORT VideoConferenceTrayController
// Returns true if any of the VC nudges are visible on screen.
bool IsAnyVcNudgeShown();
// Displays the use while disabled nudge according to the given `type`.
void DisplayUsedWhileDisabledNudge(UsedWhileDisabledNudgeType type,
const std::u16string& app_name);
UsedWhileDisabledNudgeType GetUsedWhileDisabledNudgeType(
crosapi::mojom::VideoConferenceMediaDevice device);
// The number of capturing apps, fetched from `VideoConferenceManagerAsh`.
int capturing_apps_ = 0;
@ -268,6 +283,14 @@ class ASH_EXPORT VideoConferenceTrayController
int count_repeated_shows_ = 0;
base::DelayTimer repeated_shows_timer_;
// Due to some constraint in `VideoConferenceManagerAsh`, when both microphone
// and camera is being accessed when disabled,`HandleDeviceUsedWhileDisabled`
// will be called twice for each device. Thus, we need to wait for both 2
// calls and display one nudge for both. These are the timer and the cache
// type to make that happen.
base::OneShotTimer use_while_disabled_signal_waiter_;
UsedWhileDisabledNudgeType use_while_disabled_nudge_on_wait_;
// The contents of a nudge data object that is cached so it can be shown once
// the tray has fully animated in.
std::unique_ptr<AnchoredNudgeData> requested_nudge_data_;

@ -21,6 +21,7 @@
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "chromeos/ash/components/audio/cras_audio_handler.h"
#include "chromeos/crosapi/mojom/video_conference.mojom.h"
#include "components/prefs/pref_service.h"
@ -51,10 +52,14 @@ constexpr char kVideoConferenceTrayCameraUseWhileHWDisabledNudgeId[] =
"video_conference_tray_nudge_ids.camera_use_while_hw_disabled";
constexpr char kVideoConferenceTrayCameraUseWhileSWDisabledNudgeId[] =
"video_conference_tray_nudge_ids.camera_use_while_sw_disabled";
constexpr char kVideoConferenceTrayBothUseWhileDisabledNudgeId[] =
"video_conference_tray_nudge_ids.camera_microphone_use_while_disabled";
constexpr char kRepeatedShowsHistogramName[] =
"Ash.VideoConference.NumberOfRepeatedShows";
constexpr auto kHandleDeviceUsedWhileDisabledWaitTime = base::Milliseconds(200);
bool IsNudgeShown(const std::string& id) {
return Shell::Get()->anchored_nudge_manager()->IsNudgeShown(id);
}
@ -261,14 +266,15 @@ TEST_F(VideoConferenceTrayControllerTest,
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kCamera, app_name);
// Nudge should be displayed. Showing that app is accessing while camera is
// software-muted.
// After `kHandleDeviceUsedWhileDisabledWaitTime`, nudge should be displayed.
// Showing that app is accessing while camera is software-muted.
task_environment()->FastForwardBy(kHandleDeviceUsedWhileDisabledWaitTime);
ASSERT_TRUE(IsNudgeShown(nudge_id));
EXPECT_EQ(GetNudgeAnchorView(nudge_id), camera_icon());
EXPECT_EQ(GetNudgeText(nudge_id),
l10n_util::GetStringFUTF16(
IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_SOFTWARE_DISABLED,
app_name, camera_device_name));
IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_DISABLED, app_name,
camera_device_name));
// Unmute camera through SW. Nudge should be dismissed.
controller()->OnCameraSWPrivacySwitchStateChanged(
@ -294,14 +300,15 @@ TEST_F(VideoConferenceTrayControllerTest,
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kMicrophone, app_name);
// Nudge should be displayed. Showing that app is accessing while microphone
// is software-muted.
// After `kHandleDeviceUsedWhileDisabledWaitTime`, nudge should be displayed.
// Showing that app is accessing while microphone is software-muted.
task_environment()->FastForwardBy(kHandleDeviceUsedWhileDisabledWaitTime);
ASSERT_TRUE(IsNudgeShown(nudge_id));
EXPECT_EQ(GetNudgeAnchorView(nudge_id), audio_icon());
EXPECT_EQ(GetNudgeText(nudge_id),
l10n_util::GetStringFUTF16(
IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_SOFTWARE_DISABLED,
app_name, microphone_device_name));
IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_DISABLED, app_name,
microphone_device_name));
// Unmute microphone through SW. Nudge should be dismissed.
controller()->OnInputMuteChanged(
@ -327,8 +334,9 @@ TEST_F(VideoConferenceTrayControllerTest,
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kCamera, app_name);
// Nudge should be displayed. Showing that app is accessing while camera is
// hardware-muted.
// After `kHandleDeviceUsedWhileDisabledWaitTime`, nudge should be displayed.
// Showing that app is accessing while camera is hardware-muted.
task_environment()->FastForwardBy(kHandleDeviceUsedWhileDisabledWaitTime);
ASSERT_TRUE(IsNudgeShown(nudge_id));
EXPECT_EQ(GetNudgeAnchorView(nudge_id), camera_icon());
EXPECT_EQ(GetNudgeText(nudge_id),
@ -361,8 +369,9 @@ TEST_F(VideoConferenceTrayControllerTest,
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kMicrophone, app_name);
// Nudge should be displayed. Showing that app is accessing while microphone
// is hardware-muted.
// After `kHandleDeviceUsedWhileDisabledWaitTime`, nudge should be displayed.
// Showing that app is accessing while microphone is hardware-muted.
task_environment()->FastForwardBy(kHandleDeviceUsedWhileDisabledWaitTime);
ASSERT_TRUE(IsNudgeShown(nudge_id));
EXPECT_EQ(GetNudgeAnchorView(nudge_id), audio_icon());
EXPECT_EQ(GetNudgeText(nudge_id),
@ -377,6 +386,110 @@ TEST_F(VideoConferenceTrayControllerTest,
EXPECT_FALSE(IsNudgeShown(nudge_id));
}
TEST_F(VideoConferenceTrayControllerTest,
HandleCameraMicrophoneUsedWhileDisabled) {
auto* app_name = u"app_name";
auto device_name = l10n_util::GetStringUTF16(
IDS_ASH_VIDEO_CONFERENCE_CAMERA_MICROPHONE_NAME);
auto* nudge_id = kVideoConferenceTrayBothUseWhileDisabledNudgeId;
SetTrayAndButtonsVisible();
controller()->OnInputMuteChanged(
/*mute_on=*/true, CrasAudioHandler::InputMuteChangeMethod::kOther);
controller()->OnCameraHWPrivacySwitchStateChanged(
/*device_id=*/"device_id", cros::mojom::CameraPrivacySwitchState::ON);
// No nudge is shown before `HandleDeviceUsedWhileDisabled()` is called.
EXPECT_FALSE(IsNudgeShown(nudge_id));
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kMicrophone, app_name);
task_environment()->FastForwardBy(base::Milliseconds(20));
// No nudge is shown yet since we are waiting for more signal for
// `HandleDeviceUsedWhileDisabled`.
EXPECT_FALSE(IsNudgeShown(nudge_id));
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kCamera, app_name);
// Nudge should be displayed when receiving signal for both camera and
// microphone.
ASSERT_TRUE(IsNudgeShown(nudge_id));
EXPECT_EQ(GetNudgeAnchorView(nudge_id), audio_icon());
EXPECT_EQ(GetNudgeText(nudge_id),
l10n_util::GetStringFUTF16(
IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_DISABLED, app_name,
device_name));
}
TEST_F(VideoConferenceTrayControllerTest,
UnmuteCameraWithCameraMicrophoneUsedWhileDisabledNudge) {
auto* app_name = u"app_name";
auto* nudge_id = kVideoConferenceTrayBothUseWhileDisabledNudgeId;
SetTrayAndButtonsVisible();
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kMicrophone, app_name);
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kCamera, app_name);
ASSERT_TRUE(IsNudgeShown(nudge_id));
// Now unmute camera. Nudge should also be dismissed.
controller()->OnCameraSWPrivacySwitchStateChanged(
cros::mojom::CameraPrivacySwitchState::OFF);
EXPECT_FALSE(IsNudgeShown(nudge_id));
// Test the same thing for hw-unmuting camera.
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kMicrophone, app_name);
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kCamera, app_name);
ASSERT_TRUE(IsNudgeShown(nudge_id));
controller()->OnCameraHWPrivacySwitchStateChanged(
/*device_id=*/"device_id", cros::mojom::CameraPrivacySwitchState::OFF);
EXPECT_FALSE(IsNudgeShown(nudge_id));
}
TEST_F(VideoConferenceTrayControllerTest,
UnmuteMicrophoneWithCameraMicrophoneUsedWhileDisabledNudge) {
auto* app_name = u"app_name";
auto* nudge_id = kVideoConferenceTrayBothUseWhileDisabledNudgeId;
SetTrayAndButtonsVisible();
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kMicrophone, app_name);
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kCamera, app_name);
ASSERT_TRUE(IsNudgeShown(nudge_id));
// Now unmute microphone. Nudge should also be dismissed.
controller()->OnInputMuteChanged(
/*mute_on=*/false, CrasAudioHandler::InputMuteChangeMethod::kOther);
EXPECT_FALSE(IsNudgeShown(nudge_id));
// Test the same thing for hw-unmuting microphone.
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kMicrophone, app_name);
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kCamera, app_name);
ASSERT_TRUE(IsNudgeShown(nudge_id));
controller()->OnInputMuteChanged(
/*mute_on=*/false,
CrasAudioHandler::InputMuteChangeMethod::kPhysicalShutter);
EXPECT_FALSE(IsNudgeShown(nudge_id));
}
TEST_F(VideoConferenceTrayControllerTest, SpeakOnMuteNudge) {
auto* nudge_id = kVideoConferenceTraySpeakOnMuteDetectedNudgeId;
@ -673,6 +786,7 @@ TEST_F(VideoConferenceTrayControllerTest, NudgeBlocksOtherNudges) {
/*device_id=*/"device_id", cros::mojom::CameraPrivacySwitchState::ON);
controller()->HandleDeviceUsedWhileDisabled(
crosapi::mojom::VideoConferenceMediaDevice::kCamera, app_name);
task_environment()->FastForwardBy(kHandleDeviceUsedWhileDisabledWaitTime);
EXPECT_TRUE(IsNudgeShown(use_while_disabled_nudge_id));
// Show opt-in nudge by muting the microphone, "use while disabled" nudge

@ -681,7 +681,7 @@ IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, UseWhileDisabled) {
EXPECT_EQ(
GetNudgeText(microphone_nudge_id),
l10n_util::GetStringFUTF16(
IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_SOFTWARE_DISABLED, kTitle1,
IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_DISABLED, kTitle1,
l10n_util::GetStringUTF16(IDS_ASH_VIDEO_CONFERENCE_MICROPHONE_NAME)));
EXPECT_EQ(GetNudgeAnchorView(microphone_nudge_id), GetVcTray()->audio_icon());
@ -703,7 +703,7 @@ IN_PROC_BROWSER_TEST_P(VideoConferenceIntegrationTest, UseWhileDisabled) {
EXPECT_EQ(
GetNudgeText(camera_nudge_id),
l10n_util::GetStringFUTF16(
IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_SOFTWARE_DISABLED, kTitle1,
IDS_ASH_VIDEO_CONFERENCE_TOAST_USE_WHILE_DISABLED, kTitle1,
l10n_util::GetStringUTF16(IDS_ASH_VIDEO_CONFERENCE_CAMERA_NAME)));
EXPECT_EQ(GetNudgeAnchorView(camera_nudge_id), GetVcTray()->camera_icon());
}

@ -77094,6 +77094,8 @@ Called by update_net_trust_anchors.py.-->
<int value="17"
label="Video Conference Tray Speak On Mute Opt In Confirmation"/>
<int value="18" label="Scalable IPH Bubble"/>
<int value="19"
label="Video Conference Tray Camera And Microphone Use While Disabled"/>
</enum>
<enum name="NukeProfileResult">