0

FaceGaze: Add DLC notifications

In this change, we add logic to show a notification when the
facegaze-assets DLC succeeds/fails to download. This follows the
pattern used for other accessibility-related DLCs. We also add prefs
to ensure that each notification is shown at most one time.

Bug: b:309121742
Change-Id: Idfe32f39f0d0402ccef38e5e792b449de0393d50
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5730872
Reviewed-by: Xiaoqian Dai <xdai@chromium.org>
Reviewed-by: Amanda Lin Dietz <aldietz@google.com>
Reviewed-by: Ahmed Mehfooz <amehfooz@chromium.org>
Commit-Queue: Akihiro Ota <akihiroota@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1333687}
This commit is contained in:
Akihiro Ota
2024-07-26 18:29:19 +00:00
committed by Chromium LUCI CQ
parent ff1876086d
commit 83e9072f40
14 changed files with 189 additions and 4 deletions

@ -295,6 +295,8 @@ constexpr const char* const kCopiedOnSigninAccessibilityPrefs[]{
prefs::kDictationNoDlcsDownloadedNotificationHasBeenShown,
prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted2,
prefs::kSelectToSpeakAcceleratorDialogHasBeenAccepted,
prefs::kFaceGazeDlcSuccessNotificationHasBeenShown,
prefs::kFaceGazeDlcFailureNotificationHasBeenShown,
};
// List of switch access accessibility prefs that are to be copied (if changed
@ -494,6 +496,21 @@ void ShowAccessibilityNotification(
pinned = false;
// Use CRITICAL_WARNING to force the notification color to red.
warning = message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
} else if (type == A11yNotificationType::kFaceGazeAssetsDownloaded) {
title = l10n_util::GetStringUTF16(
IDS_ASH_A11Y_FACEGAZE_ASSETS_DOWNLOADED_TITLE);
text =
l10n_util::GetStringUTF16(IDS_ASH_A11Y_FACEGAZE_ASSETS_DOWNLOADED_DESC);
catalog_name = NotificationCatalogName::kFaceGazeAssetsDownloaded;
pinned = false;
} else if (type == A11yNotificationType::kFaceGazeAssetsFailed) {
title =
l10n_util::GetStringUTF16(IDS_ASH_A11Y_FACEGAZE_ASSETS_FAILED_TITLE);
text = l10n_util::GetStringUTF16(IDS_ASH_A11Y_FACEGAZE_ASSETS_FAILED_DESC);
catalog_name = NotificationCatalogName::kFaceGazeAssetsFailed;
pinned = false;
// Use CRITICAL_WARNING to force the notification color to red.
warning = message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
} else if (type == A11yNotificationType::kSwitchAccessEnabled) {
title = l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_SWITCH_ACCESS_ENABLED_TITLE);
@ -1201,6 +1218,10 @@ void AccessibilityController::RegisterProfilePrefs(
prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted2, false);
registry->RegisterBooleanPref(prefs::kShouldAlwaysShowAccessibilityMenu,
false);
registry->RegisterBooleanPref(
prefs::kFaceGazeDlcSuccessNotificationHasBeenShown, false);
registry->RegisterBooleanPref(
prefs::kFaceGazeDlcFailureNotificationHasBeenShown, false);
registry->RegisterBooleanPref(prefs::kAccessibilityColorCorrectionEnabled,
false);
@ -3247,6 +3268,33 @@ void AccessibilityController::ShowNotificationForDictation(
notification_type, std::vector<std::u16string>{display_language}));
}
void AccessibilityController::ShowNotificationForFaceGaze(
FaceGazeNotificationType type) {
A11yNotificationType notification_type;
std::string notification_shown_pref;
switch (type) {
case FaceGazeNotificationType::kDlcSucceeded:
notification_type = A11yNotificationType::kFaceGazeAssetsDownloaded;
notification_shown_pref =
prefs::kFaceGazeDlcSuccessNotificationHasBeenShown;
break;
case FaceGazeNotificationType::kDlcFailed:
notification_type = A11yNotificationType::kFaceGazeAssetsFailed;
notification_shown_pref =
prefs::kFaceGazeDlcFailureNotificationHasBeenShown;
break;
}
if (active_user_prefs_->GetBoolean(notification_shown_pref)) {
// Do not show notifications more than once.
return;
}
active_user_prefs_->SetBoolean(notification_shown_pref, true);
ShowAccessibilityNotification(A11yNotificationWrapper(
notification_type, std::vector<std::u16string>()));
}
AccessibilityController::A11yNotificationWrapper::A11yNotificationWrapper() =
default;
AccessibilityController::A11yNotificationWrapper::A11yNotificationWrapper(

@ -94,6 +94,10 @@ enum class A11yNotificationType {
kDicationOnlyPumpkinDownloaded,
// Shown when the SODA DLC (but no other DLCs) have downloaded.
kDictationOnlySodaDownloaded,
// Shown when the facegaze-assets DLC has successfully downloaded.
kFaceGazeAssetsDownloaded,
// Shown when the facegaze-assets DLC failed to download.
kFaceGazeAssetsFailed,
// Shown when braille display is connected while spoken feedback is not
// enabled yet. Note: in this case braille display connected would enable
// spoken feedback.
@ -568,6 +572,9 @@ class ASH_EXPORT AccessibilityController : public SessionObserver,
const std::optional<std::u16string>& text,
const std::optional<std::vector<DictationBubbleHintType>>& hints);
// Shows a notification notifying the user about the FaceGaze DLC download.
void ShowNotificationForFaceGaze(FaceGazeNotificationType type);
// Cancels all of spoken feedback's current and queued speech immediately.
void SilenceSpokenFeedback();

@ -1808,6 +1808,41 @@ TEST_F(AccessibilityControllerTest,
ASSERT_EQ(nullptr, controller->GetDisableTrackpadEventRewriterForTest());
}
TEST_F(AccessibilityControllerTest, FaceGazeNotificationsOnlyShownOnce) {
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
AccessibilityController* controller =
Shell::Get()->accessibility_controller();
ASSERT_FALSE(
prefs->GetBoolean(prefs::kFaceGazeDlcSuccessNotificationHasBeenShown));
ASSERT_FALSE(
prefs->GetBoolean(prefs::kFaceGazeDlcFailureNotificationHasBeenShown));
controller->ShowNotificationForFaceGaze(
FaceGazeNotificationType::kDlcSucceeded);
ASSERT_EQ(1u, MessageCenter::Get()->GetVisibleNotifications().size());
ASSERT_TRUE(
prefs->GetBoolean(prefs::kFaceGazeDlcSuccessNotificationHasBeenShown));
message_center::MessageCenter::Get()->RemoveAllNotifications(
/*by_user=*/false, message_center::MessageCenter::RemoveType::ALL);
// The success notification shouldn't be shown again.
controller->ShowNotificationForFaceGaze(
FaceGazeNotificationType::kDlcSucceeded);
ASSERT_EQ(0u, MessageCenter::Get()->GetVisibleNotifications().size());
controller->ShowNotificationForFaceGaze(FaceGazeNotificationType::kDlcFailed);
ASSERT_EQ(1u, MessageCenter::Get()->GetVisibleNotifications().size());
ASSERT_TRUE(
prefs->GetBoolean(prefs::kFaceGazeDlcFailureNotificationHasBeenShown));
message_center::MessageCenter::Get()->RemoveAllNotifications(
/*by_user=*/false, message_center::MessageCenter::RemoveType::ALL);
// The failure notification shouldn't be shown again.
controller->ShowNotificationForFaceGaze(FaceGazeNotificationType::kDlcFailed);
ASSERT_EQ(0u, MessageCenter::Get()->GetVisibleNotifications().size());
}
namespace {
enum class TestUserLoginType {

@ -6885,6 +6885,18 @@ Here are some things you can try to get started.
<message name="IDS_ASH_A11Y_DICTATION_NOTIFICATION_ONLY_SODA_DOWNLOADED_DESC" desc="The description for a notification that is shown when speech recognition files download successfully. This notification is only shown if speech recognition assets are downloaded and command parsing assets are not.">
Speech is processed locally and dictation works offline, but some voice commands wont work.
</message>
<message name="IDS_ASH_A11Y_FACEGAZE_ASSETS_DOWNLOADED_TITLE" desc="The title for a notification that is shown when the facegaze-assets DLC is successfully downloaded.">
Face control files downloaded
</message>
<message name="IDS_ASH_A11Y_FACEGAZE_ASSETS_DOWNLOADED_DESC" desc="The description for a notification that is shown when the facegaze-assets DLC is successfully downloaded.">
Face control will be available to other users on the device
</message>
<message name="IDS_ASH_A11Y_FACEGAZE_ASSETS_FAILED_TITLE" desc="The title for a notification that is shown when the facegaze-assets DLC fails to download.">
Couldn't download face control files
</message>
<message name="IDS_ASH_A11Y_FACEGAZE_ASSETS_FAILED_DESC" desc="The description for a notification that is shown when the facegaze-assets DLC fails to download.">
Try again later
</message>
<!-- Debugd notification strings -->
<message name="IDS_ASH_DEBUG_PACKET_CAPTURE_STARTED" desc="The label used for the notification to indicate network packet capture operation has started and network information is being collected on the device.">

@ -0,0 +1 @@
8d5a2968967cb0c06e796765686c9957a27a6958

@ -0,0 +1 @@
8d5a2968967cb0c06e796765686c9957a27a6958

@ -0,0 +1 @@
e4264aeb054fe6398c45abf9b5f66e9e07a21dc7

@ -0,0 +1 @@
e4264aeb054fe6398c45abf9b5f66e9e07a21dc7

@ -814,6 +814,14 @@ inline constexpr char kAccessibilityFaceGazeActionsEnabled[] =
// separately or whether speeds in all directions should be adjusted together.
inline constexpr char kAccessibilityFaceGazeAdjustSpeedSeparately[] =
"settings.a11y.face_gaze.adjust_speed_separately";
// A boolean pref which indicates whether the FaceGaze DLC success notification
// has ever been shown.
inline constexpr char kFaceGazeDlcSuccessNotificationHasBeenShown[] =
"settings.a11y.face_gaze.dlc_success_notification_has_been_shown";
// A boolean pref which indicates whether the FaceGaze DLC failure notification
// has ever been shown.
inline constexpr char kFaceGazeDlcFailureNotificationHasBeenShown[] =
"settings.a11y.face_gaze.dlc_failure_notification_has_been_shown";
// A boolean pref which determines whether the accessibility menu shows
// regardless of the state of a11y features.

@ -200,7 +200,9 @@ enum class NotificationCatalogName {
kAudioSelection = 181,
kExtendedUpdatesAvailable = 182,
kOnDeviceAppControls = 183,
kMaxValue = kOnDeviceAppControls
kFaceGazeAssetsDownloaded = 184,
kFaceGazeAssetsFailed = 185,
kMaxValue = kFaceGazeAssetsFailed
};
// A living catalog that registers system nudges.

@ -246,6 +246,12 @@ enum class DictationNotificationType {
kOnlyPumpkinDownloaded,
};
// The types of notifications that can be shown by FaceGaze.
enum class FaceGazeNotificationType {
kDlcFailed,
kDlcSucceeded,
};
// The types of accessibility-related toasts. This enum should be kept in sync
// with chrome.accessibilityPrivate.ToastType.
enum class AccessibilityToastType {

@ -2813,7 +2813,8 @@ void AccessibilityManager::InstallFaceGazeAssets(
base::BindOnce(&AccessibilityManager::OnFaceGazeAssetsInstalled,
weak_ptr_factory_.GetWeakPtr()),
/*on_progress=*/base::DoNothing(),
/*on_error=*/base::DoNothing());
base::BindOnce(&AccessibilityManager::OnFaceGazeAssetsFailed,
weak_ptr_factory_.GetWeakPtr()));
}
void AccessibilityManager::OnFaceGazeAssetsInstalled(
@ -2832,6 +2833,8 @@ void AccessibilityManager::OnFaceGazeAssetsInstalled(
? base::FilePath(root_path)
: dlc_path_for_test_;
AccessibilityController::Get()->ShowNotificationForFaceGaze(
FaceGazeNotificationType::kDlcSucceeded);
base::ThreadPool::PostTaskAndReplyWithResult(
FROM_HERE, {base::MayBlock()},
base::BindOnce(&CreateFaceGazeAssets, base::FilePath(base_path)),
@ -2839,6 +2842,16 @@ void AccessibilityManager::OnFaceGazeAssetsInstalled(
weak_ptr_factory_.GetWeakPtr()));
}
void AccessibilityManager::OnFaceGazeAssetsFailed(std::string_view error) {
AccessibilityController::Get()->ShowNotificationForFaceGaze(
FaceGazeNotificationType::kDlcFailed);
if (install_facegaze_assets_callback_.is_null()) {
return;
}
std::move(install_facegaze_assets_callback_).Run(std::nullopt);
}
void AccessibilityManager::OnFaceGazeAssetsCreated(
std::optional<FaceGazeAssets> assets) {
if (install_facegaze_assets_callback_.is_null()) {

@ -621,6 +621,7 @@ class AccessibilityManager
// Methods for managing the FaceGaze assets DLC.
void OnFaceGazeAssetsInstalled(bool success, const std::string& root_path);
void OnFaceGazeAssetsFailed(std::string_view error);
void OnFaceGazeAssetsCreated(
std::optional<::extensions::api::accessibility_private::FaceGazeAssets>
assets);

@ -1008,12 +1008,18 @@ class AccessibilityManagerDlcTest : public AccessibilityManagerTest {
delete;
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
AccessibilityManagerTest::SetUpCommandLine(command_line);
scoped_feature_list_.InitAndEnableFeature(
::features::kAccessibilityFaceGaze);
}
void SetUpOnMainThread() override {
AccessibilityManagerTest::SetUpOnMainThread();
UninstallSodaForTesting();
EnsureSodaObservation();
ClearMessageCenter();
AssertMessageCenterEmpty();
AccessibilityManagerTest::SetUpOnMainThread();
}
void TearDownOnMainThread() override {
@ -1039,7 +1045,7 @@ class AccessibilityManagerDlcTest : public AccessibilityManagerTest {
// We require `install_pumpkin_callback_` to be set before `OnPumpkinError`
// can be called.
manager->install_pumpkin_callback_ = base::DoNothing();
AccessibilityManager::Get()->OnPumpkinError("Error");
manager->OnPumpkinError("Error");
}
void OnPumpkinInstalled(bool success, const std::string& root_path) {
@ -1051,6 +1057,20 @@ class AccessibilityManagerDlcTest : public AccessibilityManagerTest {
AccessibilityManager::Get()->OnPumpkinDataCreated(std::move(data));
}
void InstallFaceGazeAssetsAndWait() {
base::RunLoop loop;
AccessibilityManager::Get()->InstallFaceGazeAssets(base::DoNothing());
loop.RunUntilIdle();
}
void OnFaceGazeAssetsFailed() {
AccessibilityManager* manager = AccessibilityManager::Get();
// We require `install_facegaze_assets_callback_` to be set before
// `OnFaceGazeAssetsFailed` can be called.
manager->install_facegaze_assets_callback_ = base::DoNothing();
manager->OnFaceGazeAssetsFailed("Error");
}
speech::SodaInstaller* soda_installer() {
return speech::SodaInstaller::GetInstance();
}
@ -1550,6 +1570,35 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
OnPumpkinDataCreated(std::nullopt);
}
// Ensures that the correct notification is shown when the facegaze-assets DLC
// is successfully downloaded.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, FaceGazeAssetsSucceeded) {
AccessibilityManager::Get()->EnableFaceGaze(true);
InstallFaceGazeAssetsAndWait();
message_center::NotificationList::Notifications notifications =
message_center::MessageCenter::Get()->GetVisibleNotifications();
ASSERT_EQ(1u, notifications.size());
ASSERT_EQ(u"Face control files downloaded",
(*notifications.begin())->title());
ASSERT_EQ(u"Face control will be available to other users on the device",
(*notifications.begin())->message());
}
// Ensures that the correct notification is shown when the facegaze-assets DLC
// fails to download.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, FaceGazeAssetsFailed) {
AccessibilityManager::Get()->EnableFaceGaze(true);
OnFaceGazeAssetsFailed();
message_center::NotificationList::Notifications notifications =
message_center::MessageCenter::Get()->GetVisibleNotifications();
ASSERT_EQ(1u, notifications.size());
ASSERT_EQ(u"Couldn't download face control files",
(*notifications.begin())->title());
ASSERT_EQ(u"Try again later", (*notifications.begin())->message());
}
enum DictationDialogTestVariant {
kOfflineEnabledAndAvailable,
kOfflineEnabledAndUnavailable,