| // Copyright 2017 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ash/accessibility/accessibility_controller.h" |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <utility> |
| |
| #include "ash/accelerators/accelerator_controller_impl.h" |
| #include "ash/accessibility/a11y_feature_type.h" |
| #include "ash/accessibility/accessibility_notification_controller.h" |
| #include "ash/accessibility/accessibility_observer.h" |
| #include "ash/accessibility/autoclick/autoclick_controller.h" |
| #include "ash/accessibility/disable_touchpad_event_rewriter.h" |
| #include "ash/accessibility/drag_event_rewriter.h" |
| #include "ash/accessibility/filter_keys_event_rewriter.h" |
| #include "ash/accessibility/flash_screen_controller.h" |
| #include "ash/accessibility/mouse_keys/mouse_keys_controller.h" |
| #include "ash/accessibility/sticky_keys/sticky_keys_controller.h" |
| #include "ash/accessibility/switch_access/point_scan_controller.h" |
| #include "ash/accessibility/ui/accessibility_confirmation_dialog.h" |
| #include "ash/accessibility/ui/accessibility_highlight_controller.h" |
| #include "ash/accessibility/ui/accessibility_panel_layout_manager.h" |
| #include "ash/color_enhancement/color_enhancement_controller.h" |
| #include "ash/constants/ash_constants.h" |
| #include "ash/constants/ash_features.h" |
| #include "ash/constants/ash_pref_names.h" |
| #include "ash/constants/notifier_catalogs.h" |
| #include "ash/events/accessibility_event_rewriter.h" |
| #include "ash/events/event_rewriter_controller_impl.h" |
| #include "ash/events/select_to_speak_event_handler.h" |
| #include "ash/keyboard/keyboard_controller_impl.h" |
| #include "ash/keyboard/ui/keyboard_util.h" |
| #include "ash/login_status.h" |
| #include "ash/policy/policy_recommendation_restorer.h" |
| #include "ash/public/cpp/accessibility_controller_client.h" |
| #include "ash/public/cpp/accessibility_controller_enums.h" |
| #include "ash/public/cpp/ash_constants.h" |
| #include "ash/public/cpp/notification_utils.h" |
| #include "ash/public/cpp/session/session_observer.h" |
| #include "ash/public/cpp/shell_window_ids.h" |
| #include "ash/public/cpp/system/anchored_nudge_data.h" |
| #include "ash/public/cpp/system/anchored_nudge_manager.h" |
| #include "ash/public/cpp/system_tray_client.h" |
| #include "ash/resources/vector_icons/vector_icons.h" |
| #include "ash/root_window_controller.h" |
| #include "ash/session/session_controller_impl.h" |
| #include "ash/shell.h" |
| #include "ash/strings/grit/ash_strings.h" |
| #include "ash/system/accessibility/accessibility_feature_disable_dialog.h" |
| #include "ash/system/accessibility/dictation_bubble_controller.h" |
| #include "ash/system/accessibility/dictation_button_tray.h" |
| #include "ash/system/accessibility/facegaze_bubble_controller.h" |
| #include "ash/system/accessibility/floating_accessibility_controller.h" |
| #include "ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.h" |
| #include "ash/system/accessibility/switch_access/switch_access_menu_bubble_controller.h" |
| #include "ash/system/input_device_settings/input_device_settings_controller_impl.h" |
| #include "ash/system/model/enterprise_domain_model.h" |
| #include "ash/system/model/system_tray_model.h" |
| #include "ash/system/power/backlights_forced_off_setter.h" |
| #include "ash/system/power/power_status.h" |
| #include "ash/system/power/scoped_backlights_forced_off.h" |
| #include "ash/system/unified/unified_system_tray.h" |
| #include "ash/system/unified/unified_system_tray_bubble.h" |
| #include "ash/wm/window_util.h" |
| #include "base/check_op.h" |
| #include "base/feature_list.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/memory/raw_ptr_exclusion.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/time/time.h" |
| #include "chromeos/ash/components/audio/cras_audio_handler.h" |
| #include "chromeos/ash/components/audio/sounds.h" |
| #include "components/live_caption/caption_util.h" |
| #include "components/live_caption/pref_names.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_change_registrar.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/session_manager/session_manager_types.h" |
| #include "components/user_manager/user_type.h" |
| #include "components/vector_icons/vector_icons.h" |
| #include "media/base/media_switches.h" |
| #include "ui/accessibility/accessibility_features.h" |
| #include "ui/accessibility/aura/aura_window_properties.h" |
| #include "ui/aura/window.h" |
| #include "ui/base/cursor/cursor_size.h" |
| #include "ui/base/ime/ash/ime_keyboard.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/display/screen.h" |
| #include "ui/display/tablet_state.h" |
| #include "ui/events/ash/keyboard_capability.h" |
| #include "ui/events/base_event_utils.h" |
| #include "ui/events/devices/device_data_manager.h" |
| #include "ui/events/devices/keyboard_device.h" |
| #include "ui/events/event_sink.h" |
| #include "ui/gfx/animation/animation.h" |
| #include "ui/message_center/message_center.h" |
| #include "ui/message_center/public/cpp/notifier_id.h" |
| #include "ui/native_theme/features/native_theme_features.h" |
| #include "ui/native_theme/native_theme.h" |
| #include "ui/strings/grit/ui_strings.h" |
| #include "ui/wm/core/coordinate_conversion.h" |
| #include "ui/wm/core/cursor_manager.h" |
| |
| using session_manager::SessionState; |
| |
| namespace ash { |
| namespace { |
| |
| // How much time before the time out dialog should close. |
| const int kDialogTimeoutSeconds = 30; |
| // How much distance to travel with each generated scroll event. |
| const int kScrollDelta = 40; |
| |
| AccessibilityController* g_instance = nullptr; |
| |
| using FeatureType = A11yFeatureType; |
| |
| // These classes are used to store the static configuration for a11y features. |
| struct FeatureData { |
| FeatureType type; |
| const char* pref; |
| // This field is not a raw_ptr<> because it only ever points to statically- |
| // allocated data which is never freed, and hence cannot dangle. |
| RAW_PTR_EXCLUSION const gfx::VectorIcon* icon; |
| const int name_resource_id; |
| bool toggleable_in_quicksettings = true; |
| FeatureType conflicting_feature = FeatureType::kNoConflictingFeature; |
| }; |
| |
| struct FeatureDialogData { |
| FeatureType type; |
| const char* pref; |
| int title; |
| int body; |
| }; |
| |
| // A static array describing each feature. |
| const FeatureData kFeatures[] = { |
| {FeatureType::kAlwaysShowScrollbar, |
| prefs::kAccessibilityAlwaysShowScrollbarsEnabled, nullptr, 0, |
| /*toggleable_in_quicksettings=*/false}, |
| {FeatureType::kAutoclick, prefs::kAccessibilityAutoclickEnabled, |
| &kSystemMenuAccessibilityAutoClickIcon, |
| IDS_ASH_STATUS_TRAY_ACCESSIBILITY_AUTOCLICK}, |
| {FeatureType::kBounceKeys, prefs::kAccessibilityBounceKeysEnabled, nullptr, |
| 0, /*toggleable_in_quicksettings=*/false}, |
| {FeatureType::kCaretHighlight, prefs::kAccessibilityCaretHighlightEnabled, |
| nullptr, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_CARET_HIGHLIGHT}, |
| {FeatureType::kCursorHighlight, prefs::kAccessibilityCursorHighlightEnabled, |
| nullptr, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGHLIGHT_MOUSE_CURSOR}, |
| {FeatureType::kCursorColor, prefs::kAccessibilityCursorColorEnabled, |
| nullptr, 0, /*toggleable_in_quicksettings=*/false}, |
| {FeatureType::kDictation, prefs::kAccessibilityDictationEnabled, |
| &kDictationMenuIcon, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DICTATION}, |
| {FeatureType::kColorCorrection, prefs::kAccessibilityColorCorrectionEnabled, |
| &kColorCorrectionIcon, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_COLOR_CORRECTION}, |
| {FeatureType::kFlashNotifications, |
| prefs::kAccessibilityFlashNotificationsEnabled, nullptr, 0, |
| /*toggleable_in_quicksettings=*/false}, |
| {FeatureType::kFocusHighlight, prefs::kAccessibilityFocusHighlightEnabled, |
| nullptr, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGHLIGHT_KEYBOARD_FOCUS, |
| /*toggleable_in_quicksettings=*/true, |
| /* conflicting_feature= */ FeatureType::kSpokenFeedback}, |
| {FeatureType::kFloatingMenu, prefs::kAccessibilityFloatingMenuEnabled, |
| nullptr, IDS_ASH_FLOATING_ACCESSIBILITY_MAIN_MENU, |
| /*toggleable_in_quicksettings=*/false}, |
| {FeatureType::kFullscreenMagnifier, |
| prefs::kAccessibilityScreenMagnifierEnabled, |
| &kSystemMenuAccessibilityFullscreenMagnifierIcon, |
| IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SCREEN_MAGNIFIER}, |
| {FeatureType::kDockedMagnifier, prefs::kDockedMagnifierEnabled, |
| &kSystemMenuAccessibilityDockedMagnifierIcon, |
| IDS_ASH_STATUS_TRAY_ACCESSIBILITY_DOCKED_MAGNIFIER}, |
| {FeatureType::kHighContrast, prefs::kAccessibilityHighContrastEnabled, |
| &kSystemMenuAccessibilityContrastIcon, |
| IDS_ASH_STATUS_TRAY_ACCESSIBILITY_HIGH_CONTRAST_MODE}, |
| {FeatureType::kLargeCursor, prefs::kAccessibilityLargeCursorEnabled, |
| nullptr, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_LARGE_CURSOR}, |
| {FeatureType::kLiveCaption, ::prefs::kLiveCaptionEnabled, |
| &vector_icons::kLiveCaptionOnIcon, IDS_ASH_STATUS_TRAY_LIVE_CAPTION}, |
| {FeatureType::kMonoAudio, prefs::kAccessibilityMonoAudioEnabled, nullptr, |
| IDS_ASH_STATUS_TRAY_ACCESSIBILITY_MONO_AUDIO}, |
| {FeatureType::kMouseKeys, prefs::kAccessibilityMouseKeysEnabled, nullptr, 0, |
| /*toggleable_in_quicksettings=*/false}, |
| {FeatureType::kSpokenFeedback, prefs::kAccessibilitySpokenFeedbackEnabled, |
| &kSystemMenuAccessibilityChromevoxIcon, |
| IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SPOKEN_FEEDBACK}, |
| {FeatureType::kReducedAnimations, |
| prefs::kAccessibilityReducedAnimationsEnabled, nullptr, 0, |
| /*toggleable_in_quicksettings=*/false}, |
| {FeatureType::kSelectToSpeak, prefs::kAccessibilitySelectToSpeakEnabled, |
| &kSystemMenuAccessibilitySelectToSpeakIcon, |
| IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SELECT_TO_SPEAK}, |
| {FeatureType::kSlowKeys, prefs::kAccessibilitySlowKeysEnabled, nullptr, 0, |
| /*toggleable_in_quicksettings=*/false}, |
| {FeatureType::kStickyKeys, prefs::kAccessibilityStickyKeysEnabled, nullptr, |
| IDS_ASH_STATUS_TRAY_ACCESSIBILITY_STICKY_KEYS, |
| /*toggleable_in_quicksettings=*/true, |
| /*conflicting_feature=*/FeatureType::kSpokenFeedback}, |
| {FeatureType::kSwitchAccess, prefs::kAccessibilitySwitchAccessEnabled, |
| &kSwitchAccessIcon, IDS_ASH_STATUS_TRAY_ACCESSIBILITY_SWITCH_ACCESS}, |
| {FeatureType::kVirtualKeyboard, prefs::kAccessibilityVirtualKeyboardEnabled, |
| &kSystemMenuKeyboardLegacyIcon, |
| IDS_ASH_STATUS_TRAY_ACCESSIBILITY_VIRTUAL_KEYBOARD}, |
| {FeatureType::kFaceGaze, prefs::kAccessibilityFaceGazeEnabled, nullptr, |
| IDS_ASH_STATUS_TRAY_ACCESSIBILITY_FACEGAZE, |
| /*toggleable_in_quicksettings=*/true}, |
| {FeatureType::kDisableTouchpad, prefs::kAccessibilityDisableTrackpadEnabled, |
| nullptr, 0, /*toggleable_in_quicksettings=*/false}, |
| }; |
| |
| // An array describing the confirmation dialogs for the features which have |
| // them. |
| const FeatureDialogData kFeatureDialogs[] = { |
| {FeatureType::kFullscreenMagnifier, |
| prefs::kScreenMagnifierAcceleratorDialogHasBeenAccepted}, |
| {FeatureType::kDockedMagnifier, |
| prefs::kDockedMagnifierAcceleratorDialogHasBeenAccepted}, |
| {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[] = |
| "dictation_language_upgraded.nudge_id"; |
| |
| // TODO(warx): Signin screen has more controllable accessibility prefs. We may |
| // want to expand this to a complete list. If so, merge this with |
| // |kCopiedOnSigninAccessibilityPrefs|. |
| constexpr const char* const kA11yPrefsForRecommendedValueOnSignin[]{ |
| prefs::kAccessibilityLargeCursorEnabled, |
| prefs::kAccessibilityHighContrastEnabled, |
| prefs::kAccessibilityScreenMagnifierEnabled, |
| prefs::kAccessibilitySpokenFeedbackEnabled, |
| prefs::kAccessibilityVirtualKeyboardEnabled, |
| }; |
| |
| // List of accessibility prefs that are to be copied (if changed by the user) on |
| // signin screen profile to a newly created user profile or a guest session. |
| constexpr const char* const kCopiedOnSigninAccessibilityPrefs[]{ |
| prefs::kAccessibilityAutoclickDelayMs, |
| prefs::kAccessibilityAutoclickEnabled, |
| prefs::kAccessibilityBounceKeysDelayMs, |
| prefs::kAccessibilityBounceKeysEnabled, |
| prefs::kAccessibilityCaretHighlightEnabled, |
| prefs::kAccessibilityChromeVoxAutoRead, |
| prefs::kAccessibilityChromeVoxAnnounceDownloadNotifications, |
| prefs::kAccessibilityChromeVoxAnnounceRichTextAttributes, |
| prefs::kAccessibilityChromeVoxAudioStrategy, |
| prefs::kAccessibilityChromeVoxBrailleSideBySide, |
| prefs::kAccessibilityChromeVoxBrailleTable, |
| prefs::kAccessibilityChromeVoxBrailleTable6, |
| prefs::kAccessibilityChromeVoxBrailleTable8, |
| prefs::kAccessibilityChromeVoxBrailleTableType, |
| prefs::kAccessibilityChromeVoxBrailleWordWrap, |
| prefs::kAccessibilityChromeVoxCapitalStrategy, |
| prefs::kAccessibilityChromeVoxCapitalStrategyBackup, |
| prefs::kAccessibilityChromeVoxEnableBrailleLogging, |
| prefs::kAccessibilityChromeVoxEnableEarconLogging, |
| prefs::kAccessibilityChromeVoxEnableEventStreamLogging, |
| prefs::kAccessibilityChromeVoxEnableSpeechLogging, |
| prefs::kAccessibilityChromeVoxEventStreamFilters, |
| prefs::kAccessibilityChromeVoxLanguageSwitching, |
| prefs::kAccessibilityChromeVoxMenuBrailleCommands, |
| prefs::kAccessibilityChromeVoxNumberReadingStyle, |
| prefs::kAccessibilityChromeVoxPreferredBrailleDisplayAddress, |
| prefs::kAccessibilityChromeVoxPunctuationEcho, |
| prefs::kAccessibilityChromeVoxSmartStickyMode, |
| prefs::kAccessibilityChromeVoxSpeakTextUnderMouse, |
| prefs::kAccessibilityChromeVoxUsePitchChanges, |
| prefs::kAccessibilityChromeVoxUseVerboseMode, |
| prefs::kAccessibilityChromeVoxVirtualBrailleColumns, |
| prefs::kAccessibilityChromeVoxVirtualBrailleRows, |
| prefs::kAccessibilityChromeVoxVoiceName, |
| prefs::kAccessibilityColorCorrectionEnabled, |
| prefs::kAccessibilityCursorHighlightEnabled, |
| prefs::kAccessibilityCursorColorEnabled, |
| prefs::kAccessibilityCursorColor, |
| prefs::kAccessibilityDictationEnabled, |
| prefs::kAccessibilityDictationLocale, |
| prefs::kAccessibilityDictationLocaleOfflineNudge, |
| prefs::kAccessibilityDisableTrackpadEnabled, |
| prefs::kAccessibilityDisableTrackpadMode, |
| prefs::kAccessibilityFocusHighlightEnabled, |
| prefs::kAccessibilityHighContrastEnabled, |
| prefs::kAccessibilityLargeCursorEnabled, |
| prefs::kAccessibilityFaceGazeEnabled, |
| prefs::kAccessibilityMonoAudioEnabled, |
| prefs::kAccessibilityReducedAnimationsEnabled, |
| prefs::kAccessibilityAlwaysShowScrollbarsEnabled, |
| prefs::kAccessibilityMouseKeysEnabled, |
| prefs::kAccessibilityMouseKeysAcceleration, |
| prefs::kAccessibilityMouseKeysMaxSpeed, |
| prefs::kAccessibilityMouseKeysUsePrimaryKeys, |
| prefs::kAccessibilityMouseKeysDominantHand, |
| prefs::kAccessibilityScreenMagnifierEnabled, |
| prefs::kAccessibilityScreenMagnifierFocusFollowingEnabled, |
| prefs::kAccessibilityMagnifierFollowsChromeVox, |
| prefs::kAccessibilityMagnifierFollowsSts, |
| prefs::kAccessibilityScreenMagnifierMouseFollowingMode, |
| prefs::kAccessibilityScreenMagnifierScale, |
| prefs::kAccessibilitySelectToSpeakEnabled, |
| prefs::kAccessibilitySlowKeysDelayMs, |
| prefs::kAccessibilitySlowKeysEnabled, |
| prefs::kAccessibilitySpokenFeedbackEnabled, |
| prefs::kAccessibilityStickyKeysEnabled, |
| prefs::kAccessibilityShortcutsEnabled, |
| prefs::kAccessibilitySwitchAccessEnabled, |
| prefs::kAccessibilityVirtualKeyboardEnabled, |
| prefs::kDockedMagnifierEnabled, |
| prefs::kDockedMagnifierScale, |
| prefs::kDockedMagnifierScreenHeightDivisor, |
| prefs::kHighContrastAcceleratorDialogHasBeenAccepted, |
| prefs::kScreenMagnifierAcceleratorDialogHasBeenAccepted, |
| prefs::kDockedMagnifierAcceleratorDialogHasBeenAccepted, |
| prefs::kDictationAcceleratorDialogHasBeenAccepted, |
| prefs::kDictationDlcSuccessNotificationHasBeenShown, |
| prefs::kDictationDlcOnlyPumpkinDownloadedNotificationHasBeenShown, |
| prefs::kDictationDlcOnlySodaDownloadedNotificationHasBeenShown, |
| prefs::kDictationNoDlcsDownloadedNotificationHasBeenShown, |
| prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted2, |
| prefs::kSelectToSpeakAcceleratorDialogHasBeenAccepted, |
| prefs::kAccessibilityFaceGazeAcceleratorDialogHasBeenAccepted, |
| prefs::kFaceGazeDlcSuccessNotificationHasBeenShown, |
| prefs::kFaceGazeDlcFailureNotificationHasBeenShown, |
| }; |
| |
| // List of switch access accessibility prefs that are to be copied (if changed |
| // by the user) from the current user to the signin screen profile. That way |
| // if a switch access user signs out, their switch continues to function. |
| constexpr const char* const kSwitchAccessPrefsCopiedToSignin[]{ |
| prefs::kAccessibilitySwitchAccessAutoScanEnabled, |
| prefs::kAccessibilitySwitchAccessAutoScanKeyboardSpeedMs, |
| prefs::kAccessibilitySwitchAccessAutoScanSpeedMs, |
| prefs::kAccessibilitySwitchAccessPointScanSpeedDipsPerSecond, |
| prefs::kAccessibilitySwitchAccessEnabled, |
| prefs::kAccessibilitySwitchAccessNextDeviceKeyCodes, |
| prefs::kAccessibilitySwitchAccessPreviousDeviceKeyCodes, |
| prefs::kAccessibilitySwitchAccessSelectDeviceKeyCodes, |
| }; |
| |
| // Helper function that is used to verify the validity of kFeatures and |
| // kFeatureDialogs. |
| bool VerifyFeaturesData() { |
| // All feature prefs must be unique. |
| std::set<const char*> feature_prefs; |
| for (auto feature_data : kFeatures) { |
| if (base::Contains(feature_prefs, feature_data.pref)) { |
| return false; |
| } |
| feature_prefs.insert(feature_data.pref); |
| } |
| |
| for (auto dialog_data : kFeatureDialogs) { |
| if (base::Contains(feature_prefs, dialog_data.pref)) { |
| return false; |
| } |
| feature_prefs.insert(dialog_data.pref); |
| } |
| |
| return true; |
| } |
| |
| // Returns true if |pref_service| is the one used for the signin screen. |
| bool IsSigninPrefService(PrefService* pref_service) { |
| const PrefService* signin_pref_service = |
| Shell::Get()->session_controller()->GetSigninScreenPrefService(); |
| DCHECK(signin_pref_service); |
| return pref_service == signin_pref_service; |
| } |
| |
| // Returns true if the current session is the guest session. |
| bool IsCurrentSessionGuest() { |
| const std::optional<user_manager::UserType> user_type = |
| Shell::Get()->session_controller()->GetUserType(); |
| return user_type && *user_type == user_manager::UserType::kGuest; |
| } |
| |
| bool IsUserFirstLogin() { |
| return Shell::Get()->session_controller()->IsUserFirstLogin(); |
| } |
| |
| // The copying of any modified accessibility prefs on the signin prefs happens |
| // when the |previous_pref_service| is of the signin profile, and the |
| // |current_pref_service| is of a newly created profile first logged in, or if |
| // the current session is the guest session. |
| bool ShouldCopySigninPrefs(PrefService* previous_pref_service, |
| PrefService* current_pref_service) { |
| DCHECK(previous_pref_service); |
| if (IsUserFirstLogin() && IsSigninPrefService(previous_pref_service) && |
| !IsSigninPrefService(current_pref_service)) { |
| // If the user set a pref value on the login screen and is now starting a |
| // session with a new profile, copy the pref value to the profile. |
| return true; |
| } |
| |
| if (IsCurrentSessionGuest()) { |
| // Guest sessions don't have their own prefs, so always copy. |
| return true; |
| } |
| |
| return false; |
| } |
| |
| // On a user's first login into a device, any a11y features enabled/disabled |
| // by the user on the login screen are enabled/disabled in the user's profile. |
| // This function copies settings from the signin prefs into the user's prefs |
| // when it detects a login with a newly created profile. |
| void CopySigninPrefsIfNeeded(PrefService* previous_pref_service, |
| PrefService* current_pref_service) { |
| DCHECK(current_pref_service); |
| if (!ShouldCopySigninPrefs(previous_pref_service, current_pref_service)) { |
| return; |
| } |
| |
| PrefService* signin_prefs = |
| Shell::Get()->session_controller()->GetSigninScreenPrefService(); |
| DCHECK(signin_prefs); |
| for (const auto* pref_path : kCopiedOnSigninAccessibilityPrefs) { |
| const PrefService::Preference* pref = |
| signin_prefs->FindPreference(pref_path); |
| |
| // Ignore if the pref has not been set by the user. |
| if (!pref || !pref->IsUserControlled()) { |
| continue; |
| } |
| |
| // Copy the pref value from the signin profile. |
| const base::Value* value_on_login = pref->GetValue(); |
| current_pref_service->Set(pref_path, *value_on_login); |
| } |
| } |
| |
| // Returns notification icon based on the A11yNotificationType. |
| const gfx::VectorIcon& GetNotificationIcon(A11yNotificationType type) { |
| switch (type) { |
| case A11yNotificationType::kSpokenFeedbackBrailleEnabled: |
| case A11yNotificationType::kTouchpadDisabled: |
| return kNotificationAccessibilityIcon; |
| case A11yNotificationType::kBrailleDisplayConnected: |
| return kNotificationAccessibilityBrailleIcon; |
| case A11yNotificationType::kSwitchAccessEnabled: |
| return kSwitchAccessIcon; |
| case A11yNotificationType::kDictationAllDlcsDownloaded: |
| case A11yNotificationType::kDictationNoDlcsDownloaded: |
| case A11yNotificationType::kDicationOnlyPumpkinDownloaded: |
| case A11yNotificationType::kDictationOnlySodaDownloaded: |
| return kDictationMenuIcon; |
| case A11yNotificationType::kFaceGazeActive: |
| return kFacegazeIcon; |
| default: |
| return kNotificationChromevoxIcon; |
| } |
| } |
| |
| 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(notification_id, false /* by_user */); |
| |
| if (type == A11yNotificationType::kNone) { |
| return; |
| } |
| |
| std::u16string text; |
| std::u16string title; |
| std::u16string display_source; |
| auto catalog_name = NotificationCatalogName::kNone; |
| bool pinned = true; |
| message_center::SystemNotificationWarningLevel warning = |
| message_center::SystemNotificationWarningLevel::NORMAL; |
| |
| message_center::RichNotificationData options; |
| scoped_refptr<message_center::NotificationDelegate> delegate; |
| |
| if (wrapper.callback.has_value()) { |
| delegate = |
| base::MakeRefCounted<message_center::HandleNotificationClickDelegate>( |
| wrapper.callback.value()); |
| } |
| |
| if (type == A11yNotificationType::kBrailleDisplayConnected) { |
| title = 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::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); |
| text = |
| l10n_util::GetStringUTF16(IDS_ASH_A11Y_FACEGAZE_ASSETS_DOWNLOADED_DESC); |
| catalog_name = NotificationCatalogName::kFaceGazeAssetsDownloaded; |
| pinned = false; |
| } else if (type == A11yNotificationType::kFaceGazeAssetsFailed) { |
| title = |
| l10n_util::GetStringUTF16(IDS_ASH_A11Y_FACEGAZE_ASSETS_FAILED_TITLE); |
| text = l10n_util::GetStringUTF16(IDS_ASH_A11Y_FACEGAZE_ASSETS_FAILED_DESC); |
| catalog_name = NotificationCatalogName::kFaceGazeAssetsFailed; |
| pinned = false; |
| // Use CRITICAL_WARNING to force the notification color to red. |
| warning = message_center::SystemNotificationWarningLevel::CRITICAL_WARNING; |
| } else if (type == A11yNotificationType::kSwitchAccessEnabled) { |
| title = l10n_util::GetStringUTF16( |
| IDS_ASH_STATUS_TRAY_SWITCH_ACCESS_ENABLED_TITLE); |
| text = l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_SWITCH_ACCESS_ENABLED); |
| catalog_name = NotificationCatalogName::kSwitchAccessEnabled; |
| } else if (type == A11yNotificationType::kTouchpadDisabled) { |
| title = |
| l10n_util::GetStringUTF16(IDS_ASH_STATUS_TRAY_TOUCHPAD_DISABLED_TITLE); |
| text = l10n_util::GetStringUTF16( |
| IDS_ASH_STATUS_TRAY_TOUCHPAD_DISABLED_DESCRIPTION); |
| catalog_name = NotificationCatalogName::kTouchpadDisabled; |
| options.pinned = true; |
| options.buttons.emplace_back(l10n_util::GetStringUTF16( |
| IDS_ASH_STATUS_TRAY_TOUCHPAD_DISABLED_TURN_ON)); |
| |
| } else { |
| bool is_tablet = display::Screen::Get()->InTabletMode(); |
| |
| title = l10n_util::GetStringUTF16( |
| type == A11yNotificationType::kSpokenFeedbackBrailleEnabled |
| ? IDS_ASH_STATUS_TRAY_SPOKEN_FEEDBACK_BRAILLE_ENABLED_TITLE |
| : IDS_ASH_STATUS_TRAY_SPOKEN_FEEDBACK_ENABLED_TITLE); |
| text = l10n_util::GetStringUTF16( |
| is_tablet ? IDS_ASH_STATUS_TRAY_SPOKEN_FEEDBACK_ENABLED_TABLET |
| : IDS_ASH_STATUS_TRAY_SPOKEN_FEEDBACK_ENABLED); |
| catalog_name = type == A11yNotificationType::kSpokenFeedbackBrailleEnabled |
| ? NotificationCatalogName::kSpokenFeedbackBrailleEnabled |
| : NotificationCatalogName::kSpokenFeedbackEnabled; |
| } |
| |
| options.should_make_spoken_feedback_for_popup_updates = false; |
| std::unique_ptr<message_center::Notification> notification = |
| ash::CreateSystemNotificationPtr( |
| message_center::NOTIFICATION_TYPE_SIMPLE, notification_id, title, |
| text, display_source, GURL(), |
| message_center::NotifierId( |
| message_center::NotifierType::SYSTEM_COMPONENT, |
| kNotifierAccessibility, catalog_name), |
| options, delegate, GetNotificationIcon(type), warning); |
| notification->set_pinned(pinned); |
| message_center->AddNotification(std::move(notification)); |
| } |
| |
| void RemoveAccessibilityNotification() { |
| ShowAccessibilityNotification( |
| AccessibilityController::A11yNotificationWrapper( |
| A11yNotificationType::kNone, kNotificationId, |
| std::vector<std::u16string>())); |
| } |
| |
| AccessibilityPanelLayoutManager* GetLayoutManager() { |
| // The accessibility panel is only shown on the primary display. |
| aura::Window* root = Shell::GetPrimaryRootWindow(); |
| aura::Window* container = |
| Shell::GetContainer(root, kShellWindowId_AccessibilityPanelContainer); |
| // TODO(jamescook): Avoid this cast by moving ash::AccessibilityObserver |
| // ownership to this class and notifying it on accessibility panel fullscreen |
| // updates. |
| return static_cast<AccessibilityPanelLayoutManager*>( |
| container->layout_manager()); |
| } |
| |
| std::string PrefKeyForSwitchAccessCommand(SwitchAccessCommand command) { |
| switch (command) { |
| case SwitchAccessCommand::kSelect: |
| return prefs::kAccessibilitySwitchAccessSelectDeviceKeyCodes; |
| case SwitchAccessCommand::kNext: |
| return prefs::kAccessibilitySwitchAccessNextDeviceKeyCodes; |
| case SwitchAccessCommand::kPrevious: |
| return prefs::kAccessibilitySwitchAccessPreviousDeviceKeyCodes; |
| case SwitchAccessCommand::kNone: |
| NOTREACHED(); |
| } |
| } |
| |
| std::string UmaNameForSwitchAccessCommand(SwitchAccessCommand command) { |
| switch (command) { |
| case SwitchAccessCommand::kSelect: |
| return "Accessibility.CrosSwitchAccess.SelectKeyCode"; |
| case SwitchAccessCommand::kNext: |
| return "Accessibility.CrosSwitchAccess.NextKeyCode"; |
| case SwitchAccessCommand::kPrevious: |
| return "Accessibility.CrosSwitchAccess.PreviousKeyCode"; |
| case SwitchAccessCommand::kNone: |
| NOTREACHED(); |
| } |
| } |
| |
| // These values are persisted to logs. Entries should not be renumbered and |
| // numeric values should never be reused. |
| enum class SwitchAccessKeyCode { |
| kUnknown = 0, |
| kKeycode1 = 1, |
| kKeycode2 = 2, |
| kKeycode3 = 3, |
| kKeycode4 = 4, |
| kKeycode5 = 5, |
| kKeycode6 = 6, |
| kKeycode7 = 7, |
| kBackspace = 8, |
| kTab = 9, |
| kKeycode10 = 10, |
| kKeycode11 = 11, |
| kClear = 12, |
| kReturn = 13, |
| kKeycode14 = 14, |
| kKeycode15 = 15, |
| kShift = 16, |
| kControl = 17, |
| kAlt = 18, |
| kPause = 19, |
| kCapital = 20, |
| kKana = 21, |
| kKeycode22 = 22, |
| kJunja = 23, |
| kFinal = 24, |
| kHanja = 25, |
| kKeycode26 = 26, |
| kEscape = 27, |
| kConvert = 28, |
| kNonconvert = 29, |
| kAccept = 30, |
| kModechange = 31, |
| kSpace = 32, |
| kPrior = 33, |
| kNext = 34, |
| kEnd = 35, |
| kHome = 36, |
| kLeft = 37, |
| kUp = 38, |
| kRight = 39, |
| kDown = 40, |
| kSelect = 41, |
| kPrint = 42, |
| kExecute = 43, |
| kSnapshot = 44, |
| kInsert = 45, |
| kKeyDelete = 46, |
| kHelp = 47, |
| kNum0 = 48, |
| kNum1 = 49, |
| kNum2 = 50, |
| kNum3 = 51, |
| kNum4 = 52, |
| kNum5 = 53, |
| kNum6 = 54, |
| kNum7 = 55, |
| kNum8 = 56, |
| kNum9 = 57, |
| kKeycode58 = 58, |
| kKeycode59 = 59, |
| kKeycode60 = 60, |
| kKeycode61 = 61, |
| kKeycode62 = 62, |
| kKeycode63 = 63, |
| kKeycode64 = 64, |
| kA = 65, |
| kB = 66, |
| kC = 67, |
| kD = 68, |
| kE = 69, |
| kF = 70, |
| kG = 71, |
| kH = 72, |
| kI = 73, |
| kJ = 74, |
| kK = 75, |
| kL = 76, |
| kM = 77, |
| kN = 78, |
| kO = 79, |
| kP = 80, |
| kQ = 81, |
| kR = 82, |
| kS = 83, |
| kT = 84, |
| kU = 85, |
| kV = 86, |
| kW = 87, |
| kX = 88, |
| kY = 89, |
| kZ = 90, |
| kLwin = 91, |
| kRwin = 92, |
| kApps = 93, |
| kKeycode94 = 94, |
| kSleep = 95, |
| kNumpad0 = 96, |
| kNumpad1 = 97, |
| kNumpad2 = 98, |
| kNumpad3 = 99, |
| kNumpad4 = 100, |
| kNumpad5 = 101, |
| kNumpad6 = 102, |
| kNumpad7 = 103, |
| kNumpad8 = 104, |
| kNumpad9 = 105, |
| kMultiply = 106, |
| kAdd = 107, |
| kSeparator = 108, |
| kSubtract = 109, |
| kDecimal = 110, |
| kDivide = 111, |
| kF1 = 112, |
| kF2 = 113, |
| kF3 = 114, |
| kF4 = 115, |
| kF5 = 116, |
| kF6 = 117, |
| kF7 = 118, |
| kF8 = 119, |
| kF9 = 120, |
| kF10 = 121, |
| kF11 = 122, |
| kF12 = 123, |
| kF13 = 124, |
| kF14 = 125, |
| kF15 = 126, |
| kF16 = 127, |
| kF17 = 128, |
| kF18 = 129, |
| kF19 = 130, |
| kF20 = 131, |
| kF21 = 132, |
| kF22 = 133, |
| kF23 = 134, |
| kF24 = 135, |
| kKeycode136 = 136, |
| kKeycode137 = 137, |
| kKeycode138 = 138, |
| kKeycode139 = 139, |
| kKeycode140 = 140, |
| kKeycode141 = 141, |
| kKeycode142 = 142, |
| kKeycode143 = 143, |
| kNumlock = 144, |
| kScroll = 145, |
| kKeycode146 = 146, |
| kKeycode147 = 147, |
| kKeycode148 = 148, |
| kKeycode149 = 149, |
| kKeycode150 = 150, |
| kWlan = 151, |
| kPower = 152, |
| kAssistant = 153, |
| kKeycode154 = 154, |
| kKeycode155 = 155, |
| kKeycode156 = 156, |
| kKeycode157 = 157, |
| kKeycode158 = 158, |
| kKeycode159 = 159, |
| kLshift = 160, |
| kRshift = 161, |
| kLcontrol = 162, |
| kRcontrol = 163, |
| kLmenu = 164, |
| kRmenu = 165, |
| kBrowserBack = 166, |
| kBrowserForward = 167, |
| kBrowserRefresh = 168, |
| kBrowserStop = 169, |
| kBrowserSearch = 170, |
| kBrowserFavorites = 171, |
| kBrowserHome = 172, |
| kVolumeMute = 173, |
| kVolumeDown = 174, |
| kVolumeUp = 175, |
| kMediaNextTrack = 176, |
| kMediaPrevTrack = 177, |
| kMediaStop = 178, |
| kMediaPlayPause = 179, |
| kMediaLaunchMail = 180, |
| kMediaLaunchMediaSelect = 181, |
| kMediaLaunchApp1 = 182, |
| kMediaLaunchApp2 = 183, |
| kKeycode184 = 184, |
| kKeycode185 = 185, |
| kOem1 = 186, |
| kOemPlus = 187, |
| kOemComma = 188, |
| kOemMinus = 189, |
| kOemPeriod = 190, |
| kOem2 = 191, |
| kOem3 = 192, |
| kKeycode193 = 193, |
| kKeycode194 = 194, |
| kKeycode195 = 195, |
| kKeycode196 = 196, |
| kKeycode197 = 197, |
| kKeycode198 = 198, |
| kKeycode199 = 199, |
| kKeycode200 = 200, |
| kKeycode201 = 201, |
| kKeycode202 = 202, |
| kKeycode203 = 203, |
| kKeycode204 = 204, |
| kKeycode205 = 205, |
| kKeycode206 = 206, |
| kKeycode207 = 207, |
| kKeycode208 = 208, |
| kKeycode209 = 209, |
| kKeycode210 = 210, |
| kKeycode211 = 211, |
| kKeycode212 = 212, |
| kKeycode213 = 213, |
| kKeycode214 = 214, |
| kKeycode215 = 215, |
| kBrightnessDown = 216, |
| kBrightnessUp = 217, |
| kKbdBrightnessDown = 218, |
| kOem4 = 219, |
| kOem5 = 220, |
| kOem6 = 221, |
| kOem7 = 222, |
| kOem8 = 223, |
| kKeycode224 = 224, |
| kAltgr = 225, |
| kOem102 = 226, |
| kKeycode227 = 227, |
| kKeycode228 = 228, |
| kProcesskey = 229, |
| kCompose = 230, |
| kPacket = 231, |
| kKbdBrightnessUp = 232, |
| kKeycode233 = 233, |
| kKeycode234 = 234, |
| kKeycode235 = 235, |
| kKeycode236 = 236, |
| kKeycode237 = 237, |
| kKeycode238 = 238, |
| kKeycode239 = 239, |
| kKeycode240 = 240, |
| kKeycode241 = 241, |
| kKeycode242 = 242, |
| kDbeSbcschar = 243, |
| kDbeDbcschar = 244, |
| kKeycode245 = 245, |
| kAttn = 246, |
| kCrsel = 247, |
| kExsel = 248, |
| kEreof = 249, |
| kPlay = 250, |
| kZoom = 251, |
| kNoname = 252, |
| kPa1 = 253, |
| kOemClear = 254, |
| kKeycode255 = 255, |
| kNone = 256, |
| kMaxValue = kNone, |
| }; |
| |
| } // namespace |
| |
| AccessibilityController::Feature::Feature( |
| FeatureType type, |
| const std::string& pref_name, |
| const gfx::VectorIcon* icon, |
| const int name_resource_id, |
| const bool toggleable_in_quicksettings, |
| AccessibilityController* controller) |
| : type_(type), |
| pref_name_(pref_name), |
| icon_(icon), |
| name_resource_id_(name_resource_id), |
| toggleable_in_quicksettings_(toggleable_in_quicksettings), |
| owner_(controller) { |
| // If a feature is toggleable in quicksettings it must have a |
| // `name_resource_id` so it's name can be looked up. |
| if (toggleable_in_quicksettings_) { |
| CHECK(name_resource_id); |
| } |
| } |
| |
| AccessibilityController::Feature::~Feature() = default; |
| |
| void AccessibilityController::Feature::SetEnabled(bool enabled) { |
| PrefService* prefs = owner_->active_user_prefs_; |
| if (!prefs) { |
| return; |
| } |
| prefs->SetBoolean(pref_name_, enabled); |
| prefs->CommitPendingWrite(); |
| } |
| |
| bool AccessibilityController::Feature::IsVisibleInTray() const { |
| return (conflicting_feature_ == FeatureType::kNoConflictingFeature || |
| !owner_->GetFeature(conflicting_feature_).enabled()) && |
| owner_->IsAccessibilityFeatureVisibleInTrayMenu(pref_name_); |
| } |
| |
| bool AccessibilityController::Feature::IsEnterpriseIconVisible() const { |
| return owner_->IsEnterpriseIconVisibleInTrayMenu(pref_name_); |
| } |
| |
| const gfx::VectorIcon& AccessibilityController::Feature::icon() const { |
| DCHECK(icon_); |
| if (icon_) { |
| return *icon_; |
| } |
| return kPaletteTrayIconDefaultIcon; |
| } |
| |
| void AccessibilityController::Feature::UpdateFromPref() { |
| PrefService* prefs = owner_->active_user_prefs_; |
| DCHECK(prefs); |
| |
| bool enabled = prefs->GetBoolean(pref_name_); |
| |
| if (conflicting_feature_ != FeatureType::kNoConflictingFeature && |
| owner_->GetFeature(conflicting_feature_).enabled()) { |
| enabled = false; |
| } |
| |
| if (enabled) { |
| // If it was turned on and we are in a active logged in session, |
| // prepare to record duration metrics. |
| session_manager::SessionState session_state = |
| Shell::Get()->session_controller()->GetSessionState(); |
| if (session_state == session_manager::SessionState::ACTIVE) { |
| enabled_time_ = base::Time::Now(); |
| } |
| } else { |
| // Disabled. Log the duration since it was enabled, if needed. |
| LogDurationMetric(); |
| } |
| |
| if (enabled == enabled_) { |
| return; |
| } |
| |
| enabled_ = enabled; |
| owner_->UpdateFeatureFromPref(type_); |
| } |
| |
| // don't pass prefservice here because it might be old. |
| // instead save the session state type from when enabled_time_ was set. |
| // maybe don't bother logging user type. just have this be for logged in?? |
| // is session state more interesting? |
| // duration if session state is ACTIVE |
| void AccessibilityController::Feature::LogDurationMetric() { |
| if (enabled_time_ == base::Time()) { |
| return; |
| } |
| |
| std::string feature_duration_metric = "Accessibility."; |
| switch (type_) { |
| case FeatureType::kAlwaysShowScrollbar: |
| feature_duration_metric += "CrosAlwaysShowScrollbar"; |
| break; |
| case FeatureType::kAutoclick: |
| feature_duration_metric += "CrosAutoclick"; |
| break; |
| case FeatureType::kBounceKeys: |
| feature_duration_metric += "CrosBounceKeys"; |
| break; |
| case FeatureType::kCaretHighlight: |
| feature_duration_metric += "CrosCaretHighlight"; |
| break; |
| case FeatureType::kColorCorrection: |
| feature_duration_metric += "CrosColorCorrection"; |
| break; |
| case FeatureType::kCursorColor: |
| feature_duration_metric += "CrosCursorColor"; |
| break; |
| case FeatureType::kCursorHighlight: |
| feature_duration_metric += "CrosCursorHighlight"; |
| break; |
| case FeatureType::kDictation: |
| feature_duration_metric += "CrosDictation"; |
| break; |
| case FeatureType::kDisableTouchpad: |
| feature_duration_metric += "CrosDisableTouchpad"; |
| break; |
| case FeatureType::kDockedMagnifier: |
| feature_duration_metric += "CrosDockedMagnifier"; |
| break; |
| case FeatureType::kFaceGaze: |
| feature_duration_metric += "CrosFaceGaze"; |
| break; |
| case FeatureType::kFlashNotifications: |
| feature_duration_metric += "CrosFlashNotifications"; |
| break; |
| case FeatureType::kFocusHighlight: |
| feature_duration_metric += "CrosFocusHighlight"; |
| break; |
| case FeatureType::kFullscreenMagnifier: |
| feature_duration_metric += "CrosScreenMagnifier"; |
| break; |
| case FeatureType::kHighContrast: |
| feature_duration_metric += "CrosHighContrast"; |
| break; |
| case FeatureType::kLargeCursor: |
| feature_duration_metric += "CrosLargeCursor"; |
| break; |
| case FeatureType::kLiveCaption: |
| feature_duration_metric += "CrosLiveCaption"; |
| break; |
| case FeatureType::kMonoAudio: |
| feature_duration_metric += "CrosMonoAudio"; |
| break; |
| case FeatureType::kMouseKeys: |
| feature_duration_metric += "CrosMouseKeys"; |
| break; |
| case FeatureType::kReducedAnimations: |
| feature_duration_metric += "CrosReducedAnimations"; |
| break; |
| case FeatureType::kSelectToSpeak: |
| feature_duration_metric += "CrosSelectToSpeak"; |
| break; |
| case FeatureType::kSlowKeys: |
| feature_duration_metric += "CrosSlowKeys"; |
| break; |
| case FeatureType::kSpokenFeedback: |
| feature_duration_metric += "CrosSpokenFeedback"; |
| break; |
| case FeatureType::kStickyKeys: |
| feature_duration_metric += "CrosStickyKeys"; |
| break; |
| case FeatureType::kSwitchAccess: |
| feature_duration_metric += "CrosSwitchAccess"; |
| break; |
| case FeatureType::kVirtualKeyboard: |
| feature_duration_metric += "CrosVirtualKeyboard"; |
| break; |
| default: |
| return; |
| } |
| |
| feature_duration_metric += ".SessionDuration"; |
| |
| base::TimeDelta duration = base::Time::Now() - enabled_time_; |
| base::UmaHistogramCustomCounts(feature_duration_metric, duration.InSeconds(), |
| 1, base::Days(1) / base::Seconds(1), 100); |
| |
| // Reset enabled time as this duration is now logged and accounted for. |
| enabled_time_ = base::Time(); |
| } |
| |
| void AccessibilityController::Feature::SetConflictingFeature( |
| FeatureType feature) { |
| DCHECK_EQ(conflicting_feature_, FeatureType::kNoConflictingFeature); |
| conflicting_feature_ = feature; |
| } |
| |
| void AccessibilityController::Feature::ObserveConflictingFeature() { |
| std::string conflicting_pref_name = ""; |
| switch (conflicting_feature_) { |
| case A11yFeatureType::kSpokenFeedback: |
| conflicting_pref_name = prefs::kAccessibilitySpokenFeedbackEnabled; |
| break; |
| default: |
| // No other features are used as conflicting features at the moment, |
| // but this could be populated if needed in the future. |
| NOTREACHED() << "No pref name for conflicting feature " |
| << static_cast<int>(conflicting_feature_); |
| } |
| pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>(); |
| pref_change_registrar_->Init(owner_->active_user_prefs_); |
| pref_change_registrar_->Add( |
| conflicting_pref_name, |
| base::BindRepeating(&AccessibilityController::Feature::UpdateFromPref, |
| base::Unretained(this))); |
| } |
| |
| AccessibilityController::FeatureWithDialog::FeatureWithDialog( |
| FeatureType type, |
| const std::string& pref_name, |
| const gfx::VectorIcon* icon, |
| const int name_resource_id, |
| const bool toggleable_in_quicksettings, |
| const std::string& dialog_pref, |
| AccessibilityController* controller) |
| : AccessibilityController::Feature(type, |
| pref_name, |
| icon, |
| name_resource_id, |
| toggleable_in_quicksettings, |
| controller), |
| dialog_pref_(dialog_pref) {} |
| AccessibilityController::FeatureWithDialog::~FeatureWithDialog() = default; |
| |
| void AccessibilityController::FeatureWithDialog::SetDialogAccepted() { |
| PrefService* prefs = owner_->active_user_prefs_; |
| if (!prefs) { |
| return; |
| } |
| prefs->SetBoolean(dialog_pref_, true); |
| prefs->CommitPendingWrite(); |
| } |
| |
| bool AccessibilityController::FeatureWithDialog::WasDialogAccepted() const { |
| PrefService* prefs = owner_->active_user_prefs_; |
| DCHECK(prefs); |
| return prefs->GetBoolean(dialog_pref_); |
| } |
| |
| // static |
| AccessibilityController* AccessibilityController::Get() { |
| return g_instance; |
| } |
| |
| AccessibilityController::AccessibilityController() |
| : autoclick_delay_(AutoclickController::GetDefaultAutoclickDelay()) { |
| DCHECK_EQ(nullptr, g_instance); |
| g_instance = this; |
| |
| Shell::Get()->session_controller()->AddObserver(this); |
| display::Screen::Get()->AddObserver(this); |
| CreateAccessibilityFeatures(); |
| |
| accessibility_notification_controller_ = |
| std::make_unique<AccessibilityNotificationController>(); |
| |
| flash_screen_controller_ = std::make_unique<FlashScreenController>(); |
| } |
| |
| AccessibilityController::~AccessibilityController() { |
| floating_menu_controller_.reset(); |
| accessibility_notification_controller_.reset(); |
| |
| DCHECK_EQ(this, g_instance); |
| g_instance = nullptr; |
| } |
| |
| void AccessibilityController::CreateAccessibilityFeatures() { |
| // First, build all features with dialog. |
| std::map<FeatureType, std::string> dialogs; |
| for (auto dialog_data : kFeatureDialogs) { |
| dialogs[dialog_data.type] = dialog_data.pref; |
| } |
| for (auto feature_data : kFeatures) { |
| size_t feature_index = static_cast<size_t>(feature_data.type); |
| DCHECK(!features_[feature_index]); |
| auto it = dialogs.find(feature_data.type); |
| if (it == dialogs.end()) { |
| features_[feature_index] = std::make_unique<Feature>( |
| feature_data.type, feature_data.pref, feature_data.icon, |
| feature_data.name_resource_id, |
| feature_data.toggleable_in_quicksettings, this); |
| } else { |
| features_[feature_index] = std::make_unique<FeatureWithDialog>( |
| feature_data.type, feature_data.pref, feature_data.icon, |
| feature_data.name_resource_id, |
| feature_data.toggleable_in_quicksettings, it->second, this); |
| } |
| if (feature_data.conflicting_feature != |
| FeatureType::kNoConflictingFeature) { |
| features_[feature_index]->SetConflictingFeature( |
| feature_data.conflicting_feature); |
| } |
| } |
| } |
| |
| // static |
| void AccessibilityController::RegisterProfilePrefs( |
| PrefRegistrySimple* registry) { |
| // |
| // Non-syncable prefs. |
| // |
| // These prefs control whether an accessibility feature is enabled. They are |
| // not synced due to the impact they have on device interaction. |
| registry->RegisterBooleanPref(prefs::kAccessibilityAutoclickEnabled, false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityBounceKeysEnabled, false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityCursorColorEnabled, false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityCaretHighlightEnabled, |
| false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityCursorHighlightEnabled, |
| false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityDictationEnabled, false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityFloatingMenuEnabled, |
| false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityFocusHighlightEnabled, |
| false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityHighContrastEnabled, |
| false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityLargeCursorEnabled, false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityMonoAudioEnabled, false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityMouseKeysEnabled, false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityScreenMagnifierEnabled, |
| false); |
| registry->RegisterBooleanPref(prefs::kAccessibilitySelectToSpeakEnabled, |
| false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityShortcutsEnabled, true); |
| registry->RegisterBooleanPref(prefs::kAccessibilitySlowKeysEnabled, false); |
| registry->RegisterBooleanPref(prefs::kAccessibilitySpokenFeedbackEnabled, |
| false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityStickyKeysEnabled, false); |
| registry->RegisterBooleanPref(prefs::kAccessibilitySwitchAccessEnabled, |
| false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityVirtualKeyboardEnabled, |
| false); |
| 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, |
| static_cast<int>(DisableTouchpadMode::kNever)); |
| registry->RegisterIntegerPref(prefs::kAccessibilityCursorColor, |
| ui::kDefaultCursorColor); |
| |
| // Not syncable because it might change depending on application locale, |
| // user settings, and because different languages can cause speech recognition |
| // files to download. |
| registry->RegisterStringPref(prefs::kAccessibilityDictationLocale, |
| std::string()); |
| registry->RegisterDictionaryPref( |
| prefs::kAccessibilityDictationLocaleOfflineNudge); |
| |
| // A pref in this list is associated with accepting for the first time, |
| // enabling of some pref above. Non-syncable like all of the above prefs. |
| registry->RegisterBooleanPref( |
| prefs::kHighContrastAcceleratorDialogHasBeenAccepted, false); |
| registry->RegisterBooleanPref( |
| prefs::kScreenMagnifierAcceleratorDialogHasBeenAccepted, false); |
| registry->RegisterBooleanPref( |
| prefs::kDockedMagnifierAcceleratorDialogHasBeenAccepted, false); |
| registry->RegisterBooleanPref( |
| prefs::kDictationAcceleratorDialogHasBeenAccepted, false); |
| registry->RegisterBooleanPref( |
| prefs::kSelectToSpeakAcceleratorDialogHasBeenAccepted, false); |
| registry->RegisterBooleanPref( |
| prefs::kDictationDlcSuccessNotificationHasBeenShown, false); |
| registry->RegisterBooleanPref( |
| prefs::kDictationDlcOnlyPumpkinDownloadedNotificationHasBeenShown, false); |
| registry->RegisterBooleanPref( |
| prefs::kDictationDlcOnlySodaDownloadedNotificationHasBeenShown, false); |
| registry->RegisterBooleanPref( |
| prefs::kDictationNoDlcsDownloadedNotificationHasBeenShown, false); |
| registry->RegisterBooleanPref( |
| prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted2, false); |
| registry->RegisterBooleanPref(prefs::kShouldAlwaysShowAccessibilityMenu, |
| false); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityFaceGazeAcceleratorDialogHasBeenAccepted, false); |
| registry->RegisterBooleanPref( |
| prefs::kFaceGazeDlcSuccessNotificationHasBeenShown, false); |
| registry->RegisterBooleanPref( |
| prefs::kFaceGazeDlcFailureNotificationHasBeenShown, false); |
| |
| registry->RegisterBooleanPref(prefs::kAccessibilityColorCorrectionEnabled, |
| false); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityColorCorrectionHasBeenSetup, false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityFlashNotificationsEnabled, |
| false); |
| |
| registry->RegisterBooleanPref(prefs::kAccessibilityReducedAnimationsEnabled, |
| false); |
| |
| // TODO(b/266816160): Make ChromeVox prefs are syncable, to so that ChromeOS |
| // backs up users' ChromeVox settings and reflects across their devices. |
| registry->RegisterBooleanPref(prefs::kAccessibilityChromeVoxAutoRead, false); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityChromeVoxAnnounceDownloadNotifications, true); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityChromeVoxAnnounceRichTextAttributes, true); |
| registry->RegisterStringPref(prefs::kAccessibilityChromeVoxAudioStrategy, |
| kDefaultAccessibilityChromeVoxAudioStrategy); |
| registry->RegisterBooleanPref(prefs::kAccessibilityChromeVoxBrailleSideBySide, |
| true); |
| registry->RegisterStringPref(prefs::kAccessibilityChromeVoxBrailleTable, |
| kDefaultAccessibilityChromeVoxBrailleTable); |
| registry->RegisterStringPref(prefs::kAccessibilityChromeVoxBrailleTable6, |
| kDefaultAccessibilityChromeVoxBrailleTable6); |
| registry->RegisterStringPref(prefs::kAccessibilityChromeVoxBrailleTable8, |
| kDefaultAccessibilityChromeVoxBrailleTable8); |
| registry->RegisterStringPref(prefs::kAccessibilityChromeVoxBrailleTableType, |
| kDefaultAccessibilityChromeVoxBrailleTableType); |
| registry->RegisterBooleanPref(prefs::kAccessibilityChromeVoxBrailleWordWrap, |
| true); |
| registry->RegisterStringPref(prefs::kAccessibilityChromeVoxCapitalStrategy, |
| kDefaultAccessibilityChromeVoxCapitalStrategy); |
| registry->RegisterStringPref( |
| prefs::kAccessibilityChromeVoxCapitalStrategyBackup, |
| kDefaultAccessibilityChromeVoxCapitalStrategyBackup); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityChromeVoxEnableBrailleLogging, false); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityChromeVoxEnableEarconLogging, false); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityChromeVoxEnableEventStreamLogging, false); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityChromeVoxEnableSpeechLogging, false); |
| registry->RegisterDictionaryPref( |
| prefs::kAccessibilityChromeVoxEventStreamFilters, base::Value::Dict()); |
| registry->RegisterBooleanPref(prefs::kAccessibilityChromeVoxLanguageSwitching, |
| false); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityChromeVoxMenuBrailleCommands, false); |
| registry->RegisterStringPref( |
| prefs::kAccessibilityChromeVoxNumberReadingStyle, |
| kDefaultAccessibilityChromeVoxNumberReadingStyle); |
| registry->RegisterStringPref( |
| prefs::kAccessibilityChromeVoxPreferredBrailleDisplayAddress, |
| kDefaultAccessibilityChromeVoxPreferredBrailleDisplayAddress); |
| registry->RegisterIntegerPref(prefs::kAccessibilityChromeVoxPunctuationEcho, |
| kDefaultAccessibilityChromeVoxPunctuationEcho); |
| registry->RegisterBooleanPref(prefs::kAccessibilityChromeVoxSmartStickyMode, |
| true); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityChromeVoxSpeakTextUnderMouse, false); |
| registry->RegisterBooleanPref(prefs::kAccessibilityChromeVoxUsePitchChanges, |
| true); |
| registry->RegisterBooleanPref(prefs::kAccessibilityChromeVoxUseVerboseMode, |
| true); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityChromeVoxVirtualBrailleColumns, |
| kDefaultAccessibilityChromeVoxVirtualBrailleColumns); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityChromeVoxVirtualBrailleRows, |
| kDefaultAccessibilityChromeVoxVirtualBrailleRows); |
| registry->RegisterStringPref(prefs::kAccessibilityChromeVoxVoiceName, |
| kDefaultAccessibilityChromeVoxVoiceName); |
| |
| // TODO(b/259372916): Enable sync for Mouse Keys settings before launch. |
| registry->RegisterDoublePref(prefs::kAccessibilityMouseKeysAcceleration, |
| MouseKeysController::kDefaultAcceleration); |
| registry->RegisterDoublePref(prefs::kAccessibilityMouseKeysMaxSpeed, |
| MouseKeysController::kDefaultMaxSpeed); |
| registry->RegisterBooleanPref(prefs::kAccessibilityMouseKeysUsePrimaryKeys, |
| true); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityMouseKeysDominantHand, |
| static_cast<int>(MouseKeysDominantHand::kRightHandDominant)); |
| |
| // |
| // Syncable prefs. |
| // |
| // These prefs pertain to specific features. They are synced to preserve |
| // behaviors tied to user accounts once that user enables a feature. |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityAutoclickDelayMs, kDefaultAutoclickDelayMs, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityAutoclickEventType, |
| static_cast<int>(kDefaultAutoclickEventType), |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityAutoclickRevertToLeftClick, true, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityAutoclickStabilizePosition, false, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityAutoclickMovementThreshold, |
| kDefaultAutoclickMovementThreshold, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityAutoclickMenuPosition, |
| static_cast<int>(kDefaultAutoclickMenuPosition), |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| |
| if (::features::IsAccessibilityBounceKeysEnabled()) { |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityBounceKeysDelayMs, |
| kDefaultAccessibilityBounceKeysDelay.InMilliseconds(), |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| } |
| |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityFloatingMenuPosition, |
| static_cast<int>(kDefaultFloatingMenuPosition), |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| |
| registry->RegisterIntegerPref(prefs::kAccessibilityLargeCursorDipSize, |
| kDefaultLargeCursorSize); |
| |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityScreenMagnifierMouseFollowingMode, |
| static_cast<int>(MagnifierMouseFollowingMode::kEdge), |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityScreenMagnifierFocusFollowingEnabled, true, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterDoublePref(prefs::kAccessibilityScreenMagnifierScale, |
| std::numeric_limits<double>::min()); |
| if (::features::IsAccessibilitySlowKeysEnabled()) { |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilitySlowKeysDelayMs, |
| kDefaultAccessibilitySlowKeysDelay.InMilliseconds(), |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| } |
| registry->RegisterDictionaryPref( |
| prefs::kAccessibilitySwitchAccessSelectDeviceKeyCodes, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterDictionaryPref( |
| prefs::kAccessibilitySwitchAccessNextDeviceKeyCodes, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterDictionaryPref( |
| prefs::kAccessibilitySwitchAccessPreviousDeviceKeyCodes, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilitySwitchAccessAutoScanEnabled, false, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilitySwitchAccessAutoScanSpeedMs, |
| kDefaultSwitchAccessAutoScanSpeed.InMilliseconds(), |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilitySwitchAccessAutoScanKeyboardSpeedMs, |
| kDefaultSwitchAccessAutoScanSpeed.InMilliseconds(), |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilitySwitchAccessPointScanSpeedDipsPerSecond, |
| kDefaultSwitchAccessPointScanSpeedDipsPerSecond, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityEnhancedNetworkVoicesInSelectToSpeakAllowed, |
| kDefaultAccessibilityEnhancedNetworkVoicesInSelectToSpeakAllowed, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilitySelectToSpeakBackgroundShading, |
| kDefaultAccessibilitySelectToSpeakBackgroundShading, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilitySelectToSpeakEnhancedNetworkVoices, |
| kDefaultAccessibilitySelectToSpeakEnhancedNetworkVoices, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilitySelectToSpeakEnhancedVoicesDialogShown, |
| kDefaultAccessibilitySelectToSpeakEnhancedVoicesDialogShown, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilitySelectToSpeakNavigationControls, |
| kDefaultAccessibilitySelectToSpeakNavigationControls, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilitySelectToSpeakVoiceSwitching, |
| kDefaultAccessibilitySelectToSpeakVoiceSwitching, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilitySelectToSpeakWordHighlight, |
| kDefaultAccessibilitySelectToSpeakWordHighlight, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterStringPref( |
| prefs::kAccessibilitySelectToSpeakEnhancedVoiceName, |
| kDefaultAccessibilitySelectToSpeakEnhancedVoiceName, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterStringPref( |
| prefs::kAccessibilitySelectToSpeakHighlightColor, |
| kDefaultAccessibilitySelectToSpeakHighlightColor, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterStringPref( |
| prefs::kAccessibilitySelectToSpeakVoiceName, |
| kDefaultAccessibilitySelectToSpeakVoiceName, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityColorVisionCorrectionAmount, 100, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityColorVisionCorrectionType, |
| ColorVisionCorrectionType::kDeuteranomaly, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityFaceGazeCursorSpeedUp, kDefaultFaceGazeCursorSpeed, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityFaceGazeCursorSpeedDown, kDefaultFaceGazeCursorSpeed, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityFaceGazeCursorSpeedLeft, kDefaultFaceGazeCursorSpeed, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityFaceGazeCursorSpeedRight, |
| kDefaultFaceGazeCursorSpeed, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityFaceGazeCursorUseAcceleration, |
| kDefaultFaceGazeCursorUseAcceleration, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterDictionaryPref( |
| prefs::kAccessibilityFaceGazeGesturesToKeyCombos, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterDictionaryPref( |
| prefs::kAccessibilityFaceGazeGesturesToMacros, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterDictionaryPref( |
| prefs::kAccessibilityFaceGazeGesturesToConfidence, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityFaceGazeCursorControlEnabled, true, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityFaceGazeActionsEnabled, true, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityFaceGazeAdjustSpeedSeparately, false, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityFaceGazeVelocityThreshold, |
| kDefaultFaceGazeVelocityThreshold, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityFaceGazePrecisionClick, false, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref( |
| prefs::kAccessibilityFaceGazePrecisionClickSpeedFactor, |
| kDefaultFaceGazePrecisionClickSpeedFactor, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| |
| if (::features::IsAccessibilityMagnifierFollowsChromeVoxEnabled()) { |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityMagnifierFollowsChromeVox, true, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| } |
| |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityMagnifierFollowsSts, true, |
| user_prefs::PrefRegistrySyncable::SYNCABLE_OS_PREF); |
| registry->RegisterIntegerPref(prefs::kAccessibilityCaretBlinkInterval, |
| kDefaultCaretBlinkIntervalMs); |
| |
| if (::features::IsAccessibilityFlashScreenFeatureEnabled()) { |
| registry->RegisterIntegerPref(prefs::kAccessibilityFlashNotificationsColor, |
| kDefaultFlashNotificationsColor); |
| } |
| |
| registry->RegisterBooleanPref( |
| prefs::kAccessibilityAlwaysShowScrollbarsEnabled, false); |
| } |
| |
| void AccessibilityController::Shutdown() { |
| // Log metrics at shutdown. |
| for (auto& feature : features_) { |
| feature->LogDurationMetric(); |
| } |
| |
| display::Screen::Get()->RemoveObserver(this); |
| Shell::Get()->session_controller()->RemoveObserver(this); |
| |
| // Clean up any child windows and widgets that might be animating out. |
| dictation_bubble_controller_.reset(); |
| facegaze_bubble_controller_.reset(); |
| |
| for (auto& observer : observers_) { |
| observer.OnAccessibilityControllerShutdown(); |
| } |
| } |
| |
| bool AccessibilityController::HasDisplayRotationAcceleratorDialogBeenAccepted() |
| const { |
| return active_user_prefs_ && |
| active_user_prefs_->GetBoolean( |
| prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted2); |
| } |
| |
| void AccessibilityController:: |
| SetDisplayRotationAcceleratorDialogBeenAccepted() { |
| if (!active_user_prefs_) { |
| return; |
| } |
| active_user_prefs_->SetBoolean( |
| prefs::kDisplayRotationAcceleratorDialogHasBeenAccepted2, true); |
| active_user_prefs_->CommitPendingWrite(); |
| } |
| |
| void AccessibilityController::AddObserver(AccessibilityObserver* observer) { |
| observers_.AddObserver(observer); |
| } |
| |
| void AccessibilityController::RemoveObserver(AccessibilityObserver* observer) { |
| observers_.RemoveObserver(observer); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::GetFeature( |
| FeatureType type) const { |
| size_t feature_index = static_cast<size_t>(type); |
| DCHECK(features_[feature_index].get()); |
| return *features_[feature_index].get(); |
| } |
| |
| std::vector<AccessibilityController::Feature*> |
| AccessibilityController::GetEnabledFeaturesInQuickSettings() const { |
| std::vector<Feature*> enabled_features; |
| |
| for (auto& feature : features_) { |
| if (feature->enabled() && feature->toggleable_in_quicksettings()) { |
| enabled_features.push_back(feature.get()); |
| } |
| } |
| return enabled_features; |
| } |
| |
| base::WeakPtr<AccessibilityController> AccessibilityController::GetWeakPtr() { |
| return weak_ptr_factory_.GetWeakPtr(); |
| } |
| |
| AccessibilityController::Feature& |
| AccessibilityController::always_show_scrollbar() const { |
| return GetFeature(FeatureType::kAlwaysShowScrollbar); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::autoclick() const { |
| return GetFeature(FeatureType::kAutoclick); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::bounce_keys() const { |
| return GetFeature(FeatureType::kBounceKeys); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::caret_highlight() |
| const { |
| return GetFeature(FeatureType::kCaretHighlight); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::cursor_highlight() |
| const { |
| return GetFeature(FeatureType::kCursorHighlight); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::cursor_color() |
| const { |
| return GetFeature(FeatureType::kCursorColor); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::dictation() const { |
| return GetFeature(FeatureType::kDictation); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::disable_touchpad() |
| const { |
| return GetFeature(FeatureType::kDisableTouchpad); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::color_correction() |
| const { |
| return GetFeature(FeatureType::kColorCorrection); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::face_gaze() const { |
| return GetFeature(FeatureType::kFaceGaze); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::flash_notifications() |
| const { |
| return GetFeature(FeatureType::kFlashNotifications); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::focus_highlight() |
| const { |
| return GetFeature(FeatureType::kFocusHighlight); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::floating_menu() |
| const { |
| return GetFeature(FeatureType::kFloatingMenu); |
| } |
| |
| AccessibilityController::FeatureWithDialog& |
| AccessibilityController::fullscreen_magnifier() const { |
| return static_cast<FeatureWithDialog&>( |
| GetFeature(FeatureType::kFullscreenMagnifier)); |
| } |
| |
| AccessibilityController::FeatureWithDialog& |
| AccessibilityController::docked_magnifier() const { |
| return static_cast<FeatureWithDialog&>( |
| GetFeature(FeatureType::kDockedMagnifier)); |
| } |
| |
| AccessibilityController::FeatureWithDialog& |
| AccessibilityController::high_contrast() const { |
| return static_cast<FeatureWithDialog&>( |
| GetFeature(FeatureType::kHighContrast)); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::large_cursor() |
| const { |
| return GetFeature(FeatureType::kLargeCursor); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::live_caption() |
| const { |
| return GetFeature(FeatureType::kLiveCaption); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::mono_audio() const { |
| return GetFeature(FeatureType::kMonoAudio); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::mouse_keys() const { |
| return GetFeature(FeatureType::kMouseKeys); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::reduced_animations() |
| const { |
| return GetFeature(FeatureType::kReducedAnimations); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::spoken_feedback() |
| const { |
| return GetFeature(FeatureType::kSpokenFeedback); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::select_to_speak() |
| const { |
| return GetFeature(FeatureType::kSelectToSpeak); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::slow_keys() const { |
| return GetFeature(FeatureType::kSlowKeys); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::sticky_keys() const { |
| return GetFeature(FeatureType::kStickyKeys); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::switch_access() |
| const { |
| return GetFeature(FeatureType::kSwitchAccess); |
| } |
| |
| AccessibilityController::Feature& AccessibilityController::virtual_keyboard() |
| const { |
| return GetFeature(FeatureType::kVirtualKeyboard); |
| } |
| |
| bool AccessibilityController::IsAutoclickSettingVisibleInTray() { |
| return autoclick().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForAutoclick() { |
| return autoclick().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsCaretHighlightSettingVisibleInTray() { |
| return caret_highlight().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForCaretHighlight() { |
| return caret_highlight().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsCursorHighlightSettingVisibleInTray() { |
| return cursor_highlight().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForCursorHighlight() { |
| return cursor_highlight().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsDictationSettingVisibleInTray() { |
| return dictation().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForDictation() { |
| return dictation().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsFaceGazeSettingVisibleInTray() { |
| // For managed accounts, we restrict the face control quick setting to |
| // signed-in profiles. If the device is on the login screen, locked, or in a |
| // kiosk app, we don't show the face control quick setting. |
| bool is_managed = |
| Shell::Get() |
| ->system_tray_model() |
| ->enterprise_domain() |
| ->management_device_mode() != ManagementDeviceMode::kNone; |
| if (is_managed) { |
| LoginStatus status = Shell::Get()->session_controller()->login_status(); |
| if (status == LoginStatus::NOT_LOGGED_IN || status == LoginStatus::LOCKED || |
| status == LoginStatus::KIOSK_APP) { |
| return false; |
| } |
| } |
| |
| return face_gaze().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForFaceGaze() { |
| return face_gaze().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsFocusHighlightSettingVisibleInTray() { |
| return focus_highlight().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForFocusHighlight() { |
| return focus_highlight().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsFullScreenMagnifierSettingVisibleInTray() { |
| return fullscreen_magnifier().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForFullScreenMagnifier() { |
| return fullscreen_magnifier().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsDockedMagnifierSettingVisibleInTray() { |
| return docked_magnifier().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForDockedMagnifier() { |
| return docked_magnifier().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsHighContrastSettingVisibleInTray() { |
| return high_contrast().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForHighContrast() { |
| return high_contrast().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsColorCorrectionSettingVisibleInTray() { |
| if (!color_correction().enabled() && |
| Shell::Get()->session_controller()->login_status() == |
| ash::LoginStatus::NOT_LOGGED_IN) { |
| // Don't allow users to enable this on not logged in profiles because it |
| // requires set-up in settings the first time it is run. |
| return false; |
| } |
| return color_correction().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForColorCorrection() { |
| return color_correction().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsLargeCursorSettingVisibleInTray() { |
| return large_cursor().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForLargeCursor() { |
| return large_cursor().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsLiveCaptionSettingVisibleInTray() { |
| return captions::IsLiveCaptionFeatureSupported() && |
| live_caption().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForLiveCaption() { |
| return captions::IsLiveCaptionFeatureSupported() && |
| live_caption().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsMonoAudioSettingVisibleInTray() { |
| return mono_audio().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForMonoAudio() { |
| return mono_audio().IsEnterpriseIconVisible(); |
| } |
| |
| void AccessibilityController::SetSpokenFeedbackEnabled( |
| bool enabled, |
| AccessibilityNotificationVisibility notify) { |
| spoken_feedback().SetEnabled(enabled); |
| |
| // Value could be left unchanged because of higher-priority pref source, eg. |
| // policy. See crbug.com/953245. |
| const bool actual_enabled = active_user_prefs_->GetBoolean( |
| prefs::kAccessibilitySpokenFeedbackEnabled); |
| |
| A11yNotificationType type = A11yNotificationType::kNone; |
| if (enabled && actual_enabled && notify == A11Y_NOTIFICATION_SHOW) { |
| type = A11yNotificationType::kSpokenFeedbackEnabled; |
| } |
| ShowAccessibilityNotification(A11yNotificationWrapper( |
| type, kNotificationId, std::vector<std::u16string>())); |
| |
| if (::features::IsAccessibilityManifestV3EnabledForChromeVox() && |
| accessibility_event_rewriter_ && !enabled) { |
| // SetSpokenFeedbackMv3KeyHandlingEnabled(true) is called once the |
| // ChromeVox service worker is ready to receive key events |
| // (after the service worker starts and listeners are registered). |
| // SetSpokenFeedbackMv3KeyHandlingEnabled(false) needs to be called |
| // here (as opposed to the extension) to ensure that mv3 key handling |
| // is only enabled if we're guaranteed a response from the extension. |
| accessibility_event_rewriter_->SetSpokenFeedbackMv3KeyHandlingEnabled( |
| false); |
| } |
| } |
| |
| bool AccessibilityController::IsSpokenFeedbackSettingVisibleInTray() { |
| return spoken_feedback().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForSpokenFeedback() { |
| return spoken_feedback().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsSelectToSpeakSettingVisibleInTray() { |
| return select_to_speak().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForSelectToSpeak() { |
| return select_to_speak().IsEnterpriseIconVisible(); |
| } |
| |
| 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) { |
| return; |
| } |
| |
| EnableInternalTouchpad(); |
| } |
| |
| void AccessibilityController::RecordSelectToSpeakSpeechDuration( |
| SelectToSpeakState old_state, |
| SelectToSpeakState new_state) { |
| if (new_state != SelectToSpeakState::kSelectToSpeakStateSpeaking && |
| select_to_speak_speech_start_time_ == base::Time()) { |
| select_to_speak_speech_start_time_ = base::Time::Now(); |
| } |
| if (old_state != SelectToSpeakState::kSelectToSpeakStateSpeaking && |
| new_state != old_state && |
| select_to_speak_speech_start_time_ != base::Time()) { |
| base::TimeDelta duration = |
| base::Time::Now() - select_to_speak_speech_start_time_; |
| base::UmaHistogramCustomCounts( |
| "Accessibility.CrosSelectToSpeak.SpeechDuration", duration.InSeconds(), |
| /*min=*/1, /*max=*/base::Minutes(20) / base::Seconds(1), |
| /*buckets=*/100); |
| select_to_speak_speech_start_time_ = base::Time(); |
| } |
| } |
| |
| void AccessibilityController::SetSelectToSpeakState(SelectToSpeakState state) { |
| RecordSelectToSpeakSpeechDuration(select_to_speak_state_, state); |
| select_to_speak_state_ = state; |
| |
| // Forward the state change event to select_to_speak_event_handler_. |
| // The extension may have requested that the handler enter SELECTING state. |
| // Prepare to start capturing events from stylus, mouse or touch. |
| if (select_to_speak_event_handler_) { |
| select_to_speak_event_handler_->SetSelectToSpeakStateSelecting( |
| state == SelectToSpeakState::kSelectToSpeakStateSelecting); |
| } |
| NotifyAccessibilityStatusChanged(); |
| } |
| |
| void AccessibilityController::SetSelectToSpeakEventHandlerDelegate( |
| SelectToSpeakEventHandlerDelegate* delegate) { |
| select_to_speak_event_handler_delegate_ = delegate; |
| MaybeCreateSelectToSpeakEventHandler(); |
| } |
| |
| SelectToSpeakState AccessibilityController::GetSelectToSpeakState() const { |
| return select_to_speak_state_; |
| } |
| |
| void AccessibilityController::ShowSelectToSpeakPanel(const gfx::Rect& anchor, |
| bool is_paused, |
| double speech_rate) { |
| if (!select_to_speak_bubble_controller_) { |
| select_to_speak_bubble_controller_ = |
| std::make_unique<SelectToSpeakMenuBubbleController>(); |
| } |
| select_to_speak_bubble_controller_->Show(anchor, is_paused, speech_rate); |
| } |
| |
| void AccessibilityController::HideSelectToSpeakPanel() { |
| if (!select_to_speak_bubble_controller_) { |
| return; |
| } |
| select_to_speak_bubble_controller_->Hide(); |
| } |
| |
| void AccessibilityController::OnSelectToSpeakPanelAction( |
| SelectToSpeakPanelAction action, |
| double value) { |
| if (!client_) { |
| return; |
| } |
| client_->OnSelectToSpeakPanelAction(action, value); |
| } |
| |
| bool AccessibilityController::IsSwitchAccessRunning() const { |
| return switch_access().enabled() || switch_access_disable_dialog_showing_; |
| } |
| |
| bool AccessibilityController::IsSwitchAccessSettingVisibleInTray() { |
| // Switch Access cannot be enabled on the sign-in page because there is no way |
| // to configure switches while the device is locked. |
| if (!switch_access().enabled() && |
| Shell::Get()->session_controller()->login_status() == |
| ash::LoginStatus::NOT_LOGGED_IN) { |
| return false; |
| } |
| return switch_access().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForSwitchAccess() { |
| return switch_access().IsEnterpriseIconVisible(); |
| } |
| |
| void AccessibilityController::SetAccessibilityEventRewriter( |
| AccessibilityEventRewriter* accessibility_event_rewriter) { |
| accessibility_event_rewriter_ = accessibility_event_rewriter; |
| } |
| |
| void AccessibilityController::SetDisableTouchpadEventRewriter( |
| DisableTouchpadEventRewriter* rewriter) { |
| disable_touchpad_event_rewriter_ = rewriter; |
| } |
| |
| void AccessibilityController::EnableInternalTouchpad() { |
| active_user_prefs_->SetInteger(prefs::kAccessibilityDisableTrackpadMode, |
| static_cast<int>(DisableTouchpadMode::kNever)); |
| } |
| |
| void AccessibilityController::SetFilterKeysEventRewriter( |
| FilterKeysEventRewriter* rewriter) { |
| filter_keys_event_rewriter_ = rewriter; |
| } |
| |
| void AccessibilityController::HideSwitchAccessBackButton() { |
| if (IsSwitchAccessRunning()) { |
| switch_access_bubble_controller_->HideBackButton(); |
| } |
| } |
| |
| void AccessibilityController::HideSwitchAccessMenu() { |
| if (IsSwitchAccessRunning()) { |
| switch_access_bubble_controller_->HideMenuBubble(); |
| } |
| } |
| |
| void AccessibilityController::ShowSwitchAccessBackButton( |
| const gfx::Rect& anchor) { |
| switch_access_bubble_controller_->ShowBackButton(anchor); |
| } |
| |
| void AccessibilityController::ShowSwitchAccessMenu( |
| const gfx::Rect& anchor, |
| std::vector<std::string> actions_to_show) { |
| switch_access_bubble_controller_->ShowMenu(anchor, actions_to_show); |
| } |
| |
| bool AccessibilityController::IsPointScanEnabled() { |
| return point_scan_controller_.get() && |
| point_scan_controller_->IsPointScanEnabled(); |
| } |
| |
| void AccessibilityController::StartPointScan() { |
| point_scan_controller_->Start(); |
| } |
| |
| void AccessibilityController::SetA11yOverrideWindow( |
| aura::Window* a11y_override_window) { |
| if (client_) { |
| client_->SetA11yOverrideWindow(a11y_override_window); |
| } |
| } |
| |
| void AccessibilityController::StopPointScan() { |
| if (point_scan_controller_) { |
| point_scan_controller_->HideAll(); |
| } |
| } |
| |
| void AccessibilityController::SetPointScanSpeedDipsPerSecond( |
| int point_scan_speed_dips_per_second) { |
| if (point_scan_controller_) { |
| point_scan_controller_->SetSpeedDipsPerSecond( |
| point_scan_speed_dips_per_second); |
| } |
| } |
| |
| void AccessibilityController::DisablePolicyRecommendationRestorerForTesting() { |
| Shell::Get()->policy_recommendation_restorer()->DisableForTesting(); |
| } |
| |
| bool AccessibilityController::IsStickyKeysSettingVisibleInTray() { |
| return sticky_keys().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForStickyKeys() { |
| return sticky_keys().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsReducedAnimationsSettingVisibleInTray() { |
| if (!::features::IsAccessibilityReducedAnimationsInKioskEnabled()) { |
| return false; |
| } |
| |
| // Only visible in kiosk mode. |
| if (!Shell::Get()->session_controller()->IsRunningInAppMode()) { |
| return false; |
| } |
| return reduced_animations().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForReducedAnimations() { |
| return reduced_animations().IsEnterpriseIconVisible(); |
| } |
| |
| bool AccessibilityController::IsVirtualKeyboardSettingVisibleInTray() { |
| return virtual_keyboard().IsVisibleInTray(); |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleForVirtualKeyboard() { |
| return virtual_keyboard().IsEnterpriseIconVisible(); |
| } |
| |
| void AccessibilityController::ShowFloatingMenuIfEnabled() { |
| if (floating_menu().enabled() && !floating_menu_controller_) { |
| floating_menu_controller_ = |
| std::make_unique<FloatingAccessibilityController>(this); |
| floating_menu_controller_->Show(GetFloatingMenuPosition()); |
| } else { |
| always_show_floating_menu_when_enabled_ = true; |
| } |
| } |
| |
| FloatingAccessibilityController* |
| AccessibilityController::GetFloatingMenuController() { |
| return floating_menu_controller_.get(); |
| } |
| |
| PointScanController* AccessibilityController::GetPointScanController() { |
| return point_scan_controller_.get(); |
| } |
| |
| void AccessibilityController::SetTabletModeShelfNavigationButtonsEnabled( |
| bool enabled) { |
| if (!active_user_prefs_) { |
| return; |
| } |
| |
| active_user_prefs_->SetBoolean( |
| prefs::kAccessibilityTabletModeShelfNavigationButtonsEnabled, enabled); |
| active_user_prefs_->CommitPendingWrite(); |
| } |
| |
| void AccessibilityController::TriggerAccessibilityAlert( |
| AccessibilityAlert alert) { |
| if (client_) { |
| client_->TriggerAccessibilityAlert(alert); |
| } |
| } |
| |
| void AccessibilityController::TriggerAccessibilityAlertWithMessage( |
| const std::string& message) { |
| if (client_) { |
| client_->TriggerAccessibilityAlertWithMessage(message); |
| } |
| } |
| |
| void AccessibilityController::PlayEarcon(Sound sound_key) { |
| if (client_) { |
| client_->PlayEarcon(sound_key); |
| } |
| } |
| |
| base::TimeDelta AccessibilityController::PlayShutdownSound() { |
| return client_ ? client_->PlayShutdownSound() : base::TimeDelta(); |
| } |
| |
| void AccessibilityController::HandleAccessibilityGesture( |
| ax::mojom::Gesture gesture, |
| gfx::PointF location) { |
| if (client_) { |
| client_->HandleAccessibilityGesture(gesture, location); |
| } |
| } |
| |
| void AccessibilityController::ToggleDictation() { |
| // Do nothing if dictation is not enabled. |
| if (!dictation().enabled()) { |
| return; |
| } |
| |
| if (client_) { |
| const bool is_active = client_->ToggleDictation(); |
| SetDictationActive(is_active); |
| if (is_active) { |
| Shell::Get()->OnDictationStarted(); |
| } else { |
| Shell::Get()->OnDictationEnded(); |
| } |
| } |
| } |
| |
| void AccessibilityController::SetDictationActive(bool is_active) { |
| dictation_active_ = is_active; |
| } |
| |
| void AccessibilityController::ToggleDictationFromSource( |
| DictationToggleSource source) { |
| base::RecordAction(base::UserMetricsAction("Accel_Toggle_Dictation")); |
| UMA_HISTOGRAM_ENUMERATION("Accessibility.CrosDictation.ToggleDictationMethod", |
| source); |
| |
| dictation().SetEnabled(true); |
| ToggleDictation(); |
| } |
| |
| void AccessibilityController::EnableSelectToSpeakWithDialog() { |
| if (select_to_speak().enabled()) { |
| return; |
| } |
| |
| if (active_user_prefs_ |
| ->FindPreference(prefs::kAccessibilitySelectToSpeakEnabled) |
| ->IsManaged() && |
| !active_user_prefs_->GetBoolean( |
| prefs::kAccessibilitySelectToSpeakEnabled)) { |
| // Don't show the dialog if Select to speak has been disabled by a policy. |
| return; |
| } |
| |
| if (active_user_prefs_->GetBoolean( |
| prefs::kSelectToSpeakAcceleratorDialogHasBeenAccepted)) { |
| // Enable Select to Speak if the confirmation dialog has been previously |
| // accepted. |
| OnSelectToSpeakKeyboardDialogAccepted(); |
| } else { |
| // Show the confirmation dialog if it hasn't been accepted yet. |
| ShowSelectToSpeakKeyboardDialog(); |
| } |
| } |
| |
| void AccessibilityController::EnableOrToggleDictationFromSource( |
| DictationToggleSource source) { |
| if (dictation().enabled()) { |
| ToggleDictationFromSource(source); |
| } else if (source == DictationToggleSource::kKeyboard) { |
| // Only allow direct-enabling of Dictation from the keyboard. Show the |
| // confirmation dialog if it hasn't been accepted yet. |
| if (active_user_prefs_->GetBoolean( |
| prefs::kDictationAcceleratorDialogHasBeenAccepted)) { |
| OnDictationKeyboardDialogAccepted(); |
| } else { |
| ShowDictationKeyboardDialog(); |
| } |
| } |
| } |
| |
| void AccessibilityController::ShowDictationKeyboardDialog() { |
| if (!client_) { |
| return; |
| } |
| |
| dictation_keyboard_dialog_showing_for_testing_ = true; |
| |
| std::string dictation_locale; |
| if (active_user_prefs_->GetString(prefs::kAccessibilityDictationLocale) |
| .empty()) { |
| dictation_locale = client_->GetDictationDefaultLocale(/*new_user=*/true); |
| } else { |
| dictation_locale = |
| active_user_prefs_->GetString(prefs::kAccessibilityDictationLocale); |
| } |
| |
| std::u16string display_locale = l10n_util::GetDisplayNameForLocale( |
| /*locale=*/dictation_locale, /*display_locale=*/dictation_locale, |
| /*is_for_ui=*/true); |
| std::vector<std::u16string> replacements{display_locale}; |
| std::u16string title = |
| l10n_util::GetStringUTF16(IDS_ASH_DICTATION_KEYBOARD_DIALOG_TITLE); |
| std::u16string description = |
| ::features::IsDictationOfflineAvailable() |
| ? l10n_util::GetStringFUTF16( |
| IDS_ASH_DICTATION_KEYBOARD_DIALOG_DESCRIPTION_SODA_AVAILABLE, |
| replacements, nullptr) |
| : l10n_util::GetStringFUTF16( |
| IDS_ASH_DICTATION_KEYBOARD_DIALOG_DESCRIPTION_SODA_NOT_AVAILABLE, |
| replacements, nullptr); |
| ShowConfirmationDialog( |
| title, description, l10n_util::GetStringUTF16(IDS_ASH_CONTINUE_BUTTON), |
| l10n_util::GetStringUTF16(IDS_APP_CANCEL), |
| base::BindOnce( |
| &AccessibilityController::OnDictationKeyboardDialogAccepted, |
| GetWeakPtr()), |
| base::BindOnce( |
| &AccessibilityController::OnDictationKeyboardDialogDismissed, |
| GetWeakPtr()), |
| base::BindOnce( |
| &AccessibilityController::OnDictationKeyboardDialogDismissed, |
| GetWeakPtr())); |
| } |
| |
| void AccessibilityController::OnDictationKeyboardDialogAccepted() { |
| dictation_keyboard_dialog_showing_for_testing_ = false; |
| active_user_prefs_->SetBoolean( |
| prefs::kDictationAcceleratorDialogHasBeenAccepted, true); |
| confirmation_dialog_.reset(); |
| base::RecordAction(base::UserMetricsAction("Accel_Enable_Dictation")); |
| dictation().SetEnabled(true); |
| } |
| |
| void AccessibilityController::OnDictationKeyboardDialogDismissed() { |
| dictation_keyboard_dialog_showing_for_testing_ = false; |
| } |
| |
| void AccessibilityController::ShowSelectToSpeakKeyboardDialog() { |
| if (!client_) { |
| return; |
| } |
| |
| std::u16string title = |
| l10n_util::GetStringUTF16(IDS_ASH_SELECT_TO_SPEAK_KEYBOARD_DIALOG_TITLE); |
| |
| std::u16string modifier_key; |
| if (Shell::Get()->keyboard_capability()->HasLauncherButtonOnAnyKeyboard()) { |
| modifier_key = l10n_util::GetStringUTF16(IDS_KSV_MODIFIER_LAUNCHER); |
| } else { |
| modifier_key = l10n_util::GetStringUTF16(IDS_KSV_MODIFIER_SEARCH); |
| } |
| std::u16string description = l10n_util::GetStringFUTF16( |
| IDS_ASH_SELECT_TO_SPEAK_KEYBOARD_DIALOG_DESCRIPTION, modifier_key); |
| ShowConfirmationDialog( |
| title, description, l10n_util::GetStringUTF16(IDS_ASH_CONTINUE_BUTTON), |
| l10n_util::GetStringUTF16(IDS_APP_CANCEL), |
| base::BindOnce( |
| &AccessibilityController::OnSelectToSpeakKeyboardDialogAccepted, |
| GetWeakPtr()), |
| base::BindOnce( |
| &AccessibilityController::OnSelectToSpeakKeyboardDialogDismissed, |
| GetWeakPtr()), |
| base::BindOnce( |
| &AccessibilityController::OnSelectToSpeakKeyboardDialogDismissed, |
| GetWeakPtr())); |
| } |
| |
| void AccessibilityController::OnSelectToSpeakKeyboardDialogAccepted() { |
| active_user_prefs_->SetBoolean( |
| prefs::kSelectToSpeakAcceleratorDialogHasBeenAccepted, true); |
| confirmation_dialog_.reset(); |
| select_to_speak().SetEnabled(true); |
| } |
| |
| void AccessibilityController::OnSelectToSpeakKeyboardDialogDismissed() { |
| confirmation_dialog_.reset(); |
| } |
| |
| void AccessibilityController::ShowDictationLanguageUpgradedNudge( |
| const std::string& dictation_locale, |
| const std::string& application_locale) { |
| const std::u16string language_name = l10n_util::GetDisplayNameForLocale( |
| dictation_locale, application_locale, /*is_for_ui=*/true); |
| const std::u16string body_text = l10n_util::GetStringFUTF16( |
| IDS_ASH_DICTATION_LANGUAGE_SUPPORTED_OFFLINE_NUDGE, language_name); |
| |
| AnchoredNudgeData nudge_data(kDictationLanguageUpgradedNudgeId, |
| NudgeCatalogName::kDictation, body_text); |
| AnchoredNudgeManager::Get()->Show(nudge_data); |
| } |
| |
| void AccessibilityController::SilenceSpokenFeedback() { |
| if (client_) { |
| client_->SilenceSpokenFeedback(); |
| } |
| } |
| |
| bool AccessibilityController::ShouldToggleSpokenFeedbackViaTouch() const { |
| return client_ && client_->ShouldToggleSpokenFeedbackViaTouch(); |
| } |
| |
| void AccessibilityController::PlaySpokenFeedbackToggleCountdown( |
| int tick_count) { |
| if (client_) { |
| client_->PlaySpokenFeedbackToggleCountdown(tick_count); |
| } |
| } |
| |
| bool AccessibilityController::IsEnterpriseIconVisibleInTrayMenu( |
| const std::string& path) { |
| return active_user_prefs_ && |
| active_user_prefs_->FindPreference(path)->IsManaged(); |
| } |
| |
| void AccessibilityController::SetClient(AccessibilityControllerClient* client) { |
| client_ = client; |
| } |
| |
| void AccessibilityController::SetDarkenScreen(bool darken) { |
| if (darken && !scoped_backlights_forced_off_) { |
| scoped_backlights_forced_off_ = |
| Shell::Get()->backlights_forced_off_setter()->ForceBacklightsOff(); |
| } else if (!darken && scoped_backlights_forced_off_) { |
| scoped_backlights_forced_off_.reset(); |
| } |
| } |
| |
| void AccessibilityController::BrailleDisplayStateChanged(bool connected) { |
| A11yNotificationType type = A11yNotificationType::kNone; |
| if (connected && spoken_feedback().enabled()) { |
| type = A11yNotificationType::kBrailleDisplayConnected; |
| } else if (connected && !spoken_feedback().enabled()) { |
| type = A11yNotificationType::kSpokenFeedbackBrailleEnabled; |
| } |
| |
| if (connected) { |
| SetSpokenFeedbackEnabled(true, A11Y_NOTIFICATION_NONE); |
| } |
| NotifyAccessibilityStatusChanged(); |
| |
| ShowAccessibilityNotification(A11yNotificationWrapper( |
| type, kNotificationId, std::vector<std::u16string>())); |
| } |
| |
| void AccessibilityController::SetFocusHighlightRect( |
| const gfx::Rect& bounds_in_screen) { |
| if (!accessibility_highlight_controller_) { |
| return; |
| } |
| accessibility_highlight_controller_->SetFocusHighlightRect(bounds_in_screen); |
| } |
| |
| void AccessibilityController::SetCaretBounds( |
| const gfx::Rect& bounds_in_screen) { |
| if (!accessibility_highlight_controller_) { |
| return; |
| } |
| accessibility_highlight_controller_->SetCaretBounds(bounds_in_screen); |
| } |
| |
| void AccessibilityController::SetAccessibilityPanelAlwaysVisible( |
| bool always_visible) { |
| GetLayoutManager()->SetAlwaysVisible(always_visible); |
| } |
| |
| void AccessibilityController::SetAccessibilityPanelBounds( |
| const gfx::Rect& bounds, |
| AccessibilityPanelState state) { |
| GetLayoutManager()->SetPanelBounds(bounds, state); |
| } |
| |
| void AccessibilityController::OnSigninScreenPrefServiceInitialized( |
| PrefService* prefs) { |
| // Make |kA11yPrefsForRecommendedValueOnSignin| observing recommended values |
| // on signin screen. See PolicyRecommendationRestorer. |
| PolicyRecommendationRestorer* policy_recommendation_restorer = |
| Shell::Get()->policy_recommendation_restorer(); |
| for (auto* const pref_name : kA11yPrefsForRecommendedValueOnSignin) { |
| policy_recommendation_restorer->ObservePref(pref_name); |
| } |
| |
| // Observe user settings. This must happen after PolicyRecommendationRestorer. |
| ObservePrefs(prefs); |
| } |
| |
| void AccessibilityController::OnActiveUserPrefServiceChanged( |
| PrefService* prefs) { |
| // This is guaranteed to be received after |
| // OnSigninScreenPrefServiceInitialized() so only copy the signin prefs if |
| // needed here. |
| CopySigninPrefsIfNeeded(active_user_prefs_, prefs); |
| ObservePrefs(prefs); |
| } |
| |
| void AccessibilityController::OnSessionStateChanged( |
| session_manager::SessionState state) { |
| if (state != SessionState::ACTIVE) { |
| // Log metrics for how long the features were enabled if needed. |
| for (auto& feature : features_) { |
| feature->LogDurationMetric(); |
| } |
| } |
| // Everything behind the lock screen is in |
| // kShellWindowId_NonLockScreenContainersContainer. If the session state is |
| // changed to block the user session due to the lock screen or similar, |
| // everything in that window should be made invisible for accessibility. |
| // This keeps a11y features from being able to access parts of the tree |
| // that are visibly hidden behind the lock screen. |
| aura::Window* container = |
| Shell::GetContainer(Shell::GetPrimaryRootWindow(), |
| kShellWindowId_NonLockScreenContainersContainer); |
| container->SetProperty( |
| ui::kAXConsiderInvisibleAndIgnoreChildren, |
| Shell::Get()->session_controller()->IsUserSessionBlocked()); |
| } |
| |
| AccessibilityEventRewriter* |
| AccessibilityController::GetAccessibilityEventRewriterForTest() { |
| return accessibility_event_rewriter_; |
| } |
| |
| DisableTouchpadEventRewriter* |
| AccessibilityController::GetDisableTouchpadEventRewriterForTest() { |
| return disable_touchpad_event_rewriter_; |
| } |
| |
| FilterKeysEventRewriter* |
| AccessibilityController::GetFilterKeysEventRewriterForTest() { |
| return filter_keys_event_rewriter_; |
| } |
| |
| void AccessibilityController::DisableAutoClickConfirmationDialogForTest() { |
| no_auto_click_confirmation_dialog_for_testing_ = true; |
| } |
| |
| void AccessibilityController:: |
| DisableSwitchAccessDisableConfirmationDialogTesting() { |
| no_switch_access_disable_confirmation_dialog_for_testing_ = true; |
| } |
| |
| void AccessibilityController::DisableSwitchAccessEnableNotificationTesting() { |
| skip_switch_access_notification_ = true; |
| } |
| |
| void AccessibilityController::OnDisplayTabletStateChanged( |
| display::TabletState state) { |
| if (spoken_feedback().enabled()) { |
| // Show accessibility notification when tablet mode transition is completed. |
| if (state == display::TabletState::kInTabletMode || |
| state == display::TabletState::kInClamshellMode) { |
| ShowAccessibilityNotification(A11yNotificationWrapper( |
| A11yNotificationType::kSpokenFeedbackEnabled, kNotificationId, |
| std::vector<std::u16string>())); |
| } |
| } |
| } |
| |
| void AccessibilityController::ObservePrefs(PrefService* prefs) { |
| DCHECK(prefs); |
| |
| active_user_prefs_ = prefs; |
| |
| // Watch for pref updates from webui settings and policy. |
| pref_change_registrar_ = std::make_unique<PrefChangeRegistrar>(); |
| pref_change_registrar_->Init(prefs); |
| |
| // It is safe to use base::Unreatined since we own pref_change_registrar. |
| for (const std::unique_ptr<Feature>& feature : features_) { |
| DCHECK(feature); |
| pref_change_registrar_->Add( |
| feature->pref_name(), |
| base::BindRepeating(&AccessibilityController::Feature::UpdateFromPref, |
| base::Unretained(feature.get()))); |
| if (feature->conflicting_feature() != FeatureType::kNoConflictingFeature) { |
| feature->ObserveConflictingFeature(); |
| } |
| // 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( |
| &AccessibilityController::UpdateAutoclickDelayFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityAutoclickEventType, |
| base::BindRepeating( |
| &AccessibilityController::UpdateAutoclickEventTypeFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityAutoclickRevertToLeftClick, |
| base::BindRepeating( |
| &AccessibilityController::UpdateAutoclickRevertToLeftClickFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityAutoclickStabilizePosition, |
| base::BindRepeating( |
| &AccessibilityController::UpdateAutoclickStabilizePositionFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityAutoclickMovementThreshold, |
| base::BindRepeating( |
| &AccessibilityController::UpdateAutoclickMovementThresholdFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityAutoclickMenuPosition, |
| base::BindRepeating( |
| &AccessibilityController::UpdateAutoclickMenuPositionFromPref, |
| base::Unretained(this))); |
| if (::features::IsAccessibilityBounceKeysEnabled()) { |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityBounceKeysDelayMs, |
| base::BindRepeating( |
| &AccessibilityController::UpdateBounceKeysDelayFromPref, |
| base::Unretained(this))); |
| } |
| if (::features::IsAccessibilitySlowKeysEnabled()) { |
| pref_change_registrar_->Add( |
| prefs::kAccessibilitySlowKeysDelayMs, |
| base::BindRepeating( |
| &AccessibilityController::UpdateSlowKeysDelayFromPref, |
| base::Unretained(this))); |
| } |
| if (::features::IsAccessibilityMouseKeysEnabled()) { |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityMouseKeysAcceleration, |
| base::BindRepeating( |
| &AccessibilityController::UpdateMouseKeysAccelerationFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityMouseKeysMaxSpeed, |
| base::BindRepeating( |
| &AccessibilityController::UpdateMouseKeysMaxSpeedFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityMouseKeysUsePrimaryKeys, |
| base::BindRepeating( |
| &AccessibilityController::UpdateMouseKeysUsePrimaryKeysFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityMouseKeysDominantHand, |
| base::BindRepeating( |
| &AccessibilityController::UpdateMouseKeysDominantHandFromPref, |
| base::Unretained(this))); |
| } |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityFloatingMenuPosition, |
| base::BindRepeating( |
| &AccessibilityController::UpdateFloatingMenuPositionFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityLargeCursorDipSize, |
| base::BindRepeating(&AccessibilityController::UpdateLargeCursorFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityShortcutsEnabled, |
| base::BindRepeating( |
| &AccessibilityController::UpdateShortcutsEnabledFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilitySwitchAccessSelectDeviceKeyCodes, |
| base::BindRepeating( |
| &AccessibilityController::UpdateSwitchAccessKeyCodesFromPref, |
| base::Unretained(this), SwitchAccessCommand::kSelect)); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilitySwitchAccessNextDeviceKeyCodes, |
| base::BindRepeating( |
| &AccessibilityController::UpdateSwitchAccessKeyCodesFromPref, |
| base::Unretained(this), SwitchAccessCommand::kNext)); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilitySwitchAccessPreviousDeviceKeyCodes, |
| base::BindRepeating( |
| &AccessibilityController::UpdateSwitchAccessKeyCodesFromPref, |
| base::Unretained(this), SwitchAccessCommand::kPrevious)); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilitySwitchAccessAutoScanEnabled, |
| base::BindRepeating( |
| &AccessibilityController::UpdateSwitchAccessAutoScanEnabledFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilitySwitchAccessAutoScanSpeedMs, |
| base::BindRepeating( |
| &AccessibilityController::UpdateSwitchAccessAutoScanSpeedFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilitySwitchAccessAutoScanKeyboardSpeedMs, |
| base::BindRepeating(&AccessibilityController:: |
| UpdateSwitchAccessAutoScanKeyboardSpeedFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilitySwitchAccessPointScanSpeedDipsPerSecond, |
| base::BindRepeating( |
| &AccessibilityController::UpdateSwitchAccessPointScanSpeedFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityTabletModeShelfNavigationButtonsEnabled, |
| base::BindRepeating(&AccessibilityController:: |
| UpdateTabletModeShelfNavigationButtonsFromPref, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityCursorColor, |
| base::BindRepeating(&AccessibilityController::UpdateCursorColorFromPrefs, |
| base::Unretained(this), /*notify*/ true)); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityColorVisionCorrectionAmount, |
| base::BindRepeating( |
| &AccessibilityController::UpdateColorCorrectionFromPrefs, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityColorVisionCorrectionType, |
| base::BindRepeating( |
| &AccessibilityController::UpdateColorCorrectionFromPrefs, |
| base::Unretained(this))); |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityCaretBlinkInterval, |
| base::BindRepeating( |
| &AccessibilityController::UpdateCaretBlinkIntervalFromPrefs, |
| base::Unretained(this))); |
| if (::features::IsAccessibilityFlashScreenFeatureEnabled()) { |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityFlashNotificationsColor, |
| base::BindRepeating( |
| &AccessibilityController::UpdateFlashNotificationsFromPrefs, |
| base::Unretained(this))); |
| } |
| if (::features::IsAccessibilityDisableTouchpadEnabled()) { |
| pref_change_registrar_->Add( |
| prefs::kAccessibilityDisableTrackpadMode, |
| base::BindRepeating( |
| &AccessibilityController::UpdateDisableTouchpadFromPrefs, |
| base::Unretained(this), /*notify*/ true)); |
| } |
| |
| for (const std::unique_ptr<Feature>& feature : features_) { |
| // Log previous duration and clear duration metric if necessary |
| // when the profile has changed. |
| feature->LogDurationMetric(); |
| |
| // Load current state. |
| feature->UpdateFromPref(); |
| } |
| |
| // Load current state of other prefs. |
| UpdateAutoclickDelayFromPref(); |
| UpdateAutoclickEventTypeFromPref(); |
| UpdateAutoclickRevertToLeftClickFromPref(); |
| UpdateAutoclickStabilizePositionFromPref(); |
| UpdateAutoclickMovementThresholdFromPref(); |
| UpdateAutoclickMenuPositionFromPref(); |
| if (::features::IsAccessibilityBounceKeysEnabled()) { |
| UpdateBounceKeysDelayFromPref(); |
| } |
| if (::features::IsAccessibilitySlowKeysEnabled()) { |
| UpdateSlowKeysDelayFromPref(); |
| } |
| if (::features::IsAccessibilityMouseKeysEnabled()) { |
| UpdateMouseKeysAccelerationFromPref(); |
| UpdateMouseKeysMaxSpeedFromPref(); |
| UpdateMouseKeysUsePrimaryKeysFromPref(); |
| UpdateMouseKeysDominantHandFromPref(); |
| } |
| UpdateFloatingMenuPositionFromPref(); |
| UpdateLargeCursorFromPref(); |
| UpdateCursorColorFromPrefs(/*notify=*/true); |
| UpdateShortcutsEnabledFromPref(); |
| UpdateTabletModeShelfNavigationButtonsFromPref(); |
| UpdateColorCorrectionFromPrefs(); |
| UpdateCaretBlinkIntervalFromPrefs(); |
| |
| UpdateFaceGazeFromPrefs(); |
| 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(); |
| } |
| if (::features::IsAccessibilityDisableTouchpadEnabled()) { |
| UpdateDisableTouchpadFromPrefs(/*notify=*/false); |
| } |
| } |
| |
| void AccessibilityController::UpdateAutoclickDelayFromPref() { |
| DCHECK(active_user_prefs_); |
| base::TimeDelta autoclick_delay = base::Milliseconds(int64_t{ |
| active_user_prefs_->GetInteger(prefs::kAccessibilityAutoclickDelayMs)}); |
| |
| if (autoclick_delay_ == autoclick_delay) { |
| return; |
| } |
| autoclick_delay_ = autoclick_delay; |
| |
| Shell::Get()->autoclick_controller()->SetAutoclickDelay(autoclick_delay_); |
| } |
| |
| void AccessibilityController::UpdateAutoclickEventTypeFromPref() { |
| Shell::Get()->autoclick_controller()->SetAutoclickEventType( |
| GetAutoclickEventType()); |
| } |
| |
| void AccessibilityController::SetAutoclickEventType( |
| AutoclickEventType event_type) { |
| if (!active_user_prefs_) { |
| return; |
| } |
| active_user_prefs_->SetInteger(prefs::kAccessibilityAutoclickEventType, |
| static_cast<int>(event_type)); |
| active_user_prefs_->CommitPendingWrite(); |
| Shell::Get()->autoclick_controller()->SetAutoclickEventType(event_type); |
| } |
| |
| AutoclickEventType AccessibilityController::GetAutoclickEventType() { |
| DCHECK(active_user_prefs_); |
| return static_cast<AutoclickEventType>( |
| active_user_prefs_->GetInteger(prefs::kAccessibilityAutoclickEventType)); |
| } |
| |
| void AccessibilityController::UpdateAutoclickRevertToLeftClickFromPref() { |
| DCHECK(active_user_prefs_); |
| bool revert_to_left_click = active_user_prefs_->GetBoolean( |
| prefs::kAccessibilityAutoclickRevertToLeftClick); |
| |
| Shell::Get()->autoclick_controller()->set_revert_to_left_click( |
| revert_to_left_click); |
| } |
| |
| void AccessibilityController::UpdateAutoclickStabilizePositionFromPref() { |
| DCHECK(active_user_prefs_); |
| bool stabilize_position = active_user_prefs_->GetBoolean( |
| prefs::kAccessibilityAutoclickStabilizePosition); |
| |
| Shell::Get()->autoclick_controller()->set_stabilize_click_position( |
| stabilize_position); |
| } |
| |
| void AccessibilityController::UpdateAutoclickMovementThresholdFromPref() { |
| DCHECK(active_user_prefs_); |
| int movement_threshold = active_user_prefs_->GetInteger( |
| prefs::kAccessibilityAutoclickMovementThreshold); |
| |
| Shell::Get()->autoclick_controller()->SetMovementThreshold( |
| movement_threshold); |
| } |
| |
| void AccessibilityController::UpdateAutoclickMenuPositionFromPref() { |
| Shell::Get()->autoclick_controller()->SetMenuPosition( |
| GetAutoclickMenuPosition()); |
| } |
| |
| void AccessibilityController::UpdateBounceKeysDelayFromPref() { |
| if (!filter_keys_event_rewriter_) { |
| return; |
| } |
| DCHECK(active_user_prefs_); |
| base::TimeDelta delay = base::Milliseconds( |
| active_user_prefs_->GetInteger(prefs::kAccessibilityBounceKeysDelayMs)); |
| filter_keys_event_rewriter_->SetBounceKeysDelay(delay); |
| } |
| |
| void AccessibilityController::UpdateSlowKeysDelayFromPref() { |
| DCHECK(active_user_prefs_); |
| base::TimeDelta delay = base::Milliseconds( |
| active_user_prefs_->GetInteger(prefs::kAccessibilitySlowKeysDelayMs)); |
| input_method::InputMethodManager::Get()->GetImeKeyboard()->SetSlowKeysDelay( |
| delay); |
| } |
| |
| void AccessibilityController::UpdateMouseKeysAccelerationFromPref() { |
| DCHECK(active_user_prefs_); |
| double acceleration = |
| active_user_prefs_->GetDouble(prefs::kAccessibilityMouseKeysAcceleration); |
| Shell::Get()->mouse_keys_controller()->set_acceleration(acceleration); |
| } |
| |
| void AccessibilityController::UpdateMouseKeysMaxSpeedFromPref() { |
| DCHECK(active_user_prefs_); |
| double max_speed = |
| active_user_prefs_->GetDouble(prefs::kAccessibilityMouseKeysMaxSpeed); |
| Shell::Get()->mouse_keys_controller()->SetMaxSpeed(max_speed); |
| } |
| |
| void AccessibilityController::UpdateMouseKeysUsePrimaryKeysFromPref() { |
| DCHECK(active_user_prefs_); |
| bool value = active_user_prefs_->GetBoolean( |
| prefs::kAccessibilityMouseKeysUsePrimaryKeys); |
| Shell::Get()->mouse_keys_controller()->set_use_primary_keys(value); |
| } |
| |
| void AccessibilityController::UpdateMouseKeysDominantHandFromPref() { |
| DCHECK(active_user_prefs_); |
| MouseKeysDominantHand dominant_hand = |
| static_cast<MouseKeysDominantHand>(active_user_prefs_->GetInteger( |
| prefs::kAccessibilityMouseKeysDominantHand)); |
| Shell::Get()->mouse_keys_controller()->set_left_handed( |
| dominant_hand == MouseKeysDominantHand::kLeftHandDominant); |
| } |
| |
| void AccessibilityController::SetAutoclickMenuPosition( |
| FloatingMenuPosition position) { |
| if (!active_user_prefs_) { |
| return; |
| } |
| active_user_prefs_->SetInteger(prefs::kAccessibilityAutoclickMenuPosition, |
| static_cast<int>(position)); |
| active_user_prefs_->CommitPendingWrite(); |
| Shell::Get()->autoclick_controller()->SetMenuPosition(position); |
| } |
| |
| FloatingMenuPosition AccessibilityController::GetAutoclickMenuPosition() { |
| DCHECK(active_user_prefs_); |
| return static_cast<FloatingMenuPosition>(active_user_prefs_->GetInteger( |
| prefs::kAccessibilityAutoclickMenuPosition)); |
| } |
| |
| void AccessibilityController::RequestAutoclickScrollableBoundsForPoint( |
| const gfx::Point& point_in_screen) { |
| if (client_) { |
| client_->RequestAutoclickScrollableBoundsForPoint(point_in_screen); |
| } |
| } |
| |
| void AccessibilityController::MagnifierBoundsChanged( |
| const gfx::Rect& bounds_in_screen) { |
| if (client_) { |
| client_->MagnifierBoundsChanged(bounds_in_screen); |
| } |
| } |
| |
| void AccessibilityController::UpdateFloatingPanelBoundsIfNeeded() { |
| Shell* shell = Shell::Get(); |
| if (shell->accessibility_controller()->autoclick().enabled()) { |
| shell->autoclick_controller()->UpdateAutoclickMenuBoundsIfNeeded(); |
| } |
| if (shell->accessibility_controller()->sticky_keys().enabled()) { |
| shell->sticky_keys_controller()->UpdateStickyKeysOverlayBoundsIfNeeded(); |
| } |
| } |
| |
| void AccessibilityController::UpdateAutoclickMenuBoundsIfNeeded() { |
| Shell::Get()->autoclick_controller()->UpdateAutoclickMenuBoundsIfNeeded(); |
| } |
| |
| void AccessibilityController::HandleAutoclickScrollableBoundsFound( |
| const gfx::Rect& bounds_in_screen) { |
| Shell::Get()->autoclick_controller()->HandleAutoclickScrollableBoundsFound( |
| bounds_in_screen); |
| } |
| |
| void AccessibilityController::SetFloatingMenuPosition( |
| FloatingMenuPosition position) { |
| if (!active_user_prefs_) { |
| return; |
| } |
| active_user_prefs_->SetInteger(prefs::kAccessibilityFloatingMenuPosition, |
| static_cast<int>(position)); |
| active_user_prefs_->CommitPendingWrite(); |
| } |
| |
| void AccessibilityController::UpdateFloatingMenuPositionFromPref() { |
| if (floating_menu_controller_) { |
| floating_menu_controller_->SetMenuPosition(GetFloatingMenuPosition()); |
| } |
| } |
| |
| FloatingMenuPosition AccessibilityController::GetFloatingMenuPosition() { |
| DCHECK(active_user_prefs_); |
| return static_cast<FloatingMenuPosition>(active_user_prefs_->GetInteger( |
| prefs::kAccessibilityFloatingMenuPosition)); |
| } |
| |
| void AccessibilityController::UpdateLargeCursorFromPref() { |
| DCHECK(active_user_prefs_); |
| const bool enabled = large_cursor().enabled(); |
| const int size = enabled ? active_user_prefs_->GetInteger( |
| prefs::kAccessibilityLargeCursorDipSize) |
| : kDefaultLargeCursorSize; |
| |
| if (large_cursor_size_in_dip_ == size) { |
| return; |
| } |
| |
| large_cursor_size_in_dip_ = size; |
| |
| NotifyAccessibilityStatusChanged(); |
| |
| Shell* shell = Shell::Get(); |
| shell->cursor_manager()->SetCursorSize(enabled ? ui::CursorSize::kLarge |
| : ui::CursorSize::kNormal); |
| shell->SetLargeCursorSizeInDip(large_cursor_size_in_dip_); |
| shell->UpdateCursorCompositingEnabled(); |
| } |
| |
| void AccessibilityController::UpdateCursorColorFromPrefs(bool notify) { |
| DCHECK(active_user_prefs_); |
| |
| const bool enabled = |
| active_user_prefs_->GetBoolean(prefs::kAccessibilityCursorColorEnabled); |
| Shell* shell = Shell::Get(); |
| shell->SetCursorColor( |
| enabled |
| // Settings page only sends RGB now. Set alpha as full opaque. |
| ? SkColorSetA(active_user_prefs_->GetInteger( |
| prefs::kAccessibilityCursorColor), |
| 0xFF) |
| : ui::kDefaultCursorColor); |
| if (notify) { |
| NotifyAccessibilityStatusChanged(); |
| } |
| shell->UpdateCursorCompositingEnabled(); |
| } |
| |
| void AccessibilityController::UpdateFaceGazeFromPrefs() { |
| 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); |
| } |
| |
| const bool enabled = |
| active_user_prefs_->GetBoolean(prefs::kAccessibilityFaceGazeEnabled); |
| // 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() { |
| if (!::features::IsAccessibilityFlashScreenFeatureEnabled()) { |
| return; |
| } |
| flash_screen_controller_->set_enabled(active_user_prefs_->GetBoolean( |
| prefs::kAccessibilityFlashNotificationsEnabled)); |
| flash_screen_controller_->set_color(active_user_prefs_->GetInteger( |
| prefs::kAccessibilityFlashNotificationsColor)); |
| } |
| |
| void AccessibilityController::UpdateDisableTouchpadFromPrefs(bool notify) { |
| if (!disable_touchpad_event_rewriter_ || |
| !::features::IsAccessibilityDisableTouchpadEnabled()) { |
| return; |
| } |
| |
| if (notify) { |
| DisableTouchpadWithDialog(); |
| return; |
| } |
| |
| const DisableTouchpadMode touchpad_mode = static_cast<DisableTouchpadMode>( |
| active_user_prefs_->GetInteger(prefs::kAccessibilityDisableTrackpadMode)); |
| |
| if (touchpad_mode == DisableTouchpadMode::kAlways || |
| touchpad_mode == DisableTouchpadMode::kOnExternalMouseConnected) { |
| disable_touchpad_event_rewriter_->SetEnabled(true); |
| } |
| } |
| |
| void AccessibilityController::DisableTouchpadWithDialog() { |
| const DisableTouchpadMode touchpad_mode = static_cast<DisableTouchpadMode>( |
| active_user_prefs_->GetInteger(prefs::kAccessibilityDisableTrackpadMode)); |
| |
| switch (touchpad_mode) { |
| case DisableTouchpadMode::kAlways: |
| ShowDisableTouchpadDialog(); |
| break; |
| |
| case DisableTouchpadMode::kOnExternalMouseConnected: |
| if (Shell::Get() |
| ->input_device_settings_controller() |
| ->GetConnectedMice() |
| .size() > 0) { |
| ShowDisableTouchpadDialog(); |
| } |
| break; |
| |
| case DisableTouchpadMode::kNever: |
| active_user_prefs_->SetBoolean( |
| prefs::kAccessibilityDisableTrackpadEnabled, false); |
| message_center::MessageCenter* message_center = |
| message_center::MessageCenter::Get(); |
| message_center->RemoveNotification(kNotificationId, false /* by_user */); |
| ShowToast(AccessibilityToastType::kTouchpadDisabled); |
| disable_touchpad_event_rewriter_->SetEnabled(false); |
| break; |
| } |
| } |
| |
| void AccessibilityController::OnMouseConnected(const mojom::Mouse& mouse) { |
| ExternalDeviceConnected(); |
| } |
| |
| void AccessibilityController::OnTouchpadConnected( |
| const mojom::Touchpad& touchpad) { |
| ExternalDeviceConnected(); |
| } |
| |
| void AccessibilityController::ExternalDeviceConnected() { |
| if (!disable_touchpad_event_rewriter_) { |
| return; |
| } |
| |
| const DisableTouchpadMode touchpad_mode = static_cast<DisableTouchpadMode>( |
| active_user_prefs_->GetInteger(prefs::kAccessibilityDisableTrackpadMode)); |
| |
| const bool touchpad_disabled = disable_touchpad_event_rewriter_->IsEnabled(); |
| if (touchpad_mode == DisableTouchpadMode::kOnExternalMouseConnected && |
| !touchpad_disabled) { |
| DisableTouchpadWithDialog(); |
| } |
| } |
| |
| void AccessibilityController::ShowDisableTouchpadDialog() { |
| accessibility_notification_controller_->CancelToast(); |
| active_user_prefs_->SetBoolean(prefs::kAccessibilityDisableTrackpadEnabled, |
| true); |
| // The internal touchpad should be disabled before the user clicks "Accept", |
| // This is done to ensure the user can navigate with their keyboard. |
| disable_touchpad_event_rewriter_->SetEnabled(true); |
| const DisableTouchpadMode disable_touchpad_mode = |
| static_cast<DisableTouchpadMode>(active_user_prefs_->GetInteger( |
| prefs::kAccessibilityDisableTrackpadMode)); |
| const std::u16string title = |
| l10n_util::GetStringUTF16(IDS_ASH_DISABLE_TOUCHPAD_DIALOG_TITLE); |
| |
| // Construct the timeout message, leaving a placeholder for the countdown |
| // timer so that the string does not need to be completely rebuilt every |
| // timer tick. |
| constexpr char16_t kTimeoutPlaceHolder[] = u"$1"; |
| |
| const std::u16string description = |
| disable_touchpad_mode == DisableTouchpadMode::kAlways |
| ? l10n_util::GetStringFUTF16( |
| IDS_ASH_DISABLE_TOUCHPAD_DIALOG_DESCRIPTION, |
| kTimeoutPlaceHolder) |
| : l10n_util::GetStringFUTF16( |
| IDS_ASH_DISABLE_TOUCHPAD_DIALOG_EXTERNAL_MOUSE_DESCRIPTION, |
| kTimeoutPlaceHolder); |
| |
| ShowConfirmationDialog( |
| title, description, l10n_util::GetStringUTF16(IDS_ASH_CONFIRM_BUTTON), |
| l10n_util::GetStringUTF16(IDS_APP_CANCEL), |
| base::BindOnce(&AccessibilityController::OnDisableTouchpadDialogAccepted, |
| GetWeakPtr()), |
| base::BindOnce(&AccessibilityController::OnDisableTouchpadDialogDismissed, |
| GetWeakPtr()), |
| base::BindOnce(&AccessibilityController::OnDisableTouchpadDialogDismissed, |
| GetWeakPtr()), |
| kDialogTimeoutSeconds); |
| } |
| |
| void AccessibilityController::OnDisableTouchpadDialogAccepted() { |
| confirmation_dialog_.reset(); |
| ShowAccessibilityNotification(A11yNotificationWrapper( |
| A11yNotificationType::kTouchpadDisabled, kNotificationId, |
| std::vector<std::u16string>(), |
| base::BindRepeating( |
| &AccessibilityController::OnTouchpadNotificationClicked, |
| GetWeakPtr()))); |
| } |
| |
| void AccessibilityController::OnDisableTouchpadDialogDismissed() { |
| confirmation_dialog_.reset(); |
| active_user_prefs_->SetInteger(prefs::kAccessibilityDisableTrackpadMode, |
| static_cast<int>(DisableTouchpadMode::kNever)); |
| } |
| |
| DisableTouchpadMode AccessibilityController::GetDisableTouchpadMode() { |
| return static_cast<DisableTouchpadMode>( |
| active_user_prefs_->GetInteger(prefs::kAccessibilityDisableTrackpadMode)); |
| } |
| |
| bool AccessibilityController::IsTouchpadDisabled() { |
| return disable_touchpad().enabled() && |
| disable_touchpad_event_rewriter_->IsEnabled() && |
| active_user_prefs_->GetInteger( |
| prefs::kAccessibilityDisableTrackpadMode) != |
| static_cast<int>(DisableTouchpadMode::kNever); |
| } |
| |
| void AccessibilityController::UpdateColorCorrectionFromPrefs() { |
| DCHECK(active_user_prefs_); |
| |
| auto* color_enhancement_controller = |
| Shell::Get()->color_enhancement_controller(); |
| |
| if (!active_user_prefs_->GetBoolean( |
| prefs::kAccessibilityColorCorrectionEnabled)) { |
| color_enhancement_controller->SetColorCorrectionEnabledAndUpdateDisplays( |
| false); |
| return; |
| } |
| |
| const float cvd_correction_amount = |
| active_user_prefs_->GetInteger( |
| prefs::kAccessibilityColorVisionCorrectionAmount) / |
| 100.0f; |
| ColorVisionCorrectionType type = |
| static_cast<ColorVisionCorrectionType>(active_user_prefs_->GetInteger( |
| prefs::kAccessibilityColorVisionCorrectionType)); |
| color_enhancement_controller->SetColorVisionCorrectionFilter( |
| type, cvd_correction_amount); |
| |
| // Ensure displays get updated. |
| color_enhancement_controller->SetColorCorrectionEnabledAndUpdateDisplays( |
| true); |
| } |
| |
| void AccessibilityController::UpdateCaretBlinkIntervalFromPrefs() const { |
| const auto caret_blink_interval = base::Milliseconds( |
| active_user_prefs_->GetInteger(prefs::kAccessibilityCaretBlinkInterval)); |
| if (auto* const native_theme = ui::NativeTheme::GetInstanceForNativeUi(); |
| native_theme->caret_blink_interval() != caret_blink_interval) { |
| native_theme->set_caret_blink_interval(caret_blink_interval); |
| native_theme->NotifyOnNativeThemeUpdated(); |
| } |
| } |
| |
| void AccessibilityController::UpdateUseOverlayScrollbarFromPref() const { |
| const bool overlay_scrollbar_enabled_by_feature_flag = |
| ::ui::IsOverlayScrollbarEnabledByFeatureFlag(); |
| const bool overlay_scrollbar_enabled_by_os_setting = |
| !always_show_scrollbar().enabled(); |
| const bool use_overlay_scrollbar = |
| overlay_scrollbar_enabled_by_feature_flag || |
| overlay_scrollbar_enabled_by_os_setting; |
| if (auto* const native_theme = ui::NativeTheme::GetInstanceForNativeUi(); |
| native_theme->use_overlay_scrollbar() != use_overlay_scrollbar) { |
| native_theme->set_use_overlay_scrollbar(use_overlay_scrollbar); |
| native_theme->NotifyOnNativeThemeUpdated(); |
| } |
| if (auto* const native_theme_web = ui::NativeTheme::GetInstanceForWeb(); |
| native_theme_web->use_overlay_scrollbar() != use_overlay_scrollbar) { |
| native_theme_web->set_use_overlay_scrollbar(use_overlay_scrollbar); |
| native_theme_web->NotifyOnNativeThemeUpdated(); |
| } |
| } |
| |
| std::optional<ui::KeyboardCode> |
| AccessibilityController::GetCaretBrowsingActionKey() { |
| const std::vector<ui::KeyboardDevice>& keyboards = |
| ui::DeviceDataManager::GetInstance()->GetKeyboardDevices(); |
| std::optional<ui::TopRowActionKey> key; |
| if (keyboards.size() > 0) { |
| if (ash::Shell::Get() |
| ->event_rewriter_controller() |
| ->event_rewriter_ash_delegate() |
| ->TopRowKeysAreFunctionKeys(keyboards[0].id)) { |
| return ui::VKEY_F7; |
| } |
| key = ash::Shell::Get() |
| ->keyboard_capability() |
| ->GetCorrespondingActionKeyForFKey(keyboards[0], ui::VKEY_F7); |
| } |
| if (key) { |
| return ui::KeyboardCapability::ConvertToKeyboardCode(*key); |
| } |
| return std::nullopt; |
| } |
| |
| void AccessibilityController::UpdateAccessibilityHighlightingFromPrefs() { |
| if (!caret_highlight().enabled() && !cursor_highlight().enabled() && |
| !focus_highlight().enabled()) { |
| accessibility_highlight_controller_.reset(); |
| return; |
| } |
| |
| if (!accessibility_highlight_controller_) { |
| accessibility_highlight_controller_ = |
| std::make_unique<AccessibilityHighlightController>(); |
| } |
| |
| accessibility_highlight_controller_->HighlightCaret( |
| caret_highlight().enabled()); |
| accessibility_highlight_controller_->HighlightCursor( |
| cursor_highlight().enabled()); |
| accessibility_highlight_controller_->HighlightFocus( |
| focus_highlight().enabled()); |
| } |
| |
| void AccessibilityController::MaybeCreateSelectToSpeakEventHandler() { |
| // Construct the handler as needed when Select-to-Speak is enabled and the |
| // delegate is set. Otherwise, destroy the handler when Select-to-Speak is |
| // disabled or the delegate has been destroyed. |
| if (!select_to_speak().enabled() || |
| !select_to_speak_event_handler_delegate_) { |
| select_to_speak_event_handler_.reset(); |
| return; |
| } |
| |
| if (select_to_speak_event_handler_) { |
| return; |
| } |
| |
| select_to_speak_event_handler_ = std::make_unique<SelectToSpeakEventHandler>( |
| select_to_speak_event_handler_delegate_); |
| } |
| |
| void AccessibilityController::UpdateSwitchAccessKeyCodesFromPref( |
| SwitchAccessCommand command) { |
| if (!active_user_prefs_) { |
| return; |
| } |
| |
| SyncSwitchAccessPrefsToSignInProfile(); |
| |
| if (!accessibility_event_rewriter_) { |
| return; |
| } |
| |
| std::string pref_key = PrefKeyForSwitchAccessCommand(command); |
| const base::Value::Dict& key_codes_pref = |
| active_user_prefs_->GetDict(pref_key); |
| std::map<int, std::set<std::string>> key_codes; |
| for (const auto v : key_codes_pref) { |
| int key_code; |
| if (!base::StringToInt(v.first, &key_code)) { |
| NOTREACHED(); |
| } |
| |
| key_codes[key_code] = std::set<std::string>(); |
| |
| for (const base::Value& device_type : v.second.GetList()) { |
| key_codes[key_code].insert(device_type.GetString()); |
| } |
| |
| DCHECK(!key_codes[key_code].empty()); |
| } |
| |
| std::string uma_name = UmaNameForSwitchAccessCommand(command); |
| if (key_codes.size() == 0) { |
| base::UmaHistogramEnumeration(uma_name, SwitchAccessKeyCode::kNone); |
| } |
| for (const auto& key_code : key_codes) { |
| base::UmaHistogramEnumeration( |
| uma_name, static_cast<SwitchAccessKeyCode>(key_code.first)); |
| } |
| |
| accessibility_event_rewriter_->SetKeyCodesForSwitchAccessCommand(key_codes, |
| command); |
| } |
| |
| void AccessibilityController::UpdateSwitchAccessAutoScanEnabledFromPref() { |
| DCHECK(active_user_prefs_); |
| const bool enabled = active_user_prefs_->GetBoolean( |
| prefs::kAccessibilitySwitchAccessAutoScanEnabled); |
| |
| base::UmaHistogramBoolean("Accessibility.CrosSwitchAccess.AutoScan", enabled); |
| SyncSwitchAccessPrefsToSignInProfile(); |
| } |
| |
| void AccessibilityController::UpdateSwitchAccessAutoScanSpeedFromPref() { |
| DCHECK(active_user_prefs_); |
| const int speed_ms = active_user_prefs_->GetInteger( |
| prefs::kAccessibilitySwitchAccessAutoScanSpeedMs); |
| |
| base::UmaHistogramCustomCounts( |
| "Accessibility.CrosSwitchAccess.AutoScan.SpeedMs", speed_ms, 1 /* min */, |
| 10000 /* max */, 100 /* buckets */); |
| SyncSwitchAccessPrefsToSignInProfile(); |
| } |
| |
| void AccessibilityController:: |
| UpdateSwitchAccessAutoScanKeyboardSpeedFromPref() { |
| DCHECK(active_user_prefs_); |
| const int speed_ms = active_user_prefs_->GetInteger( |
| prefs::kAccessibilitySwitchAccessAutoScanKeyboardSpeedMs); |
| |
| base::UmaHistogramCustomCounts( |
| "Accessibility.CrosSwitchAccess.AutoScan.KeyboardSpeedMs", speed_ms, |
| 1 /* min */, 10000 /* max */, 100 /* buckets */); |
| SyncSwitchAccessPrefsToSignInProfile(); |
| } |
| |
| void AccessibilityController::UpdateSwitchAccessPointScanSpeedFromPref() { |
| // TODO(accessibility): Log histogram for point scan speed |
| DCHECK(active_user_prefs_); |
| const int point_scan_speed_dips_per_second = active_user_prefs_->GetInteger( |
| prefs::kAccessibilitySwitchAccessPointScanSpeedDipsPerSecond); |
| |
| SetPointScanSpeedDipsPerSecond(point_scan_speed_dips_per_second); |
| SyncSwitchAccessPrefsToSignInProfile(); |
| } |
| |
| void AccessibilityController::SwitchAccessDisableDialogClosed( |
| bool disable_dialog_accepted) { |
| switch_access_disable_dialog_showing_ = false; |
| // Always deactivate switch access. Turning switch access off ensures it is |
| // re-activated correctly. |
| // The pref was already disabled, but we left switch access on so the user |
| // could interact with the dialog. |
| DeactivateSwitchAccess(); |
| if (disable_dialog_accepted) { |
| RemoveAccessibilityNotification(); |
| NotifyAccessibilityStatusChanged(); |
| SyncSwitchAccessPrefsToSignInProfile(); |
| } else { |
| // Reset the preference (which was already set to false). Doing so turns |
| // switch access back on. |
| skip_switch_access_notification_ = true; |
| switch_access().SetEnabled(true); |
| } |
| } |
| |
| void AccessibilityController::UpdateKeyCodesAfterSwitchAccessEnabled() { |
| UpdateSwitchAccessKeyCodesFromPref(SwitchAccessCommand::kSelect); |
| UpdateSwitchAccessKeyCodesFromPref(SwitchAccessCommand::kNext); |
| UpdateSwitchAccessKeyCodesFromPref(SwitchAccessCommand::kPrevious); |
| } |
| |
| void AccessibilityController::ActivateSwitchAccess() { |
| switch_access_bubble_controller_ = |
| std::make_unique<SwitchAccessMenuBubbleController>(); |
| point_scan_controller_ = std::make_unique<PointScanController>(); |
| UpdateKeyCodesAfterSwitchAccessEnabled(); |
| UpdateSwitchAccessPointScanSpeedFromPref(); |
| if (skip_switch_access_notification_) { |
| skip_switch_access_notification_ = false; |
| return; |
| } |
| |
| ShowAccessibilityNotification( |
| A11yNotificationWrapper(A11yNotificationType::kSwitchAccessEnabled, |
| kNotificationId, std::vector<std::u16string>())); |
| } |
| |
| void AccessibilityController::DeactivateSwitchAccess() { |
| if (client_) { |
| client_->OnSwitchAccessDisabled(); |
| } |
| point_scan_controller_.reset(); |
| switch_access_bubble_controller_.reset(); |
| } |
| |
| void AccessibilityController::SyncSwitchAccessPrefsToSignInProfile() { |
| if (!active_user_prefs_ || IsSigninPrefService(active_user_prefs_)) { |
| return; |
| } |
| |
| PrefService* signin_prefs = |
| Shell::Get()->session_controller()->GetSigninScreenPrefService(); |
| DCHECK(signin_prefs); |
| |
| for (const auto* pref_path : kSwitchAccessPrefsCopiedToSignin) { |
| const PrefService::Preference* pref = |
| active_user_prefs_->FindPreference(pref_path); |
| |
| // Ignore if the pref has not been set by the user. |
| if (!pref || !pref->IsUserControlled()) { |
| continue; |
| } |
| |
| // Copy the pref value to the signin profile. |
| const base::Value* value = pref->GetValue(); |
| signin_prefs->Set(pref_path, *value); |
| } |
| } |
| |
| void AccessibilityController::UpdateShortcutsEnabledFromPref() { |
| DCHECK(active_user_prefs_); |
| const bool enabled = |
| active_user_prefs_->GetBoolean(prefs::kAccessibilityShortcutsEnabled); |
| |
| if (shortcuts_enabled_ == enabled) { |
| return; |
| } |
| |
| shortcuts_enabled_ = enabled; |
| |
| NotifyAccessibilityStatusChanged(); |
| } |
| |
| void AccessibilityController::UpdateTabletModeShelfNavigationButtonsFromPref() { |
| DCHECK(active_user_prefs_); |
| const bool enabled = active_user_prefs_->GetBoolean( |
| prefs::kAccessibilityTabletModeShelfNavigationButtonsEnabled); |
| |
| if (tablet_mode_shelf_navigation_buttons_enabled_ == enabled) { |
| return; |
| } |
| |
| tablet_mode_shelf_navigation_buttons_enabled_ = enabled; |
| |
| NotifyAccessibilityStatusChanged(); |
| } |
| |
| std::u16string AccessibilityController::GetBatteryDescription() const { |
| // Pass battery status as string to callback function. |
| return PowerStatus::Get()->GetAccessibleNameString( |
| /*full_description=*/true); |
| } |
| |
| void AccessibilityController::SetVirtualKeyboardVisible(bool is_visible) { |
| if (is_visible) { |
| Shell::Get()->keyboard_controller()->ShowKeyboard(); |
| } else { |
| Shell::Get()->keyboard_controller()->HideKeyboard(HideReason::kUser); |
| } |
| |
| if (set_virtual_keyboard_visible_callback_) { |
| set_virtual_keyboard_visible_callback_.Run(); |
| } |
| } |
| |
| void AccessibilityController::ToggleMouseKeys() { |
| if (::features::IsAccessibilityMouseKeysEnabled() && mouse_keys().enabled()) { |
| Shell::Get()->mouse_keys_controller()->Toggle(); |
| NotifyAccessibilityStatusChanged(); |
| } |
| } |
| |
| void AccessibilityController::PerformAccessibilityAction() { |
| // TODO(b/335456364): Add UMA. |
| aura::Window* target_root = Shell::GetRootWindowForNewWindows(); |
| StatusAreaWidget* status_area_widget = |
| RootWindowController::ForWindow(target_root)->GetStatusAreaWidget(); |
| if (!status_area_widget) { |
| // TODO(b/335456364): Support Kiosk mode. |
| } |
| |
| UnifiedSystemTray* tray = status_area_widget->unified_system_tray(); |
| if (tray->IsBubbleShown()) { |
| if (tray->bubble() |
| ->unified_system_tray_controller() |
| ->showing_accessibility_detailed_view()) { |
| tray->CloseBubble(); |
| return; |
| } |
| } else { |
| tray->ShowBubble(); |
| } |
| tray->bubble() |
| ->unified_system_tray_controller() |
| ->ShowAccessibilityDetailedView(); |
| } |
| |
| void AccessibilityController::PerformAcceleratorAction( |
| AcceleratorAction accelerator_action) { |
| AcceleratorController::Get()->PerformActionIfEnabled(accelerator_action, |
| /* accelerator = */ {}); |
| } |
| |
| void AccessibilityController::NotifyAccessibilityStatusChanged() { |
| for (auto& observer : observers_) { |
| observer.OnAccessibilityStatusChanged(); |
| } |
| } |
| |
| bool AccessibilityController::IsAccessibilityFeatureVisibleInTrayMenu( |
| const std::string& path) { |
| if (!active_user_prefs_) { |
| return true; |
| } |
| if (active_user_prefs_->FindPreference(path)->IsManaged() && |
| !active_user_prefs_->GetBoolean(path)) { |
| return false; |
| } |
| return true; |
| } |
| |
| void AccessibilityController::SuspendSwitchAccessKeyHandling(bool suspend) { |
| accessibility_event_rewriter_->set_suspend_switch_access_key_handling( |
| suspend); |
| } |
| |
| void AccessibilityController::EnableChromeVoxVolumeSlideGesture() { |
| enable_chromevox_volume_slide_gesture_ = true; |
| } |
| |
| void AccessibilityController::ShowConfirmationDialog( |
| const std::u16string& title, |
| const std::u16string& description, |
| const std::u16string& confirm_name, |
| const std::u16string& cancel_name, |
| base::OnceClosure on_accept_callback, |
| base::OnceClosure on_cancel_callback, |
| base::OnceClosure on_close_callback, |
| std::optional<int> timeout_seconds) { |
| if (confirmation_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_close_callback).Run(); |
| return; |
| } |
| auto* dialog = new AccessibilityConfirmationDialog( |
| title, description, confirm_name, cancel_name, |
| std::move(on_accept_callback), std::move(on_cancel_callback), |
| std::move(on_close_callback), timeout_seconds); |
| // Save the dialog so it doesn't go out of scope before it is |
| // used and closed. |
| confirmation_dialog_ = dialog->GetWeakPtr(); |
| if (show_confirmation_dialog_callback_for_testing_) { |
| show_confirmation_dialog_callback_for_testing_.Run(); |
| } |
| } |
| |
| gfx::Rect AccessibilityController::GetConfirmationDialogBoundsInScreen() { |
| if (!confirmation_dialog_.get()) { |
| return gfx::Rect(); |
| } |
| 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(); |
| } |
| |
| void AccessibilityController:: |
| UpdateDictationButtonOnSpeechRecognitionDownloadChanged( |
| int download_progress) { |
| dictation_soda_download_progress_ = download_progress; |
| Shell::Get() |
| ->GetPrimaryRootWindowController() |
| ->GetStatusAreaWidget() |
| ->dictation_button_tray() |
| ->UpdateOnSpeechRecognitionDownloadChanged(download_progress); |
| } |
| |
| void AccessibilityController::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(notification_type, kNotificationId, |
| std::vector<std::u16string>{display_language})); |
| } |
| |
| void AccessibilityController::ShowNotificationForFaceGaze( |
| FaceGazeNotificationType type) { |
| A11yNotificationType notification_type; |
| std::string notification_shown_pref; |
| switch (type) { |
| case FaceGazeNotificationType::kDlcSucceeded: |
| notification_type = A11yNotificationType::kFaceGazeAssetsDownloaded; |
| notification_shown_pref = |
| prefs::kFaceGazeDlcSuccessNotificationHasBeenShown; |
| break; |
| case FaceGazeNotificationType::kDlcFailed: |
| notification_type = A11yNotificationType::kFaceGazeAssetsFailed; |
| notification_shown_pref = |
| prefs::kFaceGazeDlcFailureNotificationHasBeenShown; |
| break; |
| } |
| |
| if (active_user_prefs_->GetBoolean(notification_shown_pref) && |
| notification_shown_pref == |
| prefs::kFaceGazeDlcSuccessNotificationHasBeenShown) { |
| // Do not show success notifications more than once. |
| return; |
| } |
| |
| active_user_prefs_->SetBoolean(notification_shown_pref, true); |
| ShowAccessibilityNotification(A11yNotificationWrapper( |
| 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), |
| 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() = |
| default; |
| AccessibilityController::A11yNotificationWrapper::A11yNotificationWrapper( |
| const A11yNotificationWrapper&) = default; |
| |
| void AccessibilityController::UpdateFeatureFromPref(FeatureType feature) { |
| size_t feature_index = static_cast<size_t>(feature); |
| bool enabled = features_[feature_index]->enabled(); |
| bool is_managed = active_user_prefs_->IsManagedPreference( |
| features_[feature_index]->pref_name()); |
| |
| switch (feature) { |
| case FeatureType::kAutoclick: |
| Shell::Get()->autoclick_controller()->SetEnabled( |
| enabled, /*show_confirmation_dialog=*/ |
| !no_auto_click_confirmation_dialog_for_testing_ && !is_managed); |
| break; |
| case FeatureType::kBounceKeys: |
| if (filter_keys_event_rewriter_) { |
| filter_keys_event_rewriter_->SetBounceKeysEnabled(enabled); |
| } |
| break; |
| case FeatureType::kCaretHighlight: |
| UpdateAccessibilityHighlightingFromPrefs(); |
| break; |
| case FeatureType::kCursorHighlight: |
| UpdateAccessibilityHighlightingFromPrefs(); |
| break; |
| case FeatureType::kDictation: |
| if (enabled) { |
| if (!dictation_bubble_controller_) { |
| dictation_bubble_controller_ = |
| std::make_unique<DictationBubbleController>(); |
| } |
| } else { |
| dictation_bubble_controller_.reset(); |
| } |
| break; |
| case FeatureType::kDisableTouchpad: |
| if (!::features::IsAccessibilityDisableTouchpadEnabled() || |
| !disable_touchpad_event_rewriter_) { |
| return; |
| } |
| |
| disable_touchpad_event_rewriter_->SetEnabled(enabled); |
| break; |
| case FeatureType::kFloatingMenu: |
| if (enabled && always_show_floating_menu_when_enabled_) { |
| ShowFloatingMenuIfEnabled(); |
| } else { |
| floating_menu_controller_.reset(); |
| } |
| break; |
| case FeatureType::kFocusHighlight: |
| UpdateAccessibilityHighlightingFromPrefs(); |
| break; |
| case FeatureType::kFullscreenMagnifier: |
| break; |
| case FeatureType::kDockedMagnifier: |
| break; |
| case FeatureType::kHighContrast: |
| Shell::Get()->color_enhancement_controller()->SetHighContrastEnabled( |
| enabled); |
| break; |
| case FeatureType::kLargeCursor: |
| Shell::Get()->cursor_manager()->SetCursorSize( |
| large_cursor().enabled() ? ui::CursorSize::kLarge |
| : ui::CursorSize::kNormal); |
| Shell::Get()->SetLargeCursorSizeInDip(large_cursor_size_in_dip_); |
| Shell::Get()->UpdateCursorCompositingEnabled(); |
| break; |
| case FeatureType::kLiveCaption: |
| live_caption().SetEnabled(enabled); |
| break; |
| case FeatureType::kMonoAudio: |
| CrasAudioHandler::Get()->SetOutputMonoEnabled(enabled); |
| break; |
| case FeatureType::kMouseKeys: |
| if (::features::IsAccessibilityMouseKeysEnabled()) { |
| // TODO(b/259372916): Consider creating/deleting MouseKeysController |
| // here. |
| Shell::Get()->mouse_keys_controller()->set_enabled(enabled); |
| } |
| break; |
| case FeatureType::kSpokenFeedback: |
| message_center::MessageCenter::Get()->SetSpokenFeedbackEnabled(enabled); |
| // TODO(warx): ChromeVox loading/unloading requires browser process |
| // started, thus it is still handled on Chrome side. |
| |
| // ChromeVox focus highlighting overrides the other focus highlighting. |
| focus_highlight().UpdateFromPref(); |
| break; |
| case FeatureType::kReducedAnimations: |
| // Handled in AccessibilityManager. |
| break; |
| case FeatureType::kAlwaysShowScrollbar: |
| UpdateUseOverlayScrollbarFromPref(); |
| break; |
| case FeatureType::kSelectToSpeak: |
| select_to_speak_state_ = SelectToSpeakState::kSelectToSpeakStateInactive; |
| if (enabled) { |
| MaybeCreateSelectToSpeakEventHandler(); |
| } else { |
| select_to_speak_event_handler_.reset(); |
| HideSelectToSpeakPanel(); |
| select_to_speak_bubble_controller_.reset(); |
| } |
| break; |
| case FeatureType::kSlowKeys: |
| if (::features::IsAccessibilitySlowKeysEnabled()) { |
| input_method::InputMethodManager::Get() |
| ->GetImeKeyboard() |
| ->SetSlowKeysEnabled(enabled); |
| } |
| break; |
| case FeatureType::kStickyKeys: |
| Shell::Get()->sticky_keys_controller()->Enable(enabled); |
| break; |
| case FeatureType::kSwitchAccess: |
| if (!enabled) { |
| if (no_switch_access_disable_confirmation_dialog_for_testing_) { |
| SwitchAccessDisableDialogClosed(true); |
| } else { |
| // Show a dialog before disabling Switch Access. |
| new AccessibilityFeatureDisableDialog( |
| IDS_ASH_SWITCH_ACCESS_DISABLE_CONFIRMATION_TEXT, |
| base::BindOnce( |
| &AccessibilityController::SwitchAccessDisableDialogClosed, |
| weak_ptr_factory_.GetWeakPtr(), true), |
| base::BindOnce( |
| &AccessibilityController::SwitchAccessDisableDialogClosed, |
| weak_ptr_factory_.GetWeakPtr(), false)); |
| switch_access_disable_dialog_showing_ = true; |
| } |
| // Return early. We will call NotifyAccessibilityStatusChanged() if the |
| // user accepts the dialog. |
| return; |
| } else { |
| ActivateSwitchAccess(); |
| } |
| SyncSwitchAccessPrefsToSignInProfile(); |
| break; |
| case FeatureType::kVirtualKeyboard: |
| keyboard::SetAccessibilityKeyboardEnabled(enabled); |
| break; |
| case FeatureType::kCursorColor: |
| // The notification will already come via UpdateFeatureFromPref |
| // so we don't need to run it twice. |
| UpdateCursorColorFromPrefs(/*notify=*/false); |
| break; |
| case FeatureType::kColorCorrection: |
| if (enabled && !active_user_prefs_->GetBoolean( |
| prefs::kAccessibilityColorCorrectionHasBeenSetup)) { |
| Shell::Get() |
| ->system_tray_model() |
| ->client() |
| ->ShowColorCorrectionSettings(); |
| active_user_prefs_->SetBoolean( |
| prefs::kAccessibilityColorCorrectionHasBeenSetup, true); |
| } |
| UpdateColorCorrectionFromPrefs(); |
| break; |
| case FeatureType::kFaceGaze: |
| if (enabled) { |
| if (!facegaze_bubble_controller_) { |
| facegaze_bubble_controller_ = |
| std::make_unique<FaceGazeBubbleController>(base::BindRepeating( |
| &AccessibilityController::RequestDisableFaceGaze, |
| GetWeakPtr())); |
| } |
| if (!drag_event_rewriter_) { |
| drag_event_rewriter_ = std::make_unique<DragEventRewriter>(); |
| |
| Shell::GetPrimaryRootWindow() |
| ->GetHost() |
| ->GetEventSource() |
| ->AddEventRewriter(drag_event_rewriter_.get()); |
| } |
| } else { |
| facegaze_bubble_controller_.reset(); |
| |
| Shell::GetPrimaryRootWindow() |
| ->GetHost() |
| ->GetEventSource() |
| ->RemoveEventRewriter(drag_event_rewriter_.get()); |
| drag_event_rewriter_.reset(); |
| } |
| UpdateFaceGazeFromPrefs(); |
| break; |
| case FeatureType::kFlashNotifications: |
| UpdateFlashNotificationsFromPrefs(); |
| break; |
| case FeatureType::kFeatureCount: |
| case FeatureType::kNoConflictingFeature: |
| NOTREACHED(); |
| } |
| NotifyAccessibilityStatusChanged(); |
| } |
| |
| void AccessibilityController::UpdateDictationBubble( |
| bool visible, |
| DictationBubbleIconType icon, |
| const std::optional<std::u16string>& text, |
| const std::optional<std::vector<DictationBubbleHintType>>& hints) { |
| DCHECK(dictation().enabled()); |
| DCHECK(dictation_bubble_controller_); |
| |
| dictation_bubble_controller_->UpdateBubble(visible, icon, text, hints); |
| } |
| |
| DictationBubbleController* |
| AccessibilityController::GetDictationBubbleControllerForTest() { |
| if (!dictation_bubble_controller_) { |
| dictation_bubble_controller_ = |
| std::make_unique<DictationBubbleController>(); |
| } |
| |
| return dictation_bubble_controller_.get(); |
| } |
| |
| void AccessibilityController::ShowToast(AccessibilityToastType type) { |
| accessibility_notification_controller_->ShowToast(type); |
| } |
| |
| void AccessibilityController::AddShowToastCallbackForTesting( |
| base::RepeatingCallback<void(AccessibilityToastType)> callback) { |
| accessibility_notification_controller_->AddShowToastCallbackForTesting( |
| std::move(callback)); |
| } |
| |
| void AccessibilityController::AddShowConfirmationDialogCallbackForTesting( |
| base::RepeatingCallback<void()> callback) { |
| 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(); |
| } |
| |
| void AccessibilityController::SetVirtualKeyboardVisibleCallbackForTesting( |
| base::RepeatingCallback<void()> callback) { |
| set_virtual_keyboard_visible_callback_ = std::move(callback); |
| } |
| |
| void AccessibilityController::ScrollAtPoint( |
| const gfx::Point& target, |
| AccessibilityScrollDirection direction) { |
| float scroll_x = 0.0f; |
| float scroll_y = 0.0f; |
| switch (direction) { |
| case AccessibilityScrollDirection::kUp: |
| scroll_y = kScrollDelta; |
| break; |
| case AccessibilityScrollDirection::kDown: |
| scroll_y = -kScrollDelta; |
| break; |
| case AccessibilityScrollDirection::kLeft: |
| scroll_x = kScrollDelta; |
| break; |
| case AccessibilityScrollDirection::kRight: |
| scroll_x = -kScrollDelta; |
| } |
| |
| // Generate a scroll event at the target location. |
| aura::Window* root_window = window_util::GetRootWindowAt(target); |
| gfx::Point location_in_pixels(target); |
| ::wm::ConvertPointFromScreen(root_window, &location_in_pixels); |
| aura::WindowTreeHost* host = root_window->GetHost(); |
| host->ConvertDIPToPixels(&location_in_pixels); |
| ui::ScrollEvent scroll( |
| ui::EventType::kScroll, gfx::PointF(location_in_pixels), |
| gfx::PointF(location_in_pixels), ui::EventTimeForNow(), |
| ui::EF_IS_SYNTHESIZED, scroll_x, scroll_y, 0 /* x_offset_ordinal */, |
| 0 /* y_offset_ordinal */, 2 /* finger_count */); |
| ui::MouseWheelEvent wheel(scroll); |
| 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; |
| } |
| |
| int window_title_text_id = 0; |
| 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; |
| } else { |
| NOTREACHED(); |
| } |
| |
| 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_) { |
| return; |
| } |
| |
| facegaze_bubble_controller_->UpdateBubble(text, is_warning); |
| } |
| |
| FaceGazeBubbleController* |
| AccessibilityController::GetFaceGazeBubbleControllerForTest() { |
| if (!facegaze_bubble_controller_) { |
| facegaze_bubble_controller_ = |
| std::make_unique<FaceGazeBubbleController>(base::BindRepeating( |
| &AccessibilityController::RequestDisableFaceGaze, GetWeakPtr())); |
| } |
| |
| return facegaze_bubble_controller_.get(); |
| } |
| |
| void AccessibilityController::ObserveInputDeviceSettings() { |
| if (!input_device_settings_observer_.IsObservingSource( |
| Shell::Get()->input_device_settings_controller())) { |
| input_device_settings_observer_.Observe( |
| Shell::Get()->input_device_settings_controller()); |
| } |
| } |
| |
| void AccessibilityController::EnableDragEventRewriter(bool enabled) { |
| if (!drag_event_rewriter_) { |
| return; |
| } |
| |
| drag_event_rewriter_->SetEnabled(enabled); |
| } |
| |
| void AccessibilityController::RequestDisableFaceGaze() { |
| ShowFeatureDisableDialog( |
| IDS_ASH_FACEGAZE_DISABLE_CONFIRMATION_TEXT, |
| BindOnce(&AccessibilityController::OnRequestDisableFaceGazeAction, |
| GetWeakPtr(), /*dialog_accepted=*/true), |
| BindOnce(&AccessibilityController::OnRequestDisableFaceGazeAction, |
| GetWeakPtr(), /*dialog_accepted=*/false)); |
| } |
| |
| void AccessibilityController::OnRequestDisableFaceGazeAction( |
| bool dialog_accepted) { |
| if (dialog_accepted) { |
| active_user_prefs_->SetBoolean(prefs::kAccessibilityFaceGazeEnabled, false); |
| } |
| |
| disable_dialog_.reset(); |
| client_->SendFaceGazeDisableDialogResultToSettings(dialog_accepted); |
| } |
| |
| } // namespace ash |