FaceGaze: Add pinned notification
This CL adds a pinned notification for FaceGaze, which can be used to quickly toggle the feature off. Screenshot: https://drive.google.com/file/d/1nUa-wyxyqDeNLshiYDA1cYoIvUTTYUqX/view?usp=sharing&resourcekey=0-35GmVeAWeR46vjPAI_tUWg Bug: 404622220 Change-Id: I16022c36ffcbc37c98d180ac4bd06f3d5f0f6b48 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6377204 Reviewed-by: Ahmed Mehfooz <amehfooz@chromium.org> Commit-Queue: Akihiro Ota <akihiroota@chromium.org> Cr-Commit-Position: refs/heads/main@{#1437514}
This commit is contained in:

committed by
Chromium LUCI CQ

parent
b5c5d454ce
commit
66bc8a6932
ash
chrome/browser/ash/accessibility
tools/metrics/histograms
@ -233,6 +233,8 @@ const FeatureDialogData kFeatureDialogs[] = {
|
||||
{FeatureType::kHighContrast,
|
||||
prefs::kHighContrastAcceleratorDialogHasBeenAccepted}};
|
||||
|
||||
constexpr char kFaceGazeActiveNotificationId[] =
|
||||
"facegaze_active_notification_id";
|
||||
constexpr char kNotificationId[] = "chrome://settings/accessibility";
|
||||
constexpr char kNotifierAccessibility[] = "ash.accessibility";
|
||||
constexpr char kDictationLanguageUpgradedNudgeId[] =
|
||||
@ -460,6 +462,8 @@ const gfx::VectorIcon& GetNotificationIcon(A11yNotificationType type) {
|
||||
case A11yNotificationType::kDicationOnlyPumpkinDownloaded:
|
||||
case A11yNotificationType::kDictationOnlySodaDownloaded:
|
||||
return kDictationMenuIcon;
|
||||
case A11yNotificationType::kFaceGazeActive:
|
||||
return kFacegazeIcon;
|
||||
default:
|
||||
return kNotificationChromevoxIcon;
|
||||
}
|
||||
@ -468,10 +472,11 @@ const gfx::VectorIcon& GetNotificationIcon(A11yNotificationType type) {
|
||||
void ShowAccessibilityNotification(
|
||||
const AccessibilityController::A11yNotificationWrapper& wrapper) {
|
||||
A11yNotificationType type = wrapper.type;
|
||||
std::string notification_id = wrapper.notification_id;
|
||||
const auto& replacements = wrapper.replacements;
|
||||
message_center::MessageCenter* message_center =
|
||||
message_center::MessageCenter::Get();
|
||||
message_center->RemoveNotification(kNotificationId, false /* by_user */);
|
||||
message_center->RemoveNotification(notification_id, false /* by_user */);
|
||||
|
||||
if (type == A11yNotificationType::kNone) {
|
||||
return;
|
||||
@ -546,6 +551,13 @@ void ShowAccessibilityNotification(
|
||||
pinned = false;
|
||||
// Use CRITICAL_WARNING to force the notification color to red.
|
||||
warning = message_center::SystemNotificationWarningLevel::CRITICAL_WARNING;
|
||||
} else if (type == A11yNotificationType::kFaceGazeActive) {
|
||||
title =
|
||||
l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_FACEGAZE_ACTIVE_TITLE);
|
||||
catalog_name = NotificationCatalogName::kFaceGazeActive;
|
||||
options.pinned = true;
|
||||
options.buttons.emplace_back(
|
||||
l10n_util::GetStringUTF16(IDS_ASH_FACEGAZE_CLOSE_BUTTON_TEXT));
|
||||
} else if (type == A11yNotificationType::kFaceGazeAssetsDownloaded) {
|
||||
title = l10n_util::GetStringUTF16(
|
||||
IDS_ASH_A11Y_FACEGAZE_ASSETS_DOWNLOADED_TITLE);
|
||||
@ -594,7 +606,7 @@ void ShowAccessibilityNotification(
|
||||
options.should_make_spoken_feedback_for_popup_updates = false;
|
||||
std::unique_ptr<message_center::Notification> notification =
|
||||
ash::CreateSystemNotificationPtr(
|
||||
message_center::NOTIFICATION_TYPE_SIMPLE, kNotificationId, title,
|
||||
message_center::NOTIFICATION_TYPE_SIMPLE, notification_id, title,
|
||||
text, display_source, GURL(),
|
||||
message_center::NotifierId(
|
||||
message_center::NotifierType::SYSTEM_COMPONENT,
|
||||
@ -607,7 +619,8 @@ void ShowAccessibilityNotification(
|
||||
void RemoveAccessibilityNotification() {
|
||||
ShowAccessibilityNotification(
|
||||
AccessibilityController::A11yNotificationWrapper(
|
||||
A11yNotificationType::kNone, std::vector<std::u16string>()));
|
||||
A11yNotificationType::kNone, kNotificationId,
|
||||
std::vector<std::u16string>()));
|
||||
}
|
||||
|
||||
AccessibilityPanelLayoutManager* GetLayoutManager() {
|
||||
@ -1918,8 +1931,8 @@ void AccessibilityController::SetSpokenFeedbackEnabled(
|
||||
if (enabled && actual_enabled && notify == A11Y_NOTIFICATION_SHOW) {
|
||||
type = A11yNotificationType::kSpokenFeedbackEnabled;
|
||||
}
|
||||
ShowAccessibilityNotification(
|
||||
A11yNotificationWrapper(type, std::vector<std::u16string>()));
|
||||
ShowAccessibilityNotification(A11yNotificationWrapper(
|
||||
type, kNotificationId, std::vector<std::u16string>()));
|
||||
}
|
||||
|
||||
bool AccessibilityController::IsSpokenFeedbackSettingVisibleInTray() {
|
||||
@ -1942,6 +1955,15 @@ void AccessibilityController::RequestSelectToSpeakStateChange() {
|
||||
client_->RequestSelectToSpeakStateChange();
|
||||
}
|
||||
|
||||
void AccessibilityController::OnFaceGazeActiveNotificationClicked(
|
||||
std::optional<int> button_index) {
|
||||
if (!button_index) {
|
||||
return;
|
||||
}
|
||||
|
||||
RequestDisableFaceGaze();
|
||||
}
|
||||
|
||||
void AccessibilityController::OnTouchpadNotificationClicked(
|
||||
std::optional<int> button_index) {
|
||||
if (!button_index) {
|
||||
@ -2443,8 +2465,8 @@ void AccessibilityController::BrailleDisplayStateChanged(bool connected) {
|
||||
}
|
||||
NotifyAccessibilityStatusChanged();
|
||||
|
||||
ShowAccessibilityNotification(
|
||||
A11yNotificationWrapper(type, std::vector<std::u16string>()));
|
||||
ShowAccessibilityNotification(A11yNotificationWrapper(
|
||||
type, kNotificationId, std::vector<std::u16string>()));
|
||||
}
|
||||
|
||||
void AccessibilityController::SetFocusHighlightRect(
|
||||
@ -2553,9 +2575,9 @@ void AccessibilityController::OnDisplayTabletStateChanged(
|
||||
// Show accessibility notification when tablet mode transition is completed.
|
||||
if (state == display::TabletState::kInTabletMode ||
|
||||
state == display::TabletState::kInClamshellMode) {
|
||||
ShowAccessibilityNotification(
|
||||
A11yNotificationWrapper(A11yNotificationType::kSpokenFeedbackEnabled,
|
||||
std::vector<std::u16string>()));
|
||||
ShowAccessibilityNotification(A11yNotificationWrapper(
|
||||
A11yNotificationType::kSpokenFeedbackEnabled, kNotificationId,
|
||||
std::vector<std::u16string>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3070,6 +3092,19 @@ void AccessibilityController::UpdateFaceGazeFromPrefs() {
|
||||
active_user_prefs_->SetBoolean(
|
||||
prefs::kAccessibilityFaceGazeActionsEnabledSentinel, actions_enabled);
|
||||
}
|
||||
|
||||
// Manage the pinned notification.
|
||||
if (enabled) {
|
||||
ShowAccessibilityNotification(A11yNotificationWrapper(
|
||||
A11yNotificationType::kFaceGazeActive, kFaceGazeActiveNotificationId,
|
||||
std::vector<std::u16string>(),
|
||||
base::BindRepeating(
|
||||
&AccessibilityController::OnFaceGazeActiveNotificationClicked,
|
||||
GetWeakPtr())));
|
||||
} else {
|
||||
message_center::MessageCenter::Get()->RemoveNotification(
|
||||
kFaceGazeActiveNotificationId, /*by_user=*/false);
|
||||
}
|
||||
}
|
||||
|
||||
void AccessibilityController::UpdateFlashNotificationsFromPrefs() {
|
||||
@ -3198,7 +3233,8 @@ void AccessibilityController::ShowDisableTouchpadDialog() {
|
||||
void AccessibilityController::OnDisableTouchpadDialogAccepted() {
|
||||
confirmation_dialog_.reset();
|
||||
ShowAccessibilityNotification(A11yNotificationWrapper(
|
||||
A11yNotificationType::kTouchpadDisabled, std::vector<std::u16string>(),
|
||||
A11yNotificationType::kTouchpadDisabled, kNotificationId,
|
||||
std::vector<std::u16string>(),
|
||||
base::BindRepeating(
|
||||
&AccessibilityController::OnTouchpadNotificationClicked,
|
||||
GetWeakPtr())));
|
||||
@ -3507,7 +3543,7 @@ void AccessibilityController::ActivateSwitchAccess() {
|
||||
|
||||
ShowAccessibilityNotification(
|
||||
A11yNotificationWrapper(A11yNotificationType::kSwitchAccessEnabled,
|
||||
std::vector<std::u16string>()));
|
||||
kNotificationId, std::vector<std::u16string>()));
|
||||
}
|
||||
|
||||
void AccessibilityController::DeactivateSwitchAccess() {
|
||||
@ -3745,8 +3781,9 @@ void AccessibilityController::ShowNotificationForDictation(
|
||||
break;
|
||||
}
|
||||
|
||||
ShowAccessibilityNotification(A11yNotificationWrapper(
|
||||
notification_type, std::vector<std::u16string>{display_language}));
|
||||
ShowAccessibilityNotification(
|
||||
A11yNotificationWrapper(notification_type, kNotificationId,
|
||||
std::vector<std::u16string>{display_language}));
|
||||
}
|
||||
|
||||
void AccessibilityController::ShowNotificationForFaceGaze(
|
||||
@ -3775,21 +3812,26 @@ void AccessibilityController::ShowNotificationForFaceGaze(
|
||||
|
||||
active_user_prefs_->SetBoolean(notification_shown_pref, true);
|
||||
ShowAccessibilityNotification(A11yNotificationWrapper(
|
||||
notification_type, std::vector<std::u16string>()));
|
||||
notification_type, kNotificationId, std::vector<std::u16string>()));
|
||||
}
|
||||
|
||||
AccessibilityController::A11yNotificationWrapper::A11yNotificationWrapper() =
|
||||
default;
|
||||
AccessibilityController::A11yNotificationWrapper::A11yNotificationWrapper(
|
||||
A11yNotificationType type_in,
|
||||
const std::string& notification_id_in,
|
||||
std::vector<std::u16string> replacements_in)
|
||||
: type(type_in), replacements(replacements_in) {}
|
||||
: type(type_in),
|
||||
notification_id(notification_id_in),
|
||||
replacements(replacements_in) {}
|
||||
AccessibilityController::A11yNotificationWrapper::A11yNotificationWrapper(
|
||||
A11yNotificationType type_in,
|
||||
const std::string& notification_id_in,
|
||||
std::vector<std::u16string> replacements_in,
|
||||
std::optional<base::RepeatingCallback<void(std::optional<int>)>>
|
||||
callback_in)
|
||||
: type(type_in),
|
||||
notification_id(notification_id_in),
|
||||
replacements(replacements_in),
|
||||
callback(std::move(callback_in)) {}
|
||||
AccessibilityController::A11yNotificationWrapper::~A11yNotificationWrapper() =
|
||||
|
@ -101,6 +101,8 @@ enum class A11yNotificationType {
|
||||
kDicationOnlyPumpkinDownloaded,
|
||||
// Shown when the SODA DLC (but no other DLCs) have downloaded.
|
||||
kDictationOnlySodaDownloaded,
|
||||
// Shown when FaceGaze is active.
|
||||
kFaceGazeActive,
|
||||
// Shown when the facegaze-assets DLC has successfully downloaded.
|
||||
kFaceGazeAssetsDownloaded,
|
||||
// Shown when the facegaze-assets DLC failed to download.
|
||||
@ -215,9 +217,11 @@ class ASH_EXPORT AccessibilityController
|
||||
struct A11yNotificationWrapper {
|
||||
A11yNotificationWrapper();
|
||||
A11yNotificationWrapper(A11yNotificationType type_in,
|
||||
const std::string& notification_id_in,
|
||||
std::vector<std::u16string> replacements_in);
|
||||
A11yNotificationWrapper(
|
||||
A11yNotificationType type_in,
|
||||
const std::string& notification_id_in,
|
||||
std::vector<std::u16string> replacements_in,
|
||||
std::optional<base::RepeatingCallback<void(std::optional<int>)>>
|
||||
callback_in);
|
||||
@ -225,6 +229,7 @@ class ASH_EXPORT AccessibilityController
|
||||
A11yNotificationWrapper(const A11yNotificationWrapper&);
|
||||
|
||||
A11yNotificationType type = A11yNotificationType::kNone;
|
||||
std::string notification_id;
|
||||
std::vector<std::u16string> replacements;
|
||||
std::optional<base::RepeatingCallback<void(std::optional<int>)>> callback;
|
||||
};
|
||||
@ -368,6 +373,7 @@ class ASH_EXPORT AccessibilityController
|
||||
|
||||
bool IsTouchpadDisabled();
|
||||
|
||||
void OnFaceGazeActiveNotificationClicked(std::optional<int> button_index);
|
||||
void OnTouchpadNotificationClicked(std::optional<int> button_index);
|
||||
|
||||
// Switch access may be disabled in prefs but still running when the disable
|
||||
|
@ -37,7 +37,9 @@
|
||||
#include "ash/test/ash_test_base.h"
|
||||
#include "base/functional/bind.h"
|
||||
#include "base/functional/callback_helpers.h"
|
||||
#include "base/run_loop.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/test/bind.h"
|
||||
#include "base/test/metrics/histogram_tester.h"
|
||||
#include "base/test/scoped_feature_list.h"
|
||||
#include "base/time/time.h"
|
||||
@ -1941,6 +1943,38 @@ TEST_F(AccessibilityControllerTest, FaceGazeNotifications) {
|
||||
ASSERT_EQ(1u, MessageCenter::Get()->GetVisibleNotifications().size());
|
||||
}
|
||||
|
||||
TEST_F(AccessibilityControllerTest, ShowNotificationOnFaceGaze) {
|
||||
// Enabling FaceGaze should show a pinned notification.
|
||||
controller()->face_gaze().SetEnabled(true);
|
||||
message_center::NotificationList::Notifications notifications =
|
||||
MessageCenter::Get()->GetVisibleNotifications();
|
||||
ASSERT_EQ(1u, notifications.size());
|
||||
EXPECT_EQ(u"Face control active", (*notifications.begin())->title());
|
||||
ASSERT_TRUE((*notifications.begin())->pinned());
|
||||
|
||||
// Disabling FaceGaze should clear the notification.
|
||||
controller()->face_gaze().SetEnabled(false);
|
||||
notifications = MessageCenter::Get()->GetVisibleNotifications();
|
||||
EXPECT_EQ(0u, notifications.size());
|
||||
}
|
||||
|
||||
TEST_F(AccessibilityControllerTest, ClickNotification) {
|
||||
// Enabling FaceGaze should show a notification.
|
||||
controller()->face_gaze().SetEnabled(true);
|
||||
message_center::NotificationList::Notifications notifications =
|
||||
MessageCenter::Get()->GetVisibleNotifications();
|
||||
ASSERT_EQ(1u, notifications.size());
|
||||
|
||||
// Clicking the notification will show a confirmation dialog.
|
||||
base::RunLoop dialog_waiter;
|
||||
controller()->AddFeatureDisableDialogCallbackForTesting(
|
||||
base::BindLambdaForTesting([&dialog_waiter]() { dialog_waiter.Quit(); }));
|
||||
(*notifications.begin())
|
||||
->delegate()
|
||||
->Click(/*button_index=*/1, /*reply=*/std::nullopt);
|
||||
dialog_waiter.Run();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
enum class TestUserLoginType {
|
||||
|
@ -1577,6 +1577,9 @@ You can also use the keyboard shortcut. First, highlight text, then press <ph na
|
||||
<message name="IDS_ASH_FACEGAZE_CLOSE_BUTTON_TEXT" desc="The text for the close button that is shown in the FaceGaze top bar UI.">
|
||||
Turn off Face control
|
||||
</message>
|
||||
<message name="IDS_ASH_STATUS_TRAY_FACEGAZE_ACTIVE_TITLE" desc="The title of the notification shown when FaceGaze is active.">
|
||||
Face control active
|
||||
</message>
|
||||
|
||||
<!-- Accessibility nudges -->
|
||||
<message name="IDS_ASH_ACCESSIBILITY_NUDGE_DICTATION_NO_FOCUSED_TEXT_FIELD" desc="Displayed in a nudge when Dictation is stopped automatically because there is no focused text field.">
|
||||
|
@ -0,0 +1 @@
|
||||
cf871ec1594ee4b7a41f5738f08e90b504b511fd
|
@ -213,7 +213,8 @@ enum class NotificationCatalogName {
|
||||
kChromeAppDeprecation = 194,
|
||||
kDownloadImageFromLobster = 195,
|
||||
kBocaSpotlightStarted = 196,
|
||||
kMaxValue = kBocaSpotlightStarted
|
||||
kFaceGazeActive = 197,
|
||||
kMaxValue = kFaceGazeActive
|
||||
};
|
||||
|
||||
// A living catalog that registers system nudges.
|
||||
|
@ -1610,6 +1610,9 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest,
|
||||
// is successfully downloaded.
|
||||
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, FaceGazeAssetsSucceeded) {
|
||||
AccessibilityManager::Get()->EnableFaceGaze(true);
|
||||
// Turning on FaceGaze will add a pinned notification to the message center,
|
||||
// so clear it for the purposes of this test.
|
||||
ClearMessageCenter();
|
||||
InstallFaceGazeAssetsAndWait();
|
||||
|
||||
message_center::NotificationList::Notifications notifications =
|
||||
@ -1625,6 +1628,9 @@ IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, FaceGazeAssetsSucceeded) {
|
||||
// fails to download.
|
||||
IN_PROC_BROWSER_TEST_F(AccessibilityManagerDlcTest, FaceGazeAssetsFailed) {
|
||||
AccessibilityManager::Get()->EnableFaceGaze(true);
|
||||
// Turning on FaceGaze will add a pinned notification to the message center,
|
||||
// so clear it for the purposes of this test.
|
||||
ClearMessageCenter();
|
||||
OnFaceGazeAssetsFailed();
|
||||
|
||||
message_center::NotificationList::Notifications notifications =
|
||||
|
@ -20850,6 +20850,7 @@ Called by update_net_error_codes.py.-->
|
||||
<int value="193" label="Scanner action executing"/>
|
||||
<int value="194" label="Chrome App deprecation"/>
|
||||
<int value="196" label="Spotlight session start countdown"/>
|
||||
<int value="197" label="Face Gaze Active"/>
|
||||
</enum>
|
||||
|
||||
<enum name="NQEObservationSource">
|
||||
|
Reference in New Issue
Block a user