0

Dictation: Add Pumpkin success and failure notifications

This change adds the behavior to show a notification to the user
when Pumpkin install succeeds/fails. Instead of showing a notification
for both SODA and Pumpkin (the two DLCs used by Dictation), we combine
the two into one notification.

AX-Relnotes: N/A
Bug: b:259352407
Change-Id: I47afc8e7f82aa835dae310ac1c2e1a079771b887
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4034829
Reviewed-by: Alex Newcomer <newcomer@chromium.org>
Reviewed-by: Katie Dektar <katie@chromium.org>
Commit-Queue: Akihiro Ota <akihiroota@chromium.org>
Reviewed-by: Xiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1082191}
This commit is contained in:
Akihiro Ota
2022-12-13 02:59:34 +00:00
committed by Chromium LUCI CQ
parent 841bdadb7d
commit f7a8febcdd
27 changed files with 364 additions and 150 deletions

@ -310,8 +310,10 @@ const gfx::VectorIcon& GetNotificationIcon(A11yNotificationType type) {
return kNotificationAccessibilityBrailleIcon;
case A11yNotificationType::kSwitchAccessEnabled:
return kSwitchAccessIcon;
case A11yNotificationType::kSpeechRecognitionFilesDownloaded:
case A11yNotificationType::kSpeechRecognitionFilesFailed:
case A11yNotificationType::kDictationAllDlcsDownloaded:
case A11yNotificationType::kDictationNoDlcsDownloaded:
case A11yNotificationType::kDicationOnlyPumpkinDownloaded:
case A11yNotificationType::kDictationOnlySodaDownloaded:
return kDictationMenuIcon;
default:
return kNotificationChromevoxIcon;
@ -341,33 +343,59 @@ void ShowAccessibilityNotification(
text = l10n_util::GetStringUTF16(
IDS_ASH_STATUS_TRAY_BRAILLE_DISPLAY_CONNECTED);
catalog_name = NotificationCatalogName::kBrailleDisplayConnected;
} else if (type == A11yNotificationType::kDictationAllDlcsDownloaded) {
display_source =
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DICTATION);
title = l10n_util::GetStringFUTF16(
IDS_ASH_A11Y_DICTATION_NOTIFICATION_ALL_DLCS_DOWNLOADED_TITLE,
replacements, nullptr);
text = l10n_util::GetStringUTF16(
IDS_ASH_A11Y_DICTATION_NOTIFICATION_ALL_DLCS_DOWNLOADED_DESC);
catalog_name = NotificationCatalogName::kDictationAllDlcsDownloaded;
pinned = false;
} else if (type == A11yNotificationType::kDictationNoDlcsDownloaded) {
display_source =
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DICTATION);
title = l10n_util::GetStringFUTF16(
IDS_ASH_A11Y_DICTATION_NOTIFICATION_NO_DLCS_DOWNLOADED_TITLE,
replacements, nullptr);
text = l10n_util::GetStringUTF16(
IDS_ASH_A11Y_DICTATION_NOTIFICATION_NO_DLCS_DOWNLOADED_DESC);
catalog_name = NotificationCatalogName::kDictationNoDlcsDownloaded;
pinned = false;
// Use CRITICAL_WARNING to force the notification color to red.
warning = message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
} else if (type == A11yNotificationType::kDicationOnlyPumpkinDownloaded) {
display_source =
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DICTATION);
title = l10n_util::GetStringFUTF16(
IDS_ASH_A11Y_DICTATION_NOTIFICATION_ONLY_PUMPKIN_DOWNLOADED_TITLE,
replacements, nullptr);
text = l10n_util::GetStringUTF16(
IDS_ASH_A11Y_DICTATION_NOTIFICATION_ONLY_PUMPKIN_DOWNLOADED_DESC);
catalog_name = NotificationCatalogName::kDicationOnlyPumpkinDownloaded;
pinned = false;
// Use CRITICAL_WARNING to force the notification color to red.
warning = message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
} else if (type == A11yNotificationType::kDictationOnlySodaDownloaded) {
display_source =
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DICTATION);
title = l10n_util::GetStringFUTF16(
IDS_ASH_A11Y_DICTATION_NOTIFICATION_ONLY_SODA_DOWNLOADED_TITLE,
replacements, nullptr);
text = l10n_util::GetStringUTF16(
IDS_ASH_A11Y_DICTATION_NOTIFICATION_ONLY_SODA_DOWNLOADED_DESC);
catalog_name = NotificationCatalogName::kDictationOnlySodaDownloaded;
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);
text = l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SWITCH_ACCESS_ENABLED);
catalog_name = NotificationCatalogName::kSwitchAccessEnabled;
} else if (type == A11yNotificationType::kSpeechRecognitionFilesDownloaded) {
display_source =
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DICTATION);
title = l10n_util::GetStringFUTF16(
IDS_ASH_A11Y_DICTATION_NOTIFICATION_SODA_DOWNLOAD_SUCCEEDED_TITLE,
replacements, nullptr);
text = l10n_util::GetStringUTF16(
IDS_ASH_A11Y_DICTATION_NOTIFICATION_SODA_DOWNLOAD_SUCCEEDED_DESC);
pinned = false;
catalog_name = NotificationCatalogName::kSpeechRecognitionFilesDownloaded;
} else if (type == A11yNotificationType::kSpeechRecognitionFilesFailed) {
display_source =
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DICTATION);
title = l10n_util::GetStringFUTF16(
IDS_ASH_A11Y_DICTATION_NOTIFICATION_SODA_DOWNLOAD_FAILED_TITLE,
replacements, nullptr);
text = l10n_util::GetStringUTF16(
IDS_ASH_A11Y_DICTATION_NOTIFICATION_SODA_DOWNLOAD_FAILED_DESC);
// Use CRITICAL_WARNING to force the notification color to red.
warning = message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
pinned = false;
catalog_name = NotificationCatalogName::kSpeechRecognitionFilesFailed;
} else {
bool is_tablet = Shell::Get()->tablet_mode_controller()->InTabletMode();
@ -382,6 +410,7 @@ void ShowAccessibilityNotification(
? NotificationCatalogName::kSpokenFeedbackBrailleEnabled
: NotificationCatalogName::kSpokenFeedbackEnabled;
}
message_center::RichNotificationData options;
options.should_make_spoken_feedback_for_popup_updates = false;
std::unique_ptr<message_center::Notification> notification =
@ -2412,15 +2441,27 @@ void AccessibilityControllerImpl::
->UpdateOnSpeechRecognitionDownloadChanged(download_progress);
}
void AccessibilityControllerImpl::
ShowSpeechRecognitionDownloadNotificationForDictation(
bool succeeded,
const std::u16string& display_language) {
A11yNotificationType type =
succeeded ? A11yNotificationType::kSpeechRecognitionFilesDownloaded
: A11yNotificationType::kSpeechRecognitionFilesFailed;
void AccessibilityControllerImpl::ShowNotificationForDictation(
DictationNotificationType type,
const std::u16string& display_language) {
A11yNotificationType notification_type;
switch (type) {
case DictationNotificationType::kAllDlcsDownloaded:
notification_type = A11yNotificationType::kDictationAllDlcsDownloaded;
break;
case DictationNotificationType::kNoDlcsDownloaded:
notification_type = A11yNotificationType::kDictationNoDlcsDownloaded;
break;
case DictationNotificationType::kOnlySodaDownloaded:
notification_type = A11yNotificationType::kDictationOnlySodaDownloaded;
break;
case DictationNotificationType::kOnlyPumpkinDownloaded:
notification_type = A11yNotificationType::kDicationOnlyPumpkinDownloaded;
break;
}
ShowAccessibilityNotification(A11yNotificationWrapper(
type, std::vector<std::u16string>{display_language}));
notification_type, std::vector<std::u16string>{display_language}));
}
AccessibilityControllerImpl::A11yNotificationWrapper::

@ -67,16 +67,20 @@ enum class A11yNotificationType {
kSpokenFeedbackEnabled,
// Shown when braille display is connected while spoken feedback is enabled.
kBrailleDisplayConnected,
// Shown when all Dictation-related DLCs have downloaded successfully.
kDictationAllDlcsDownloaded,
// Shown when all Dictation-related DLCs failed to download.
kDictationNoDlcsDownloaded,
// Shown when the Pumpkin DLC (but no other DLCs) have downloaded.
kDicationOnlyPumpkinDownloaded,
// Shown when the SODA DLC (but no other DLCs) have downloaded.
kDictationOnlySodaDownloaded,
// Shown when braille display is connected while spoken feedback is not
// enabled yet. Note: in this case braille display connected would enable
// spoken feedback.
kSpokenFeedbackBrailleEnabled,
// Shown when Switch Access is enabled.
kSwitchAccessEnabled,
// Shown when speech recognition files download successfully.
kSpeechRecognitionFilesDownloaded,
// Shown when speech recognition files download fails.
kSpeechRecognitionFilesFailed,
};
// The controller for accessibility features in ash. Features can be enabled
@ -423,8 +427,8 @@ class ASH_EXPORT AccessibilityControllerImpl : public AccessibilityController,
void EnableChromeVoxVolumeSlideGesture() override;
void UpdateDictationButtonOnSpeechRecognitionDownloadChanged(
int download_progress) override;
void ShowSpeechRecognitionDownloadNotificationForDictation(
bool succeeded,
void ShowNotificationForDictation(
DictationNotificationType type,
const std::u16string& display_language) override;
void UpdateDictationBubble(
bool visible,

@ -1141,21 +1141,23 @@ TEST_F(AccessibilityControllerTest, SelectToSpeakStateChanges) {
TEST_F(AccessibilityControllerTest,
SpeechRecognitionDownloadSucceededNotification) {
const std::u16string kSucceededTitle = u"English speech files downloaded";
const std::u16string kSucceededTitle =
u"English speech files partially downloaded";
const std::u16string kSucceededDescription =
u"Speech is now processed locally and Dictation works offline";
u"Speech is processed locally and dictation works offline, but some "
u"voice commands wont work.";
AccessibilityControllerImpl* controller =
Shell::Get()->accessibility_controller();
controller->ShowSpeechRecognitionDownloadNotificationForDictation(true,
u"English");
controller->ShowNotificationForDictation(
DictationNotificationType::kOnlySodaDownloaded, u"English");
message_center::NotificationList::Notifications notifications =
MessageCenter::Get()->GetVisibleNotifications();
ASSERT_EQ(1u, notifications.size());
EXPECT_EQ(kSucceededTitle, (*notifications.begin())->title());
EXPECT_EQ(kSucceededDescription, (*notifications.begin())->message());
EXPECT_EQ(u"Dictation", (*notifications.begin())->display_source());
EXPECT_EQ(message_center::SystemNotificationWarningLevel::NORMAL,
EXPECT_EQ(message_center::SystemNotificationWarningLevel::CRITICAL_WARNING,
(*notifications.begin())->system_notification_warning_level());
}
@ -1164,12 +1166,12 @@ TEST_F(AccessibilityControllerTest,
const std::u16string kFailedTitle = u"Couldn't download English speech files";
const std::u16string kFailedDescription =
u"Download will be attempted later. Speech will be sent to Google for "
u"processing until download is completed.";
u"processing for now.";
AccessibilityControllerImpl* controller =
Shell::Get()->accessibility_controller();
controller->ShowSpeechRecognitionDownloadNotificationForDictation(false,
u"English");
controller->ShowNotificationForDictation(
DictationNotificationType::kNoDlcsDownloaded, u"English");
message_center::NotificationList::Notifications notifications =
MessageCenter::Get()->GetVisibleNotifications();
ASSERT_EQ(1u, notifications.size());

@ -5305,19 +5305,30 @@ Here are some things you can try to get started.
<message name="IDS_ASH_ACCESSIBILITY_DICTATION_BUTTON_TOOLTIP_SODA_DOWNLOADING" desc="A tooltip explaining that speech recognition files are downloading." >
Downloading speech files
</message>
<message name="IDS_ASH_A11Y_DICTATION_NOTIFICATION_SODA_DOWNLOAD_SUCCEEDED_TITLE" desc="The title for a notification that is shown when speech recognition files download successfully.">
<message name="IDS_ASH_A11Y_DICTATION_NOTIFICATION_ALL_DLCS_DOWNLOADED_TITLE" desc="The title for a notification that is shown when all Dictation assets download successfully.">
<ph name="Language">$1<ex>English</ex></ph> speech files downloaded
</message>
<message name="IDS_ASH_A11Y_DICTATION_NOTIFICATION_SODA_DOWNLOAD_SUCCEEDED_DESC" desc="The text for a notification that is shown when speech recognition files download successfully.">
<message name="IDS_ASH_A11Y_DICTATION_NOTIFICATION_ALL_DLCS_DOWNLOADED_DESC" desc="The text for a notification that is shown when all Dictation assets download successfully.">
Speech is now processed locally and Dictation works offline
</message>
<message name="IDS_ASH_A11Y_DICTATION_NOTIFICATION_SODA_DOWNLOAD_FAILED_TITLE" desc="The title for a notification that is shown when speech recognition files fail to download.">
<message name="IDS_ASH_A11Y_DICTATION_NOTIFICATION_NO_DLCS_DOWNLOADED_TITLE" desc="The title for a notification that is shown when all Dictation assets fail to download.">
Couldn't download <ph name="Language">$1<ex>English</ex></ph> speech files
</message>
<message name="IDS_ASH_A11Y_DICTATION_NOTIFICATION_SODA_DOWNLOAD_FAILED_DESC" desc="The text for a notification that is shown when speech recognition files fail to download.">
Download will be attempted later. Speech will be sent to Google for processing until download is completed.
<message name="IDS_ASH_A11Y_DICTATION_NOTIFICATION_NO_DLCS_DOWNLOADED_DESC" desc="The text for a notification that is shown when all Dictation assets fail to download.">
Download will be attempted later. Speech will be sent to Google for processing for now.
</message>
<message name="IDS_ASH_A11Y_DICTATION_NOTIFICATION_ONLY_PUMPKIN_DOWNLOADED_TITLE" desc="The title for a notification that is shown when command parsing assets download successfully. This notification is only shown if command parsing assets are downloaded and speech recognition assets are not.">
<ph name="Language">$1<ex>English</ex></ph> speech files partially downloaded
</message>
<message name="IDS_ASH_A11Y_DICTATION_NOTIFICATION_ONLY_PUMPKIN_DOWNLOADED_DESC" desc="The description for a notification that is shown when command parsing assets download successfully. This notification is only shown if command parsing assets are downloaded and speech recognition assets are not.">
Download will be attempted later. Speech will be sent to Google for processing for now.
</message>
<message name="IDS_ASH_A11Y_DICTATION_NOTIFICATION_ONLY_SODA_DOWNLOADED_TITLE" desc="The title 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.">
<ph name="Language">$1<ex>English</ex></ph> speech files partially downloaded
</message>
<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>
<!-- Strings for persistent desks bar-->
<message name="IDS_ASH_PERSISTENT_DESKS_BAR_CONTEXT_MENU_FEEDBACK" desc="Title of the menu item in the context menu to send feedback.">
Feedback

@ -0,0 +1 @@
da2649add3c3e89f6a07af97a7d9ca82809f061e

@ -0,0 +1 @@
da2649add3c3e89f6a07af97a7d9ca82809f061e

@ -0,0 +1 @@
52ebb71e71a7ef3f7dbdc208ff66e9b90bc4d359

@ -0,0 +1 @@
52ebb71e71a7ef3f7dbdc208ff66e9b90bc4d359

@ -0,0 +1 @@
7e7b638b53f33749e7473f4e242f6342f090f2c9

@ -0,0 +1 @@
7e7b638b53f33749e7473f4e242f6342f090f2c9

@ -0,0 +1 @@
4c4a6c719652bd36c08b2a2d1cba45edc70b3e8f

@ -0,0 +1 @@
4c4a6c719652bd36c08b2a2d1cba45edc70b3e8f

@ -173,7 +173,11 @@ enum class NotificationCatalogName {
kArcVmDataMigration = 158,
kWebHid = 159,
kDoNotDisturb = 160,
kMaxValue = kDoNotDisturb,
kDictationAllDlcsDownloaded = 161,
kDictationNoDlcsDownloaded = 162,
kDicationOnlyPumpkinDownloaded = 163,
kDictationOnlySodaDownloaded = 164,
kMaxValue = kDictationOnlySodaDownloaded
};
// A living catalog that registers system nudges.

@ -25,6 +25,7 @@ enum class AccessibilityPanelState;
enum class DictationToggleSource;
enum class DictationBubbleHintType;
enum class DictationBubbleIconType;
enum class DictationNotificationType;
class SelectToSpeakEventHandlerDelegate;
enum class SelectToSpeakState;
@ -182,8 +183,8 @@ class ASH_PUBLIC_EXPORT AccessibilityController {
// Shows a notification card in the message center informing the user that
// speech recognition files have either downloaded successfully or failed.
// Specific to the Dictation feature.
virtual void ShowSpeechRecognitionDownloadNotificationForDictation(
bool succeeded,
virtual void ShowNotificationForDictation(
DictationNotificationType type,
const std::u16string& display_language) = 0;
// Updates the Dictation UI bubble. `text` is optional to allow clients to

@ -224,6 +224,14 @@ enum class DictationBubbleHintType {
kCopy,
};
// The types of notifications that can be shown by Dictation.
enum class DictationNotificationType {
kAllDlcsDownloaded,
kNoDlcsDownloaded,
kOnlySodaDownloaded,
kOnlyPumpkinDownloaded,
};
} // namespace ash
#endif // ASH_PUBLIC_CPP_ACCESSIBILITY_CONTROLLER_ENUMS_H_

@ -494,6 +494,8 @@ AccessibilityManager::AccessibilityManager() {
&AccessibilityManager::PlayVolumeAdjustSound, base::Unretained(this)));
CrasAudioHandler::Get()->AddAudioObserver(this);
pumpkin_installer_ = std::make_unique<PumpkinInstaller>();
}
AccessibilityManager::~AccessibilityManager() {
@ -2318,7 +2320,7 @@ void AccessibilityManager::OnSodaInstalled(speech::LanguageCode language_code) {
return;
if (ShouldShowSodaSucceededNotificationForDictation())
ShowSodaDownloadNotificationForDictation(true);
UpdateDictationNotification();
OnSodaInstallUpdated(100);
}
@ -2333,7 +2335,7 @@ void AccessibilityManager::OnSodaInstallError(
// Show the failed message if either the Dictation locale failed or the SODA
// binary failed (encoded by LanguageCode::kNone).
if (ShouldShowSodaFailedNotificationForDictation(language_code))
ShowSodaDownloadNotificationForDictation(false);
UpdateDictationNotification();
OnSodaInstallUpdated(0);
}
@ -2380,11 +2382,7 @@ bool AccessibilityManager::ShouldShowSodaFailedNotificationForDictation(
language_code == GetDictationLanguageCode();
}
void AccessibilityManager::ShowSodaDownloadNotificationForDictation(
bool succeeded) {
if (!::features::IsDictationOfflineAvailable())
return;
void AccessibilityManager::UpdateDictationNotification() {
const std::string locale =
profile_->GetPrefs()->GetString(prefs::kAccessibilityDictationLocale);
// Get the display name of |locale| in the application locale.
@ -2392,11 +2390,35 @@ void AccessibilityManager::ShowSodaDownloadNotificationForDictation(
/*locale=*/locale,
/*display_locale=*/g_browser_process->GetApplicationLocale(),
/*is_ui=*/true);
AccessibilityController::Get()
->ShowSpeechRecognitionDownloadNotificationForDictation(succeeded,
display_name);
if (!succeeded)
bool soda_installed = false;
if (::features::IsDictationOfflineAvailable()) {
// Only access SodaInstaller if offline Dictation is available.
soda_installed = speech::SodaInstaller::GetInstance()->IsSodaInstalled(
GetDictationLanguageCode());
}
bool pumpkin_installed = pumpkin_installer_->IsPumpkinInstalled();
// There are four possible states for the Dictation notification:
// 1. Pumpkin installed, SODA installed
// 2. Pumpkin installed, SODA not installed
// 3. Pumpkin not installed, SODA installed
// 4. Pumpkin not installed, SODA not installed
DictationNotificationType type;
if (pumpkin_installed && soda_installed) {
type = DictationNotificationType::kAllDlcsDownloaded;
} else if (pumpkin_installed && !soda_installed) {
type = DictationNotificationType::kOnlyPumpkinDownloaded;
} else if (!pumpkin_installed && soda_installed) {
type = DictationNotificationType::kOnlySodaDownloaded;
} else {
type = DictationNotificationType::kNoDlcsDownloaded;
}
AccessibilityController::Get()->ShowNotificationForDictation(type,
display_name);
if (type == DictationNotificationType::kNoDlcsDownloaded)
soda_failed_notification_shown_ = true;
}
@ -2415,9 +2437,6 @@ void AccessibilityManager::InstallPumpkinForDictation(
return;
}
if (!pumpkin_installer_)
pumpkin_installer_ = std::make_unique<PumpkinInstaller>();
// Save `callback` and run it after the installation succeeds or fails.
install_pumpkin_callback_ = std::move(callback);
pumpkin_installer_->MaybeInstall(
@ -2445,6 +2464,8 @@ void AccessibilityManager::OnPumpkinInstalled(bool success) {
base::BindOnce(&CreatePumpkinData, base_pumpkin_path),
base::BindOnce(&AccessibilityManager::OnPumpkinDataCreated,
weak_ptr_factory_.GetWeakPtr()));
UpdateDictationNotification();
}
void AccessibilityManager::OnPumpkinDataCreated(
@ -2457,7 +2478,8 @@ void AccessibilityManager::OnPumpkinError(const std::string& error) {
DCHECK(!install_pumpkin_callback_.is_null());
std::move(install_pumpkin_callback_).Run(nullptr);
is_pumpkin_installed_for_testing_ = false;
// TODO(akihiroota): Consider showing the error message to the user.
UpdateDictationNotification();
}
void AccessibilityManager::GetDlcContents(DlcType dlc,

@ -521,13 +521,18 @@ class AccessibilityManager
bool ShouldShowSodaSucceededNotificationForDictation();
bool ShouldShowSodaFailedNotificationForDictation(
speech::LanguageCode language_code);
void ShowSodaDownloadNotificationForDictation(bool succeeded);
// Updates the Dictation notification according to DLC states. Assumes that
// it's only called when a Dictation-related DLC has downloaded (or failed to
// download).
void UpdateDictationNotification();
void ShowDictationLanguageUpgradedNudge(const std::string& locale);
speech::LanguageCode GetDictationLanguageCode();
void CreateChromeVoxPanel();
// Pumpkin-related methods.
void OnPumpkinInstalled(bool success);
void OnPumpkinError(const std::string& error);
void OnPumpkinDataCreated(
@ -644,7 +649,7 @@ class AccessibilityManager
friend class DictationTest;
friend class SwitchAccessTest;
friend class AccessibilityManagerTest;
friend class AccessibilityManagerSodaTest;
friend class AccessibilityManagerDlcTest;
friend class AccessibilityManagerDictationDialogTest;
friend class AccessibilityManagerNoOnDeviceSpeechRecognitionTest;
};

@ -11,6 +11,7 @@
#include "base/callback_helpers.h"
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/strings/string_util.h"
#include "chrome/browser/ash/accessibility/accessibility_test_utils.h"
#include "chrome/browser/ash/accessibility/magnification_manager.h"
#include "chrome/browser/ash/login/session/user_session_manager.h"
@ -67,6 +68,27 @@ using ::testing::WithParamInterface;
constexpr char kTestUserName[] = "owner@gmail.com";
constexpr char kTestUserGaiaId[] = "9876543210";
// Dictation notification titles and descriptions. '*'s are used as placeholders
// for languages, which are substituted in at a later time.
std::u16string kDictationAllDlcsDownloadedTitle = u"* speech files downloaded";
std::u16string kDictationAllDlcsDownloadedDesc =
u"Speech is now processed locally and Dictation works offline";
std::u16string kDictationNoDlcsDownloadedTitle =
u"Couldn't download * speech files";
std::u16string kDictationNoDlcsDownloadedDesc =
u"Download will be attempted later. Speech will be sent to Google for "
u"processing for now.";
std::u16string kDicationOnlyPumpkinDownloadedTitle =
u"* speech files partially downloaded";
std::u16string kDicationOnlyPumpkinDownloadedDesc =
u"Download will be attempted later. Speech will be sent to Google for "
u"processing for now.";
std::u16string kDictationOnlySodaDownloadedTitle =
u"* speech files partially downloaded";
std::u16string kDictationOnlySodaDownloadedDesc =
u"Speech is processed locally and dictation works offline, but some voice "
u"commands wont work.";
constexpr int kTestAutoclickDelayMs = 2000;
class MockAccessibilityObserver {
@ -315,31 +337,60 @@ absl::optional<bool> GetDictationOfflineNudgePref(const std::string& locale) {
return offline_nudges.FindBool(locale);
}
void AssertSodaNotificationShownForDictation(
const std::u16string& display_language,
bool success) {
const std::u16string kTitle =
success ? display_language + u" speech files downloaded"
: u"Couldn't download " + display_language + u" speech files";
const std::u16string kDescription =
success ? u"Speech is now processed locally and Dictation works offline"
: u"Download will be attempted later. Speech will be sent to "
u"Google for processing until download is completed.";
message_center::SystemNotificationWarningLevel warning =
success
? message_center::SystemNotificationWarningLevel::NORMAL
: message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
void AssertDictationNotificationShown(const std::u16string& display_language,
const std::u16string& title,
const std::u16string& description,
bool is_critical) {
// Replace the '*' placeholder in `title` with `display_name`.
std::u16string new_title = title;
ASSERT_TRUE(
base::ReplaceChars(new_title, u"*", display_language, &new_title));
message_center::NotificationList::Notifications notifications =
message_center::MessageCenter::Get()->GetVisibleNotifications();
ASSERT_EQ(1u, notifications.size());
ASSERT_EQ(kTitle, (*notifications.begin())->title());
ASSERT_EQ(kDescription, (*notifications.begin())->message());
ASSERT_EQ(new_title, (*notifications.begin())->title());
ASSERT_EQ(description, (*notifications.begin())->message());
ASSERT_EQ(u"Dictation", (*notifications.begin())->display_source());
message_center::SystemNotificationWarningLevel warning =
is_critical
? message_center::SystemNotificationWarningLevel::CRITICAL_WARNING
: message_center::SystemNotificationWarningLevel::NORMAL;
ASSERT_EQ(warning,
(*notifications.begin())->system_notification_warning_level());
}
void AssertDictationAllDlcsNotifcation(const std::u16string& display_language) {
AssertDictationNotificationShown(display_language,
kDictationAllDlcsDownloadedTitle,
kDictationAllDlcsDownloadedDesc,
/*is_critical=*/false);
}
void AssertDictationNoDlcsNotifcation(const std::u16string& display_language) {
AssertDictationNotificationShown(display_language,
kDictationNoDlcsDownloadedTitle,
kDictationNoDlcsDownloadedDesc,
/*is_critical=*/true);
}
void AssertDictationOnlySodaNotifcation(
const std::u16string& display_language) {
AssertDictationNotificationShown(display_language,
kDictationOnlySodaDownloadedTitle,
kDictationOnlySodaDownloadedDesc,
/*is_critical=*/true);
}
void AssertDictationOnlyPumpkinNotifcation(
const std::u16string& display_language) {
AssertDictationNotificationShown(display_language,
kDicationOnlyPumpkinDownloadedTitle,
kDicationOnlyPumpkinDownloadedDesc,
/*is_critical=*/true);
}
void AssertMessageCenterEmpty() {
message_center::NotificationList::Notifications notifications =
message_center::MessageCenter::Get()->GetVisibleNotifications();
@ -860,19 +911,22 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerTest, TtsDlcTypeToPath) {
TtsDlcTypeToPath(DlcType::DLC_TYPE_TTSSVSE));
}
class AccessibilityManagerSodaTest : public AccessibilityManagerTest {
protected:
AccessibilityManagerSodaTest()
class AccessibilityManagerDlcTest : public AccessibilityManagerTest {
public:
AccessibilityManagerDlcTest()
: disable_animations_(
ui::ScopedAnimationDurationScaleMode::ZERO_DURATION) {}
~AccessibilityManagerSodaTest() override = default;
AccessibilityManagerSodaTest(const AccessibilityManagerSodaTest&) = delete;
AccessibilityManagerSodaTest& operator=(const AccessibilityManagerSodaTest&) =
~AccessibilityManagerDlcTest() override = default;
AccessibilityManagerDlcTest(const AccessibilityManagerDlcTest&) = delete;
AccessibilityManagerDlcTest& operator=(const AccessibilityManagerDlcTest&) =
delete;
protected:
void SetUpOnMainThread() override {
UninstallSodaForTesting();
EnsureSodaObservation();
ClearMessageCenter();
AssertMessageCenterEmpty();
AccessibilityManagerTest::SetUpOnMainThread();
}
@ -888,6 +942,20 @@ class AccessibilityManagerSodaTest : public AccessibilityManagerTest {
AccessibilityManager::Get()->soda_observation_.Observe(soda_installer());
}
void InstallPumpkinAndWait() {
base::RunLoop loop;
AccessibilityManager::Get()->InstallPumpkinForDictation(base::DoNothing());
loop.RunUntilIdle();
}
void OnPumpkinError() {
AccessibilityManager* manager = AccessibilityManager::Get();
// We require `install_pumpkin_callback_` to be set before `OnPumpkinError`
// can be called.
manager->install_pumpkin_callback_ = base::DoNothing();
AccessibilityManager::Get()->OnPumpkinError("Error");
}
speech::SodaInstaller* soda_installer() {
return speech::SodaInstaller::GetInstance();
}
@ -901,11 +969,12 @@ class AccessibilityManagerSodaTest : public AccessibilityManagerTest {
private:
ui::ScopedAnimationDurationScaleMode disable_animations_;
base::test::ScopedFeatureList scoped_feature_list_;
};
// Tests that SODA download is initiated when Dictation is enabled.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
DownloadWhenDictationEnabled) {
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaDownloadWhenDictationEnabled) {
ClearDictationOfflineNudgePref("en-US");
EXPECT_FALSE(IsSodaDownloading());
EXPECT_FALSE(ShouldShowNetworkDictationDialog("en-US"));
@ -921,7 +990,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
EXPECT_FALSE(GetDictationOfflineNudgePref("en-US"));
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaDownloadNotTriggeredByUserShowsNudge) {
SetDictationEnabled(true);
// Reset to initial state: no SODA, no locale. Then trigger the dictation
@ -942,7 +1011,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
AssertMessageCenterEmpty();
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaErrorNotTriggeredByUserTriesToShowNudge) {
SetDictationEnabled(true);
// Reset to initial state: no SODA, no locale. Then trigger the dictation
@ -963,7 +1032,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
AssertMessageCenterEmpty();
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
OneNudgeForSodaMultipleDownload) {
ClearDictationOfflineNudgePref("en-US");
EnableDictationTriggeredByUser(/*soda_uninstalled_first=*/true);
@ -985,7 +1054,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
EXPECT_TRUE(GetDictationOfflineNudgePref("en-US").value());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaInstalledBeforeDictationEnabled) {
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
@ -999,7 +1068,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
EXPECT_TRUE(GetDictationOfflineNudgePref("en-US").value());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaDownloadTriggeredByLocaleChange) {
EXPECT_FALSE(IsSodaDownloading());
@ -1021,15 +1090,14 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
// The nudge was never shown because this was a user-initiated change.
EXPECT_FALSE(GetDictationOfflineNudgePref("en-US"));
// The notification was shown.
AssertSodaNotificationShownForDictation(u"English (United States)",
/*success=*/true);
AssertDictationOnlySodaNotifcation(en_us_display_name());
}
// Ensures that we show the SODA succeeded notification for Dictation if the
// SODA binary downloads, followed by the language pack matching the dictation
// language.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
SucceededNotificationCase1) {
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaSucceededNotificationCase1) {
// For this test, pretend that the Dictation locale is fr-FR.
g_browser_process->SetApplicationLocale("fr-FR");
SetDictationEnabled(true);
@ -1037,58 +1105,53 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting(fr_fr());
AssertSodaNotificationShownForDictation(u"français (France)",
/*success=*/true);
AssertDictationOnlySodaNotifcation(u"français (France)");
}
// Similar to above. Ensures that we show the SODA succeeded notification for
// Dictation if the language pack downloads, followed by the SODA binary.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
SucceededNotificationCase2) {
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaSucceededNotificationCase2) {
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting();
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/true);
AssertDictationOnlySodaNotifcation(en_us_display_name());
}
// Ensures that we show the SODA failed notification for Dictation if the SODA
// binary fails.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaFailedNotificationBinaryError) {
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting();
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/false);
AssertDictationNoDlcsNotifcation(en_us_display_name());
}
// Similar to above. Ensures that we show the SODA failed notification for
// Dictation if the language pack fails.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaFailedNotificationLanguageError) {
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/false);
AssertDictationNoDlcsNotifcation(en_us_display_name());
}
// Ensures that the SODA failed notification for Dictation is given if
// the language pack downloads, but the SODA binary fails.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
LanguageInstalledBinaryFails) {
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaLanguageInstalledBinaryFails) {
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaErrorForTesting();
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/false);
AssertDictationNoDlcsNotifcation(en_us_display_name());
}
// Similar to above. Ensures that we show the SODA failed notification if the
// SODA binary downloads, but the language pack fails.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
BinaryInstalledLanguageFails) {
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaBinaryInstalledLanguageFails) {
// For this test, pretend that the Dictation locale is fr-FR.
g_browser_process->SetApplicationLocale("fr-FR");
SetDictationEnabled(true);
@ -1096,18 +1159,16 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaErrorForTesting(fr_fr());
AssertSodaNotificationShownForDictation(u"français (France)",
/*success=*/false);
AssertDictationNoDlcsNotifcation(u"français (France)");
}
// Ensures that SODA failed notification is shown just once if both the SODA
// binary fails and the language pack fails.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaFailedNotificationNotShownTwice) {
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/false);
AssertDictationNoDlcsNotifcation(en_us_display_name());
ClearMessageCenter();
// No second message is shown on additional failures.
@ -1119,12 +1180,11 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
// Ensures that SODA failed notification could be shown each time Dictation
// is toggled on.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaFailedNotificationShownOncePerDownload) {
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/false);
AssertDictationNoDlcsNotifcation(en_us_display_name());
SetDictationEnabled(false);
// Reset SODA state so that it tries the download again.
@ -1134,13 +1194,12 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
// A fresh attempt at Dictation means another chance to show an error message.
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/false);
AssertDictationNoDlcsNotifcation(en_us_display_name());
}
// Tests that the SODA download notification for Dictation is NOT given if
// Dictation wasn't triggered by the user.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest, NotTriggeredByUser) {
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, SodaNotTriggeredByUser) {
EnableDictationTriggeredByUser(/*soda_uninstalled_first=*/false);
soda_installer()->NotifySodaInstalledForTesting();
soda_installer()->NotifySodaInstalledForTesting(en_us());
@ -1149,7 +1208,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest, NotTriggeredByUser) {
// Tests that the SODA download notification for Dictation is NOT given if
// the installed language doesn't match the Dictation locale.
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest, WrongLanguage) {
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, SodaWrongLanguage) {
// For this test, pretend that the Dictation locale is fr-FR.
g_browser_process->SetApplicationLocale("fr-FR");
SetDictationEnabled(true);
@ -1159,8 +1218,8 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest, WrongLanguage) {
AssertMessageCenterEmpty();
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
NotificationShownOnDictationLocaleChange) {
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
SodaNotificationShownOnDictationLocaleChange) {
// it-IT is not supported by SODA.
SetDictationLocale("it-IT");
EnableDictationTriggeredByUser(/*soda_uninstalled_first=*/false);
@ -1173,8 +1232,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
soda_installer()->NotifySodaInstalledForTesting(en_us());
// The notification should have been shown.
AssertSodaNotificationShownForDictation(en_us_display_name(),
/*success=*/true);
AssertDictationOnlySodaNotifcation(en_us_display_name());
}
// Tests the behavior of AccessibilityManager and when it calls the
@ -1182,7 +1240,7 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerSodaTest,
// The method is used to update the Dictation button tray when SODA download
// state changes.
IN_PROC_BROWSER_TEST_F(
AccessibilityManagerSodaTest,
AccessibilityManagerDlcTest,
UpdateDictationButtonOnSpeechRecognitionDownloadChanged) {
SetDictationLocale("en-US");
SetDictationEnabled(true);
@ -1208,6 +1266,51 @@ IN_PROC_BROWSER_TEST_F(
EXPECT_EQ(100, test_api->GetDictationSodaDownloadProgress());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, SodaSuccessPumpkinSuccess) {
SetDictationLocale("en-US");
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting();
AssertDictationOnlySodaNotifcation(en_us_display_name());
InstallPumpkinAndWait();
AssertDictationAllDlcsNotifcation(en_us_display_name());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, SodaSuccessPumpkinFail) {
SetDictationLocale("en-US");
SetDictationEnabled(true);
soda_installer()->NotifySodaInstalledForTesting(en_us());
AssertMessageCenterEmpty();
soda_installer()->NotifySodaInstalledForTesting();
AssertDictationOnlySodaNotifcation(en_us_display_name());
OnPumpkinError();
AssertDictationOnlySodaNotifcation(en_us_display_name());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, SodaFailPumpkinSuccess) {
// Calling `InstallPumpkinAndWait()` will run the message loop and cause SODA
// to be installed. Avoid this scenario for the purposes of this test.
soda_installer()->NeverDownloadSodaForTesting();
SetDictationLocale("en-US");
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertDictationNoDlcsNotifcation(en_us_display_name());
InstallPumpkinAndWait();
AssertDictationOnlyPumpkinNotifcation(en_us_display_name());
}
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, SodaFailPumpkinFail) {
SetDictationLocale("en-US");
SetDictationEnabled(true);
soda_installer()->NotifySodaErrorForTesting(en_us());
AssertDictationNoDlcsNotifcation(en_us_display_name());
OnPumpkinError();
AssertDictationNoDlcsNotifcation(en_us_display_name());
}
enum DictationDialogTestVariant {
kOfflineEnabledAndAvailable,
kOfflineEnabledAndUnavailable,

@ -57,6 +57,7 @@ void PumpkinInstaller::MaybeInstallHelper(
OnError(kPumpkinInstallingError);
return;
case dlcservice::DlcState_State_INSTALLED:
is_pumpkin_installed_ = true;
CHECK(!on_installed_.is_null());
std::move(on_installed_).Run(true);
return;
@ -77,6 +78,7 @@ void PumpkinInstaller::MaybeInstallHelper(
void PumpkinInstaller::OnInstalled(
const DlcserviceClient::InstallResult& install_result) {
pending_dlc_request_ = false;
is_pumpkin_installed_ = true;
base::UmaHistogramBoolean(kInstallationMetricName,
install_result.error == dlcservice::kErrorNone);
if (install_result.error != dlcservice::kErrorNone) {
@ -93,6 +95,7 @@ void PumpkinInstaller::OnProgress(double progress) {
}
void PumpkinInstaller::OnError(const std::string& error) {
is_pumpkin_installed_ = false;
CHECK(!on_error_.is_null());
std::move(on_error_).Run(error);
}

@ -33,6 +33,8 @@ class PumpkinInstaller {
ProgressCallback on_progress,
ErrorCallback on_error);
bool IsPumpkinInstalled() const { return is_pumpkin_installed_; }
private:
// A helper function that is run once we've grabbed the state of the Pumpkin
// DLC from the DLC service.
@ -52,6 +54,7 @@ class PumpkinInstaller {
// Requests to DlcserviceClient are async. This is true if we've made a
// request and are still waiting for a response.
bool pending_dlc_request_ = false;
bool is_pumpkin_installed_ = false;
base::WeakPtrFactory<PumpkinInstaller> weak_ptr_factory_{this};
};

@ -100,10 +100,9 @@ void FakeAccessibilityController::
UpdateDictationButtonOnSpeechRecognitionDownloadChanged(
int download_progress) {}
void FakeAccessibilityController::
ShowSpeechRecognitionDownloadNotificationForDictation(
bool succeeded,
const std::u16string& display_language) {}
void FakeAccessibilityController::ShowNotificationForDictation(
ash::DictationNotificationType type,
const std::u16string& display_language) {}
void FakeAccessibilityController::UpdateDictationBubble(
bool visible,

@ -64,8 +64,8 @@ class FakeAccessibilityController : ash::AccessibilityController {
void DisableSwitchAccessDisableConfirmationDialogTesting() override;
void UpdateDictationButtonOnSpeechRecognitionDownloadChanged(
int download_progress) override;
void ShowSpeechRecognitionDownloadNotificationForDictation(
bool succeeded,
void ShowNotificationForDictation(
ash::DictationNotificationType type,
const std::u16string& display_language) override;
void UpdateDictationBubble(
bool visible,

@ -72507,6 +72507,10 @@ Called by update_net_trust_anchors.py.-->
<int value="158" label="ARCVM Data Migration"/>
<int value="159" label="WebHID"/>
<int value="160" label="Do Not Disturb"/>
<int value="161" label="Dictation all DLCs downloaded"/>
<int value="162" label="Dictation no DLCs downloaded"/>
<int value="163" label="Dictation only Pumpkin DLC downloaded"/>
<int value="164" label="Dictation only SODA DLC downloaded"/>
</enum>
<enum name="NotificationDatabaseStatus">