From b59764323fdf4b4408045662b51b9c8a6ff3151d Mon Sep 17 00:00:00 2001 From: Amanda Lin Dietz <aldietz@google.com> Date: Wed, 18 Dec 2024 11:07:54 -0800 Subject: [PATCH] Reland "[FaceGaze] Add confirmation dialogs for feature/cursor/actions buttons" This is a reland of commit c41380632ca608590fb12955865d759c8ecb3dd9 The original commit was reverted under suspicion of introducing test failures on linux-chromeos-dbg, but the test failures persisted after revert and were definitively traced back to a different commit. Relanding this CL as-is. crbug.com/384012319 filed for the test failures. Bug: 371199351 Original change's description: > [FaceGaze] Add confirmation dialogs for feature/cursor/actions buttons > > * Show standard confirmation dialog when feature is toggled off. > * Show standard confirmation dialog when cursor control is toggled off. > * Show standard confirmation dialog when actions are toggled off. > * To enable user to use FaceGaze to interact with the dialog, > add a set of sentinel prefs to indicate when users have toggled > the button off. > * To enable JS to force feature off without showing a dialog, > add a sentinel pref for showing a confirmation dialog that is > set to false when FaceGaze is forcing the feature off. > * Update all JS to only interact with the set of sentinel prefs. > * Add tests. > * Leave cursor/actions strings as translateable=false for now. > > Design doc: > https://docs.google.com/document/d/1ULHXI_QK6ET1Inyrm_9DrIpiecVFc3Z7kyyv6cs0oJ0/edit?usp=sharing > > Screenshots: > https://screenshot.googleplex.com/3f6ir9GbJC7zd5q > https://screenshot.googleplex.com/ra4nbokD8K4Cpub > https://screenshot.googleplex.com/6fd2gzZvx8Gmg3W > > Change-Id: Iccb932657e9613cef670f2638e5dfa0794d0e3db > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6042086 > Reviewed-by: Akihiro Ota <akihiroota@chromium.org> > Commit-Queue: Amanda Lin Dietz <aldietz@google.com> > Reviewed-by: Xiaohui Chen <xiaohuic@chromium.org> > Reviewed-by: James Cook <jamescook@chromium.org> > Reviewed-by: Kyle Horimoto <khorimoto@chromium.org> > Cr-Commit-Position: refs/heads/main@{#1395618} Change-Id: I2a6ff4a9e251c86b66ffd36af778e06531423ce4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6101831 Reviewed-by: James Cook <jamescook@chromium.org> Auto-Submit: Amanda Lin Dietz <aldietz@google.com> Reviewed-by: Akihiro Ota <akihiroota@chromium.org> Reviewed-by: Kyle Horimoto <khorimoto@chromium.org> Commit-Queue: Amanda Lin Dietz <aldietz@google.com> Reviewed-by: Xiaohui Chen <xiaohuic@chromium.org> Cr-Commit-Position: refs/heads/main@{#1398090} --- ash/accessibility/accessibility_controller.cc | 149 ++++++++ ash/accessibility/accessibility_controller.h | 24 ++ .../accessibility_controller_unittest.cc | 8 + ash/ash_strings.grd | 11 + ...ACEGAZE_DISABLE_CONFIRMATION_TEXT.png.sha1 | 1 + ash/constants/ash_pref_names.h | 43 +++ .../ash/accessibility/facegaze_browsertest.cc | 340 +++++++++++++++++- .../api/settings_private/prefs_util.cc | 9 + .../prefs/pref_service_incognito_allowlist.cc | 4 + .../os_a11y_page/facegaze_actions_card.html | 2 +- .../os_a11y_page/facegaze_cursor_card.html | 2 +- .../os_a11y_page/facegaze_subpage.html | 2 +- .../settings/os_a11y_page/facegaze_subpage.ts | 4 +- .../facegaze/constants.ts | 6 + .../accessibility_common/facegaze/facegaze.ts | 9 +- .../facegaze/web_cam_face_landmarker.ts | 5 +- .../facegaze_actions_card_test.ts | 6 +- .../os_a11y_page/facegaze_cursor_card_test.ts | 4 +- .../os_a11y_page/facegaze_subpage_test.ts | 12 +- 19 files changed, 623 insertions(+), 18 deletions(-) create mode 100644 ash/ash_strings_grd/IDS_ASH_FACEGAZE_DISABLE_CONFIRMATION_TEXT.png.sha1 diff --git a/ash/accessibility/accessibility_controller.cc b/ash/accessibility/accessibility_controller.cc index 2cbbb49b5f02f..57010995270a7 100644 --- a/ash/accessibility/accessibility_controller.cc +++ b/ash/accessibility/accessibility_controller.cc @@ -1254,6 +1254,14 @@ void AccessibilityController::RegisterProfilePrefs( registry->RegisterBooleanPref( prefs::kAccessibilityTabletModeShelfNavigationButtonsEnabled, false); registry->RegisterBooleanPref(prefs::kAccessibilityFaceGazeEnabled, false); + registry->RegisterBooleanPref(prefs::kAccessibilityFaceGazeEnabledSentinel, + false); + registry->RegisterBooleanPref( + prefs::kAccessibilityFaceGazeEnabledSentinelShowDialog, true); + registry->RegisterBooleanPref( + prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel, true); + registry->RegisterBooleanPref( + prefs::kAccessibilityFaceGazeActionsEnabledSentinel, true); registry->RegisterBooleanPref(prefs::kAccessibilityDisableTrackpadEnabled, false); registry->RegisterIntegerPref(prefs::kAccessibilityDisableTrackpadMode, @@ -2574,6 +2582,8 @@ void AccessibilityController::ObservePrefs(PrefService* prefs) { // Features will be initialized from current prefs later. } + // TODO(crbug.com/383754550): Consider updating calls from + // base::Unretained(this) to GetWeakPtr(). pref_change_registrar_->Add( prefs::kAccessibilityAutoclickDelayMs, base::BindRepeating( @@ -2766,6 +2776,25 @@ void AccessibilityController::ObservePrefs(PrefService* prefs) { if (::features::IsAccessibilityFaceGazeEnabled()) { UpdateFaceGazeFromPrefs(); + pref_change_registrar_->Add( + prefs::kAccessibilityFaceGazeEnabledSentinel, + base::BindRepeating(&AccessibilityController::OnFaceGazeSentinelChanged, + base::Unretained(this), + prefs::kAccessibilityFaceGazeEnabledSentinel, + prefs::kAccessibilityFaceGazeEnabled)); + pref_change_registrar_->Add( + prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel, + base::BindRepeating( + &AccessibilityController::OnFaceGazeSentinelChanged, + base::Unretained(this), + prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel, + prefs::kAccessibilityFaceGazeCursorControlEnabled)); + pref_change_registrar_->Add( + prefs::kAccessibilityFaceGazeActionsEnabledSentinel, + base::BindRepeating(&AccessibilityController::OnFaceGazeSentinelChanged, + base::Unretained(this), + prefs::kAccessibilityFaceGazeActionsEnabledSentinel, + prefs::kAccessibilityFaceGazeActionsEnabled)); } if (::features::IsAccessibilityFlashScreenFeatureEnabled()) { UpdateFlashNotificationsFromPrefs(); @@ -3004,6 +3033,43 @@ void AccessibilityController::UpdateFaceGazeFromPrefs() { if (!::features::IsAccessibilityFaceGazeEnabled()) { return; } + + const bool enabled = + active_user_prefs_->GetBoolean(prefs::kAccessibilityFaceGazeEnabled); + const bool sentinel_enabled = active_user_prefs_->GetBoolean( + prefs::kAccessibilityFaceGazeEnabledSentinel); + // Sentinel and behavior pref are not in sync. + if (enabled != sentinel_enabled) { + if (enabled) { + active_user_prefs_->SetBoolean( + prefs::kAccessibilityFaceGazeEnabledSentinel, true); + } else { + // Set sentinel pref to false without showing the dialog. + active_user_prefs_->SetBoolean( + prefs::kAccessibilityFaceGazeEnabledSentinelShowDialog, false); + active_user_prefs_->SetBoolean( + prefs::kAccessibilityFaceGazeEnabledSentinel, false); + } + } + + const bool cursor_control_enabled = active_user_prefs_->GetBoolean( + prefs::kAccessibilityFaceGazeCursorControlEnabled); + const bool cursor_control_sentinel_enabled = active_user_prefs_->GetBoolean( + prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel); + if (cursor_control_enabled != cursor_control_sentinel_enabled) { + active_user_prefs_->SetBoolean( + prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel, + cursor_control_enabled); + } + + const bool actions_enabled = active_user_prefs_->GetBoolean( + prefs::kAccessibilityFaceGazeActionsEnabled); + const bool actions_sentinel_enabled = active_user_prefs_->GetBoolean( + prefs::kAccessibilityFaceGazeActionsEnabledSentinel); + if (actions_enabled != actions_sentinel_enabled) { + active_user_prefs_->SetBoolean( + prefs::kAccessibilityFaceGazeActionsEnabledSentinel, actions_enabled); + } } void AccessibilityController::UpdateFlashNotificationsFromPrefs() { @@ -3626,6 +3692,28 @@ gfx::Rect AccessibilityController::GetConfirmationDialogBoundsInScreen() { return confirmation_dialog_.get()->GetWidget()->GetWindowBoundsInScreen(); } +void AccessibilityController::ShowFeatureDisableDialog( + int window_title_text_id, + base::OnceClosure on_accept_callback, + base::OnceClosure on_cancel_callback) { + if (disable_dialog_) { + // If a dialog is already being shown we do not show a new one. + // Instead, run the on_close_callback on the new dialog to indicate + // it was closed without the user taking any action. + // This is consistent with AcceleratorController. + std::move(on_cancel_callback).Run(); + return; + } + + auto* dialog = new AccessibilityFeatureDisableDialog( + window_title_text_id, std::move(on_accept_callback), + std::move(on_cancel_callback)); + disable_dialog_ = dialog->GetWeakPtr(); + if (show_disable_dialog_callback_for_testing_) { + show_disable_dialog_callback_for_testing_.Run(); + } +} + void AccessibilityController::PreviewFlashNotification() const { flash_screen_controller_->PreviewFlash(); } @@ -3938,6 +4026,11 @@ void AccessibilityController::AddShowConfirmationDialogCallbackForTesting( show_confirmation_dialog_callback_for_testing_ = std::move(callback); } +void AccessibilityController::AddFeatureDisableDialogCallbackForTesting( + base::RepeatingCallback<void()> callback) { + show_disable_dialog_callback_for_testing_ = std::move(callback); +} + bool AccessibilityController::VerifyFeaturesDataForTesting() { return VerifyFeaturesData(); } @@ -3981,6 +4074,62 @@ void AccessibilityController::ScrollAtPoint( std::ignore = host->GetEventSink()->OnEventFromSource(&wheel); } +void AccessibilityController::OnFaceGazeSentinelChanged( + const std::string& sentinel_pref, + const std::string& behavior_pref) { + DCHECK(active_user_prefs_); + if (active_user_prefs_->GetBoolean(sentinel_pref)) { + active_user_prefs_->SetBoolean(behavior_pref, true); + return; + } + + // Set to FaceGaze disable confirmation text by default to ensure a valid + // value. + int window_title_text_id = IDS_ASH_FACEGAZE_DISABLE_CONFIRMATION_TEXT; + if (sentinel_pref == prefs::kAccessibilityFaceGazeEnabledSentinel) { + const bool show_dialog = active_user_prefs_->GetBoolean( + prefs::kAccessibilityFaceGazeEnabledSentinelShowDialog); + if (!show_dialog) { + face_gaze().SetEnabled(false); + // Reset show dialog pref to default after pref is handled. + active_user_prefs_->SetBoolean( + prefs::kAccessibilityFaceGazeEnabledSentinelShowDialog, true); + return; + } + } else if (sentinel_pref == + prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel) { + window_title_text_id = + IDS_ASH_FACEGAZE_CURSOR_CONTROL_DISABLE_CONFIRMATION_TEXT; + } else if (sentinel_pref == + prefs::kAccessibilityFaceGazeActionsEnabledSentinel) { + window_title_text_id = IDS_ASH_FACEGAZE_ACTIONS_DISABLE_CONFIRMATION_TEXT; + } + + ShowFeatureDisableDialog( + window_title_text_id, + BindOnce(&AccessibilityController::OnFaceGazeDisableDialogClosed, + GetWeakPtr(), sentinel_pref, behavior_pref, + /*dialog_accepted=*/true), + BindOnce(&AccessibilityController::OnFaceGazeDisableDialogClosed, + GetWeakPtr(), sentinel_pref, behavior_pref, + /*dialog_accepted=*/false)); +} + +void AccessibilityController::OnFaceGazeDisableDialogClosed( + const std::string& sentinel_pref, + const std::string& behavior_pref, + bool dialog_accepted) { + if (dialog_accepted) { + // After confirmation, set behavior pref to false to turn off the feature. + active_user_prefs_->SetBoolean(behavior_pref, false); + } else { + // Ensure sentinel pref is in sync with behavior pref if dialog is + // cancelled. + active_user_prefs_->SetBoolean(sentinel_pref, true); + } + disable_dialog_.reset(); +} + void AccessibilityController::UpdateFaceGazeBubble(const std::u16string& text, bool is_warning) { if (!facegaze_bubble_controller_ || diff --git a/ash/accessibility/accessibility_controller.h b/ash/accessibility/accessibility_controller.h index 505d6a795e670..10792e2d7f6be 100644 --- a/ash/accessibility/accessibility_controller.h +++ b/ash/accessibility/accessibility_controller.h @@ -55,6 +55,7 @@ namespace ash { class AccessibilityConfirmationDialog; class AccessibilityControllerClient; class AccessibilityEventRewriter; +class AccessibilityFeatureDisableDialog; class AccessibilityHighlightController; class AccessibilityObserver; enum class AccessibilityPanelState; @@ -644,6 +645,12 @@ class ASH_EXPORT AccessibilityController std::optional<int> timeout_seconds = std::nullopt); gfx::Rect GetConfirmationDialogBoundsInScreen(); + // Shows a dialog to disable a feature with the given text, and calls the + // relevant callback when the dialog is accepted or cancelled. + void ShowFeatureDisableDialog(int window_title_text_id, + base::OnceClosure on_accept_callback, + base::OnceClosure on_cancel_callback); + void PreviewFlashNotification() const; // SessionObserver: @@ -679,6 +686,9 @@ class ASH_EXPORT AccessibilityController AccessibilityConfirmationDialog* GetConfirmationDialogForTest() { return confirmation_dialog_.get(); } + AccessibilityFeatureDisableDialog* GetFeatureDisableDialogForTest() { + return disable_dialog_.get(); + } bool enable_chromevox_volume_slide_gesture() { return enable_chromevox_volume_slide_gesture_; @@ -718,6 +728,9 @@ class ASH_EXPORT AccessibilityController void AddShowConfirmationDialogCallbackForTesting( base::RepeatingCallback<void()> callback); + void AddFeatureDisableDialogCallbackForTesting( + base::RepeatingCallback<void()> callback); + bool VerifyFeaturesDataForTesting(); SelectToSpeakEventHandler* GetSelectToSpeakEventHandlerForTesting() const { @@ -813,6 +826,12 @@ class ASH_EXPORT AccessibilityController void OnDisableTouchpadDialogDismissed(); void ExternalDeviceConnected(); + void OnFaceGazeSentinelChanged(const std::string& sentinel_pref, + const std::string& behavior_pref); + void OnFaceGazeDisableDialogClosed(const std::string& sentinel_pref, + const std::string& behavior_pref, + bool dialog_accepted); + void RecordSelectToSpeakSpeechDuration(SelectToSpeakState old_state, SelectToSpeakState new_state); @@ -912,6 +931,11 @@ class ASH_EXPORT AccessibilityController base::RepeatingCallback<void()> show_confirmation_dialog_callback_for_testing_; + // The current AccessibilityFeatureDisableDialog, if one exists. + base::WeakPtr<AccessibilityFeatureDisableDialog> disable_dialog_; + + base::RepeatingCallback<void()> show_disable_dialog_callback_for_testing_; + base::Time select_to_speak_speech_start_time_; base::ScopedObservation<InputDeviceSettingsController, diff --git a/ash/accessibility/accessibility_controller_unittest.cc b/ash/accessibility/accessibility_controller_unittest.cc index 7e73e8e5042ef..394880ae0a127 100644 --- a/ash/accessibility/accessibility_controller_unittest.cc +++ b/ash/accessibility/accessibility_controller_unittest.cc @@ -342,6 +342,14 @@ TEST_F(AccessibilityControllerTest, PrefsAreRegistered) { prefs()->FindPreference(prefs::kAccessibilityFaceGazePrecisionClick)); EXPECT_TRUE(prefs()->FindPreference( prefs::kAccessibilityFaceGazePrecisionClickSpeedFactor)); + EXPECT_TRUE( + prefs()->FindPreference(prefs::kAccessibilityFaceGazeEnabledSentinel)); + EXPECT_TRUE(prefs()->FindPreference( + prefs::kAccessibilityFaceGazeEnabledSentinelShowDialog)); + EXPECT_TRUE(prefs()->FindPreference( + prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel)); + EXPECT_TRUE(prefs()->FindPreference( + prefs::kAccessibilityFaceGazeActionsEnabledSentinel)); EXPECT_TRUE(prefs()->FindPreference(prefs::kAccessibilityCaretBlinkInterval)); EXPECT_TRUE( prefs()->FindPreference(prefs::kAccessibilityFlashNotificationsEnabled)); diff --git a/ash/ash_strings.grd b/ash/ash_strings.grd index ce68e337fccea..54d01626c61b6 100644 --- a/ash/ash_strings.grd +++ b/ash/ash_strings.grd @@ -1512,6 +1512,17 @@ Style notes: You can also use the keyboard shortcut. First, highlight text, then press <ph name="modifier">$1<ex>launcher</ex></ph> + s. </message> + <!-- FaceGaze dialog strings --> + <message name="IDS_ASH_FACEGAZE_DISABLE_CONFIRMATION_TEXT" desc="The text for the modal dialog shown when the user disables FaceGaze, to confirm they meant to disable the feature."> + Are you sure you want to turn off face control? + </message> + <message name="IDS_ASH_FACEGAZE_CURSOR_CONTROL_DISABLE_CONFIRMATION_TEXT" desc="The text for the modal dialog shown when the user disables cursor control in FaceGaze, to confirm they meant to disable the feature." translateable="false"> + Are you sure you want to turn off mouse cursor control? + </message> + <message name="IDS_ASH_FACEGAZE_ACTIONS_DISABLE_CONFIRMATION_TEXT" desc="The text for the modal dialog shown when the user disables performing actions using facial gestures in FaceGaze, to confirm they meant to disable the feature." translateable="false"> + Are you sure you want to turn off facial gestures? + </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."> Go to a text field to use Dictation diff --git a/ash/ash_strings_grd/IDS_ASH_FACEGAZE_DISABLE_CONFIRMATION_TEXT.png.sha1 b/ash/ash_strings_grd/IDS_ASH_FACEGAZE_DISABLE_CONFIRMATION_TEXT.png.sha1 new file mode 100644 index 0000000000000..d114f7b71b74e --- /dev/null +++ b/ash/ash_strings_grd/IDS_ASH_FACEGAZE_DISABLE_CONFIRMATION_TEXT.png.sha1 @@ -0,0 +1 @@ +2ef8fbb30486b5eb3b1bee216347beaa3de2040c \ No newline at end of file diff --git a/ash/constants/ash_pref_names.h b/ash/constants/ash_pref_names.h index a53459a18384b..01642d62b8e65 100644 --- a/ash/constants/ash_pref_names.h +++ b/ash/constants/ash_pref_names.h @@ -917,6 +917,49 @@ inline constexpr char kAccessibilityFaceGazePrecisionClick[] = // should be dampened by during a precision click. inline constexpr char kAccessibilityFaceGazePrecisionClickSpeedFactor[] = "settings.a11y.face_gaze.precision_click_speed_factor"; +// A boolean pref which indicates when a request has been made to change the +// FaceGaze enabled state. This pref acts a sentinel for the requested state. +// The feature uses this pref to determine whether FaceGaze should be 1) enabled +// 2) disabled, or 3) a dialog needs to be shown to confirm whether the user +// wants to disable the feature. Using a separate sentinel pref to store the +// requested state here ensures the kAccessibilityFaceGazeEnabled pref always +// accurately reflects the feature state, specifically in the case where the +// sentinel pref is set to false and the behavior pref must remain true until +// the confirmation dialog is accepted or cancelled. This is to ensure the user +// can interact with the dialog with FaceGaze as expected. In all other +// scenarios, the behavior pref and sentinel pref are kept in sync. +inline constexpr char kAccessibilityFaceGazeEnabledSentinel[] = + "settings.a11y.face_gaze.enabled_sentinel"; +// A boolean pref which indicates whether the confirmation dialog should be +// shown when kAccessibilityFaceGazeEnabledSentinel is set to false. +inline constexpr char kAccessibilityFaceGazeEnabledSentinelShowDialog[] = + "settings.a11y.face_gaze.enabled_sentinel_show_dialog"; +// A boolean pref which indicates the requested enabled state for FaceGaze +// cursor control. This pref acts as a sentinel for the requested cursor control +// state. The feature uses this pref to determine whether cursor control should +// be 1) enabled or 2) a dialog needs to be shown to confirm whether the user +// wants to disable the feature. Using a separate sentinel pref to store the +// requested state here ensures the kAccessibilityFaceGazeCursorControlEnabled +// pref always accurately reflects the feature state, specifically in the case +// where the sentinel pref is set to false and the behavior pref must remain +// true until the confirmation dialog is accepted or cancelled. This is to +// ensure the user can interact with the dialog with FaceGaze as expected. In +// all other scenarios, the behavior pref and sentinel pref are kept in sync. +inline constexpr char kAccessibilityFaceGazeCursorControlEnabledSentinel[] = + "settings.a11y.face_gaze.cursor_control_enabled_sentinel"; +// A boolean pref which indicates the requested enabled state for FaceGaze +// actions. This pref acts as a sentinel for the requested actions +// state. The feature uses this pref to determine whether actions should +// be 1) enabled or 2) a dialog needs to be shown to confirm whether the user +// wants to disable the feature. Using a separate sentinel pref to store the +// requested state here ensures the kAccessibilityFaceGazeActionsEnabled pref +// always accurately reflects the feature state, specifically in the case where +// the sentinel pref is set to false and the behavior pref must remain true +// until the confirmation dialog is accepted or cancelled. This is to ensure the +// user can interact with the dialog with FaceGaze as expected. In all other +// scenarios, the behavior pref and sentinel pref are kept in sync. +inline constexpr char kAccessibilityFaceGazeActionsEnabledSentinel[] = + "settings.a11y.face_gaze.actions_enabled_sentinel"; // A boolean pref which determines whether the accessibility menu shows // regardless of the state of a11y features. diff --git a/chrome/browser/ash/accessibility/facegaze_browsertest.cc b/chrome/browser/ash/accessibility/facegaze_browsertest.cc index 31371d359b70b..57355e68771d4 100644 --- a/chrome/browser/ash/accessibility/facegaze_browsertest.cc +++ b/chrome/browser/ash/accessibility/facegaze_browsertest.cc @@ -9,6 +9,7 @@ #include "ash/accessibility/ui/accessibility_confirmation_dialog.h" #include "ash/constants/ash_pref_names.h" #include "ash/shell.h" +#include "ash/system/accessibility/accessibility_feature_disable_dialog.h" #include "base/run_loop.h" #include "base/test/bind.h" #include "base/test/scoped_feature_list.h" @@ -683,7 +684,7 @@ IN_PROC_BROWSER_TEST_F(FaceGazeIntegrationTest, DefaultBehavior) { GetPrefs()->GetDict(prefs::kAccessibilityFaceGazeGesturesToConfidence); ASSERT_EQ(gestures_to_macros.size(), 2u); ASSERT_EQ(gestures_to_confidences.size(), 2u); - ASSERT_EQ(/* MOUTH_SMILE */ 35, + ASSERT_EQ(/* MOUSE_CLICK_LEFT */ 35, gestures_to_macros.FindInt( FaceGazeTestUtils::ToString(FaceGazeGesture::MOUTH_SMILE))); ASSERT_EQ(/* SCROLL */ 50, @@ -695,4 +696,341 @@ IN_PROC_BROWSER_TEST_F(FaceGazeIntegrationTest, DefaultBehavior) { FaceGazeTestUtils::ToString(FaceGazeGesture::JAW_OPEN))); } +IN_PROC_BROWSER_TEST_F(FaceGazeIntegrationTest, EnableNoDialog) { + auto* controller = ash::Shell::Get()->accessibility_controller(); + auto* prefs = GetPrefs(); + + base::RunLoop dialog_waiter; + controller->AddFeatureDisableDialogCallbackForTesting( + base::BindLambdaForTesting([&dialog_waiter]() { dialog_waiter.Quit(); })); + + // Setting sentinel value to true should enable the feature without showing + // the feature disable dialog. + prefs->SetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel, true); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_EQ(nullptr, controller->GetFeatureDisableDialogForTest()); +} + +IN_PROC_BROWSER_TEST_F(FaceGazeIntegrationTest, DisableDialogAccept) { + auto* controller = ash::Shell::Get()->accessibility_controller(); + auto* prefs = GetPrefs(); + + base::RunLoop dialog_waiter; + controller->AddFeatureDisableDialogCallbackForTesting( + base::BindLambdaForTesting([&dialog_waiter]() { dialog_waiter.Quit(); })); + + // Enabling FaceGaze should not show the feature disable dialog. + utils()->EnableFaceGaze(Config().Default()); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel)); + ASSERT_EQ(nullptr, controller->GetFeatureDisableDialogForTest()); + + // Setting sentinel value to false should show the feature disable dialog and + // leave the behavior pref unchanged. + prefs->SetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel, false); + dialog_waiter.Run(); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_FALSE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel)); + ASSERT_NE(nullptr, controller->GetFeatureDisableDialogForTest()); + + base::RunLoop pref_waiter; + PrefChangeRegistrar change_observer; + change_observer.Init(prefs); + change_observer.Add(prefs::kAccessibilityFaceGazeEnabled, + pref_waiter.QuitClosure()); + + // Accepting the dialog should turn off FaceGaze. + controller->GetFeatureDisableDialogForTest()->Accept(); + pref_waiter.Run(); + + // Assert behavior and sentinel prefs are in sync. + ASSERT_FALSE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_FALSE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel)); +} + +IN_PROC_BROWSER_TEST_F(FaceGazeIntegrationTest, DisableDialogNoShow) { + auto* controller = ash::Shell::Get()->accessibility_controller(); + auto* prefs = GetPrefs(); + + // Enabling FaceGaze should not show the feature disable dialog. + utils()->EnableFaceGaze(Config().Default()); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel)); + ASSERT_EQ(nullptr, controller->GetFeatureDisableDialogForTest()); + + base::RunLoop pref_waiter; + PrefChangeRegistrar change_observer; + change_observer.Init(prefs); + change_observer.Add(prefs::kAccessibilityFaceGazeEnabled, + pref_waiter.QuitClosure()); + + // Setting show dialog value to false should allow the feature to be set to + // false when the sentinel is set to false. + prefs->SetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinelShowDialog, + false); + prefs->SetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel, false); + ASSERT_EQ(nullptr, controller->GetFeatureDisableDialogForTest()); + + pref_waiter.Run(); + + // Assert behavior and sentinel prefs are in sync. + ASSERT_FALSE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_FALSE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel)); +} + +IN_PROC_BROWSER_TEST_F(FaceGazeIntegrationTest, DisableDialogCancel) { + auto* controller = ash::Shell::Get()->accessibility_controller(); + auto* prefs = GetPrefs(); + + base::RunLoop dialog_waiter; + controller->AddFeatureDisableDialogCallbackForTesting( + base::BindLambdaForTesting([&dialog_waiter]() { dialog_waiter.Quit(); })); + + // Enabling FaceGaze should not show the feature disable dialog. + utils()->EnableFaceGaze(Config().Default()); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel)); + ASSERT_EQ(nullptr, controller->GetFeatureDisableDialogForTest()); + + // Setting sentinel value to false should show the feature disable dialog and + // leave the behavior pref unchanged. + prefs->SetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel, false); + dialog_waiter.Run(); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_NE(nullptr, controller->GetFeatureDisableDialogForTest()); + + base::RunLoop pref_waiter; + PrefChangeRegistrar change_observer; + change_observer.Init(prefs); + change_observer.Add(prefs::kAccessibilityFaceGazeEnabledSentinel, + pref_waiter.QuitClosure()); + + // Cancelling the dialog should leave FaceGaze on and set the sentinel to + // true. + controller->GetFeatureDisableDialogForTest()->Cancel(); + pref_waiter.Run(); + + // Assert behavior and sentinel prefs are in sync. + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel)); +} + +// TODO(crbug.com/383757982): Add test API for .WithCursorControlEnabled() and +// .WithActionsEnabled() and update tests accordingly. +IN_PROC_BROWSER_TEST_F(FaceGazeIntegrationTest, EnableCursorControlNoDialog) { + auto* controller = ash::Shell::Get()->accessibility_controller(); + auto* prefs = GetPrefs(); + + base::RunLoop dialog_waiter; + controller->AddFeatureDisableDialogCallbackForTesting( + base::BindLambdaForTesting([&dialog_waiter]() { dialog_waiter.Quit(); })); + + // Enabling FaceGaze should not show the feature disable dialog. + utils()->EnableFaceGaze(Config().Default()); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel)); + ASSERT_EQ(nullptr, controller->GetFeatureDisableDialogForTest()); + + // Setting sentinel value to true should not show the feature disable dialog. + prefs->SetBoolean(prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel, + true); + ASSERT_TRUE( + prefs->GetBoolean(prefs::kAccessibilityFaceGazeCursorControlEnabled)); + ASSERT_EQ(nullptr, controller->GetFeatureDisableDialogForTest()); +} + +IN_PROC_BROWSER_TEST_F(FaceGazeIntegrationTest, + DisableCursorControlDialogAccept) { + auto* controller = ash::Shell::Get()->accessibility_controller(); + auto* prefs = GetPrefs(); + + base::RunLoop dialog_waiter; + controller->AddFeatureDisableDialogCallbackForTesting( + base::BindLambdaForTesting([&dialog_waiter]() { dialog_waiter.Quit(); })); + + // Enabling FaceGaze should not show the feature disable dialog. + utils()->EnableFaceGaze(Config().Default()); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel)); + ASSERT_EQ(nullptr, controller->GetFeatureDisableDialogForTest()); + prefs->SetBoolean(prefs::kAccessibilityFaceGazeCursorControlEnabled, true); + ASSERT_TRUE(prefs->GetBoolean( + prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel)); + + // Setting sentinel value to false should show the feature disable dialog and + // leave the behavior pref unchanged. + prefs->SetBoolean(prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel, + false); + dialog_waiter.Run(); + ASSERT_TRUE( + prefs->GetBoolean(prefs::kAccessibilityFaceGazeCursorControlEnabled)); + ASSERT_FALSE(prefs->GetBoolean( + prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel)); + ASSERT_NE(nullptr, controller->GetFeatureDisableDialogForTest()); + + base::RunLoop pref_waiter; + PrefChangeRegistrar change_observer; + change_observer.Init(prefs); + change_observer.Add(prefs::kAccessibilityFaceGazeCursorControlEnabled, + pref_waiter.QuitClosure()); + + // Accepting the dialog should turn off FaceGaze cursor control. + controller->GetFeatureDisableDialogForTest()->Accept(); + pref_waiter.Run(); + + // Assert behavior and sentinel prefs are in sync. + ASSERT_FALSE( + prefs->GetBoolean(prefs::kAccessibilityFaceGazeCursorControlEnabled)); + ASSERT_FALSE(prefs->GetBoolean( + prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel)); +} + +IN_PROC_BROWSER_TEST_F(FaceGazeIntegrationTest, + DisableCursorControlDialogCancel) { + auto* controller = ash::Shell::Get()->accessibility_controller(); + auto* prefs = GetPrefs(); + + base::RunLoop dialog_waiter; + controller->AddFeatureDisableDialogCallbackForTesting( + base::BindLambdaForTesting([&dialog_waiter]() { dialog_waiter.Quit(); })); + + // Enabling FaceGaze should not show the feature disable dialog. + utils()->EnableFaceGaze(Config().Default()); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel)); + ASSERT_EQ(nullptr, controller->GetFeatureDisableDialogForTest()); + prefs->SetBoolean(prefs::kAccessibilityFaceGazeCursorControlEnabled, true); + ASSERT_TRUE(prefs->GetBoolean( + prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel)); + + // Setting sentinel value to false should show the feature disable dialog and + // leave the behavior pref unchanged. + prefs->SetBoolean(prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel, + false); + dialog_waiter.Run(); + ASSERT_TRUE( + prefs->GetBoolean(prefs::kAccessibilityFaceGazeCursorControlEnabled)); + ASSERT_NE(nullptr, controller->GetFeatureDisableDialogForTest()); + + base::RunLoop pref_waiter; + PrefChangeRegistrar change_observer; + change_observer.Init(prefs); + change_observer.Add(prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel, + pref_waiter.QuitClosure()); + + // Cancelling the dialog should leave FaceGaze cursor control on and set the + // sentinel to true. + controller->GetFeatureDisableDialogForTest()->Cancel(); + pref_waiter.Run(); + + // Assert behavior and sentinel prefs are in sync. + ASSERT_TRUE( + prefs->GetBoolean(prefs::kAccessibilityFaceGazeCursorControlEnabled)); + ASSERT_TRUE(prefs->GetBoolean( + prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel)); +} + +IN_PROC_BROWSER_TEST_F(FaceGazeIntegrationTest, EnableActionsNoDialog) { + auto* controller = ash::Shell::Get()->accessibility_controller(); + auto* prefs = GetPrefs(); + + base::RunLoop dialog_waiter; + controller->AddFeatureDisableDialogCallbackForTesting( + base::BindLambdaForTesting([&dialog_waiter]() { dialog_waiter.Quit(); })); + + // Enabling FaceGaze should not show the feature disable dialog. + utils()->EnableFaceGaze(Config().Default()); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel)); + ASSERT_EQ(nullptr, controller->GetFeatureDisableDialogForTest()); + + // Setting sentinel value to true should not show the feature disable dialog. + prefs->SetBoolean(prefs::kAccessibilityFaceGazeActionsEnabledSentinel, true); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeActionsEnabled)); + ASSERT_EQ(nullptr, controller->GetFeatureDisableDialogForTest()); +} + +IN_PROC_BROWSER_TEST_F(FaceGazeIntegrationTest, DisableActionsDialogAccept) { + auto* controller = ash::Shell::Get()->accessibility_controller(); + auto* prefs = GetPrefs(); + + base::RunLoop dialog_waiter; + controller->AddFeatureDisableDialogCallbackForTesting( + base::BindLambdaForTesting([&dialog_waiter]() { dialog_waiter.Quit(); })); + + // Setting sentinel value to true should not show the feature disable dialog. + utils()->EnableFaceGaze(Config().Default()); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel)); + ASSERT_EQ(nullptr, controller->GetFeatureDisableDialogForTest()); + prefs->SetBoolean(prefs::kAccessibilityFaceGazeActionsEnabled, true); + ASSERT_TRUE( + prefs->GetBoolean(prefs::kAccessibilityFaceGazeActionsEnabledSentinel)); + + // Setting sentinel value to false should show the feature disable dialog and + // leave the behavior pref unchanged. + prefs->SetBoolean(prefs::kAccessibilityFaceGazeActionsEnabledSentinel, false); + dialog_waiter.Run(); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeActionsEnabled)); + ASSERT_FALSE( + prefs->GetBoolean(prefs::kAccessibilityFaceGazeActionsEnabledSentinel)); + ASSERT_NE(nullptr, controller->GetFeatureDisableDialogForTest()); + + base::RunLoop pref_waiter; + PrefChangeRegistrar change_observer; + change_observer.Init(prefs); + change_observer.Add(prefs::kAccessibilityFaceGazeActionsEnabled, + pref_waiter.QuitClosure()); + + // Accepting the dialog should turn off FaceGaze actions. + controller->GetFeatureDisableDialogForTest()->Accept(); + pref_waiter.Run(); + + // Assert behavior and sentinel prefs are in sync. + ASSERT_FALSE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeActionsEnabled)); + ASSERT_FALSE( + prefs->GetBoolean(prefs::kAccessibilityFaceGazeActionsEnabledSentinel)); +} + +IN_PROC_BROWSER_TEST_F(FaceGazeIntegrationTest, DisableActionsDialogCancel) { + auto* controller = ash::Shell::Get()->accessibility_controller(); + auto* prefs = GetPrefs(); + + base::RunLoop dialog_waiter; + controller->AddFeatureDisableDialogCallbackForTesting( + base::BindLambdaForTesting([&dialog_waiter]() { dialog_waiter.Quit(); })); + + // Setting sentinel value to true should not show the feature disable dialog. + utils()->EnableFaceGaze(Config().Default()); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabled)); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeEnabledSentinel)); + ASSERT_EQ(nullptr, controller->GetFeatureDisableDialogForTest()); + prefs->SetBoolean(prefs::kAccessibilityFaceGazeActionsEnabled, true); + ASSERT_TRUE( + prefs->GetBoolean(prefs::kAccessibilityFaceGazeActionsEnabledSentinel)); + + // Setting sentinel value to false should show the feature disable dialog and + // leave the behavior pref unchanged. + prefs->SetBoolean(prefs::kAccessibilityFaceGazeActionsEnabledSentinel, false); + dialog_waiter.Run(); + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeActionsEnabled)); + ASSERT_NE(nullptr, controller->GetFeatureDisableDialogForTest()); + + base::RunLoop pref_waiter; + PrefChangeRegistrar change_observer; + change_observer.Init(prefs); + change_observer.Add(prefs::kAccessibilityFaceGazeActionsEnabledSentinel, + pref_waiter.QuitClosure()); + + // Cancelling the dialog should leave FaceGaze actions on and set the sentinel + // to true. + controller->GetFeatureDisableDialogForTest()->Cancel(); + pref_waiter.Run(); + + // Assert behavior and sentinel prefs are in sync. + ASSERT_TRUE(prefs->GetBoolean(prefs::kAccessibilityFaceGazeActionsEnabled)); + ASSERT_TRUE( + prefs->GetBoolean(prefs::kAccessibilityFaceGazeActionsEnabledSentinel)); +} + } // namespace ash diff --git a/chrome/browser/extensions/api/settings_private/prefs_util.cc b/chrome/browser/extensions/api/settings_private/prefs_util.cc index 0a0679570a686..ef05604425bfd 100644 --- a/chrome/browser/extensions/api/settings_private/prefs_util.cc +++ b/chrome/browser/extensions/api/settings_private/prefs_util.cc @@ -779,6 +779,15 @@ const PrefsUtil::TypedPrefMap& PrefsUtil::GetAllowlistedKeys() { settings_api::PrefType::kBoolean; (*s_allowlist)[ash::prefs::kAccessibilityFaceGazePrecisionClickSpeedFactor] = settings_api::PrefType::kNumber; + (*s_allowlist)[ash::prefs::kAccessibilityFaceGazeEnabledSentinel] = + settings_api::PrefType::kBoolean; + (*s_allowlist)[ash::prefs::kAccessibilityFaceGazeEnabledSentinelShowDialog] = + settings_api::PrefType::kBoolean; + (*s_allowlist) + [ash::prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel] = + settings_api::PrefType::kBoolean; + (*s_allowlist)[ash::prefs::kAccessibilityFaceGazeActionsEnabledSentinel] = + settings_api::PrefType::kBoolean; (*s_allowlist)[ash::prefs::kAccessibilityCaretBlinkInterval] = settings_api::PrefType::kNumber; (*s_allowlist)[ash::prefs::kAccessibilityDisableTrackpadEnabled] = diff --git a/chrome/browser/prefs/pref_service_incognito_allowlist.cc b/chrome/browser/prefs/pref_service_incognito_allowlist.cc index 8da23e3f62f22..53cc913c147bb 100644 --- a/chrome/browser/prefs/pref_service_incognito_allowlist.cc +++ b/chrome/browser/prefs/pref_service_incognito_allowlist.cc @@ -90,6 +90,10 @@ const char* const kPersistentPrefNames[] = { ash::prefs::kAccessibilityFaceGazeVelocityThreshold, ash::prefs::kAccessibilityFaceGazePrecisionClick, ash::prefs::kAccessibilityFaceGazePrecisionClickSpeedFactor, + ash::prefs::kAccessibilityFaceGazeEnabledSentinel, + ash::prefs::kAccessibilityFaceGazeEnabledSentinelShowDialog, + ash::prefs::kAccessibilityFaceGazeCursorControlEnabledSentinel, + ash::prefs::kAccessibilityFaceGazeActionsEnabledSentinel, ash::prefs::kAccessibilityFlashNotificationsEnabled, ash::prefs::kAccessibilityFlashNotificationsColor, ash::prefs::kAccessibilityHighContrastEnabled, diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_actions_card.html b/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_actions_card.html index 39216ae202d52..ef26dff356e26 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_actions_card.html +++ b/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_actions_card.html @@ -44,7 +44,7 @@ <settings-toggle-button id="faceGazeActionsEnabledButton" label="$i18n{faceGazeActionsEnabledLabel}" - pref="{{prefs.settings.a11y.face_gaze.actions_enabled}}" + pref="{{prefs.settings.a11y.face_gaze.actions_enabled_sentinel}}" disabled="[[disabled]]"> </settings-toggle-button> <dom-repeat id="faceGazeActionsCommandPairs" diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_cursor_card.html b/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_cursor_card.html index 2a82651d0f08b..142fcfbcf2a71 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_cursor_card.html +++ b/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_cursor_card.html @@ -35,7 +35,7 @@ <settings-toggle-button id="faceGazeCursorControlEnabledButton" label="$i18n{faceGazeCursorControlEnabledLabel}" - pref="{{prefs.settings.a11y.face_gaze.cursor_control_enabled}}"> + pref="{{prefs.settings.a11y.face_gaze.cursor_control_enabled_sentinel}}"> </settings-toggle-button> <div class="settings-box short"> $i18n{faceGazeCursorSpeedSectionName} diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_subpage.html b/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_subpage.html index 5d6e96a1a116c..4cc835593a11b 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_subpage.html +++ b/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_subpage.html @@ -19,7 +19,7 @@ <settings-toggle-button id="faceGazeToggle" label="[[toggleLabel_]]" - pref="{{prefs.settings.a11y.face_gaze.enabled}}" + pref="{{prefs.settings.a11y.face_gaze.enabled_sentinel}}" deep-link-focus-id$="[[Setting.kFaceGaze]]"> </settings-toggle-button> </settings-card> diff --git a/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_subpage.ts b/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_subpage.ts index 0044286e483f8..fb8c7ef6ff3a3 100644 --- a/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_subpage.ts +++ b/chrome/browser/resources/ash/settings/os_a11y_page/facegaze_subpage.ts @@ -46,7 +46,7 @@ export class SettingsFaceGazeSubpageElement extends toggleLabel_: { type: String, computed: - 'getToggleLabel_(prefs.settings.a11y.face_gaze.enabled.value)', + 'getToggleLabel_(prefs.settings.a11y.face_gaze.enabled_sentinel.value)', }, supportedSettingIds: { @@ -59,7 +59,7 @@ export class SettingsFaceGazeSubpageElement extends } private getToggleLabel_(): string { - return this.getPref('settings.a11y.face_gaze.enabled').value ? + return this.getPref('settings.a11y.face_gaze.enabled_sentinel').value ? this.i18n('deviceOn') : this.i18n('deviceOff'); } diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/constants.ts b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/constants.ts index 27c535d760ced..a2e5e72e749d0 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/constants.ts +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/constants.ts @@ -11,9 +11,15 @@ export enum PrefNames { ACCELERATOR_DIALOG_HAS_BEEN_ACCEPTED = 'settings.a11y.face_gaze.accelerator_dialog_has_been_accepted', ACTIONS_ENABLED = 'settings.a11y.face_gaze.actions_enabled', + ACTIONS_ENABLED_SENTINEL = 'settings.a11y.face_gaze.actions_enabled_sentinel', CURSOR_CONTROL_ENABLED = 'settings.a11y.face_gaze.cursor_control_enabled', + CURSOR_CONTROL_ENABLED_SENTINEL = + 'settings.a11y.face_gaze.cursor_control_enabled_sentinel', CURSOR_USE_ACCELERATION = 'settings.a11y.face_gaze.cursor_use_acceleration', FACE_GAZE_ENABLED = 'settings.a11y.face_gaze.enabled', + FACE_GAZE_ENABLED_SENTINEL = 'settings.a11y.face_gaze.enabled_sentinel', + FACE_GAZE_ENABLED_SENTINEL_SHOW_DIALOG = + 'settings.a11y.face_gaze.enabled_sentinel_show_dialog', GESTURE_TO_CONFIDENCE = 'settings.a11y.face_gaze.gestures_to_confidence', GESTURE_TO_KEY_COMBO = 'settings.a11y.face_gaze.gestures_to_key_combos', GESTURE_TO_MACRO = 'settings.a11y.face_gaze.gestures_to_macros', diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze.ts b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze.ts index f8219bbad0990..58d031f0dca9c 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze.ts +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/facegaze.ts @@ -115,8 +115,13 @@ export class FaceGaze { chrome.settingsPrivate.setPref( PrefNames.ACCELERATOR_DIALOG_HAS_BEEN_ACCEPTED, accepted); if (!accepted) { - // If the dialog was rejected, then disable the FaceGaze feature. - chrome.settingsPrivate.setPref(PrefNames.FACE_GAZE_ENABLED, false); + // If the dialog was rejected, then disable the FaceGaze feature and do + // not show the confirmation dialog for disabling. + chrome.settingsPrivate.setPref( + PrefNames.FACE_GAZE_ENABLED_SENTINEL_SHOW_DIALOG, false, undefined, + () => chrome.settingsPrivate.setPref( + PrefNames.FACE_GAZE_ENABLED_SENTINEL, false)); + return; } diff --git a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/web_cam_face_landmarker.ts b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/web_cam_face_landmarker.ts index 1ddac6d3823d1..ff5cb5bab301c 100644 --- a/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/web_cam_face_landmarker.ts +++ b/chrome/browser/resources/chromeos/accessibility/accessibility_common/facegaze/web_cam_face_landmarker.ts @@ -88,7 +88,10 @@ export class WebCamFaceLandmarker { `Couldn't create FaceLandmarker because FaceGaze assets couldn't be installed.`); - chrome.settingsPrivate.setPref(PrefNames.FACE_GAZE_ENABLED, false); + chrome.settingsPrivate.setPref( + PrefNames.FACE_GAZE_ENABLED_SENTINEL_SHOW_DIALOG, false, undefined, + () => chrome.settingsPrivate.setPref( + PrefNames.FACE_GAZE_ENABLED_SENTINEL, false)); return; } diff --git a/chrome/test/data/webui/chromeos/settings/os_a11y_page/facegaze_actions_card_test.ts b/chrome/test/data/webui/chromeos/settings/os_a11y_page/facegaze_actions_card_test.ts index 2449b6cf33e3f..9e86343197c33 100644 --- a/chrome/test/data/webui/chromeos/settings/os_a11y_page/facegaze_actions_card_test.ts +++ b/chrome/test/data/webui/chromeos/settings/os_a11y_page/facegaze_actions_card_test.ts @@ -149,8 +149,8 @@ suite('<facegaze-actions-card>', () => { test('actions enabled button syncs to pref', async () => { await initPage(); - assertTrue(faceGazeActionsCard.prefs.settings.a11y.face_gaze.actions_enabled - .value); + assertTrue(faceGazeActionsCard.prefs.settings.a11y.face_gaze + .actions_enabled_sentinel.value); const button = faceGazeActionsCard.shadowRoot! .querySelector<SettingsToggleButtonElement>( @@ -164,7 +164,7 @@ suite('<facegaze-actions-card>', () => { assertFalse(button.checked); assertFalse(faceGazeActionsCard.prefs.settings.a11y.face_gaze - .actions_enabled.value); + .actions_enabled_sentinel.value); }); test('actions disables controls if feature is disabled', async () => { diff --git a/chrome/test/data/webui/chromeos/settings/os_a11y_page/facegaze_cursor_card_test.ts b/chrome/test/data/webui/chromeos/settings/os_a11y_page/facegaze_cursor_card_test.ts index 098dc82948064..da01e0b28efcc 100644 --- a/chrome/test/data/webui/chromeos/settings/os_a11y_page/facegaze_cursor_card_test.ts +++ b/chrome/test/data/webui/chromeos/settings/os_a11y_page/facegaze_cursor_card_test.ts @@ -77,7 +77,7 @@ suite('<facegaze-cursor-card>', () => { const prefs = faceGazeCursorCard.prefs.settings.a11y.face_gaze; - assertTrue(prefs.cursor_control_enabled.value); + assertTrue(prefs.cursor_control_enabled_sentinel.value); const button = faceGazeCursorCard.shadowRoot! .querySelector<SettingsToggleButtonElement>( @@ -90,7 +90,7 @@ suite('<facegaze-cursor-card>', () => { flush(); assertFalse(button.checked); - assertFalse(prefs.cursor_control_enabled.value); + assertFalse(prefs.cursor_control_enabled_sentinel.value); }); test( diff --git a/chrome/test/data/webui/chromeos/settings/os_a11y_page/facegaze_subpage_test.ts b/chrome/test/data/webui/chromeos/settings/os_a11y_page/facegaze_subpage_test.ts index e906406051bf7..1d11459c4d2ae 100644 --- a/chrome/test/data/webui/chromeos/settings/os_a11y_page/facegaze_subpage_test.ts +++ b/chrome/test/data/webui/chromeos/settings/os_a11y_page/facegaze_subpage_test.ts @@ -71,10 +71,12 @@ suite('<settings-facegaze-subpage>', () => { test('toggle button reflects pref value', async () => { await initPage(); - faceGazeSubpage.set('prefs.settings.a11y.face_gaze.enabled.value', true); + faceGazeSubpage.set( + 'prefs.settings.a11y.face_gaze.enabled_sentinel.value', true); await flushTasks(); - assertTrue(faceGazeSubpage.prefs.settings.a11y.face_gaze.enabled.value); + assertTrue( + faceGazeSubpage.prefs.settings.a11y.face_gaze.enabled_sentinel.value); const toggle = getToggleButton(); assertTrue(!!toggle); @@ -86,7 +88,8 @@ suite('<settings-facegaze-subpage>', () => { test('clicking toggle button updates pref value', async () => { await initPage(); - assertFalse(faceGazeSubpage.prefs.settings.a11y.face_gaze.enabled.value); + assertFalse( + faceGazeSubpage.prefs.settings.a11y.face_gaze.enabled_sentinel.value); const toggle = getToggleButton(); assertTrue(!!toggle); @@ -98,7 +101,8 @@ suite('<settings-facegaze-subpage>', () => { await flushTasks(); assertTrue(toggle.checked); - assertTrue(faceGazeSubpage.prefs.settings.a11y.face_gaze.enabled.value); + assertTrue( + faceGazeSubpage.prefs.settings.a11y.face_gaze.enabled_sentinel.value); assertEquals('On', toggle.label); }); });