1 // Copyright 2017 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef ASH_ACCESSIBILITY_ACCESSIBILITY_CONTROLLER_IMPL_H_ 6 #define ASH_ACCESSIBILITY_ACCESSIBILITY_CONTROLLER_IMPL_H_ 7 8 #include <memory> 9 10 #include "ash/ash_export.h" 11 #include "ash/public/cpp/accessibility_controller.h" 12 #include "ash/public/cpp/ash_constants.h" 13 #include "ash/public/cpp/session/session_observer.h" 14 #include "ash/public/cpp/tablet_mode_observer.h" 15 #include "base/callback_forward.h" 16 #include "base/macros.h" 17 #include "base/memory/weak_ptr.h" 18 #include "base/observer_list.h" 19 #include "base/time/time.h" 20 21 class PrefChangeRegistrar; 22 class PrefRegistrySimple; 23 class PrefService; 24 25 namespace ax { 26 namespace mojom { 27 enum class Gesture; 28 } // namespace mojom 29 } // namespace ax 30 31 namespace gfx { 32 class Point; 33 class PointF; 34 struct VectorIcon; 35 } // namespace gfx 36 37 namespace ash { 38 39 class AccessibilityEventRewriter; 40 class AccessibilityHighlightController; 41 class AccessibilityObserver; 42 class FloatingAccessibilityController; 43 class PointScanController; 44 class ScopedBacklightsForcedOff; 45 class SelectToSpeakEventHandler; 46 class SelectToSpeakMenuBubbleController; 47 class SwitchAccessMenuBubbleController; 48 49 enum AccessibilityNotificationVisibility { 50 A11Y_NOTIFICATION_NONE, 51 A11Y_NOTIFICATION_SHOW, 52 }; 53 54 // The controller for accessibility features in ash. Features can be enabled 55 // in chrome's webui settings or the system tray menu (see TrayAccessibility). 56 // Uses preferences to communicate with chrome to support mash. 57 class ASH_EXPORT AccessibilityControllerImpl : public AccessibilityController, 58 public SessionObserver, 59 public TabletModeObserver { 60 public: 61 enum FeatureType { 62 kAutoclick = 0, 63 kCaretHighlight, 64 KCursorHighlight, 65 kDictation, 66 kFloatingMenu, 67 kFocusHighlight, 68 kFullscreenMagnifier, 69 kDockedMagnifier, 70 kHighContrast, 71 kLargeCursor, 72 kMonoAudio, 73 kSpokenFeedback, 74 kSelectToSpeak, 75 kStickyKeys, 76 kSwitchAccess, 77 kVirtualKeyboard, 78 kCursorColor, 79 80 kFeatureCount, 81 kNoConflictingFeature 82 }; 83 84 // Common interface for all features. 85 class Feature { 86 public: 87 Feature(FeatureType type, 88 const std::string& pref_name, 89 const gfx::VectorIcon* icon, 90 AccessibilityControllerImpl* controller); 91 Feature(const Feature&) = delete; 92 Feature& operator=(Feature const&) = delete; 93 virtual ~Feature(); 94 type()95 FeatureType type() const { return type_; } 96 // Tries to set the feature to |enabled| by setting the user pref. 97 // Setting feature to be enabled can fail in following conditions: 98 // - there is a higher priority pref(managed), which overrides this value. 99 // - there is an other feature, which conflicts with the current one. 100 virtual void SetEnabled(bool enabled); enabled()101 bool enabled() const { return enabled_; } 102 bool IsVisibleInTray() const; 103 bool IsEnterpriseIconVisible() const; pref_name()104 const std::string& pref_name() const { return pref_name_; } 105 const gfx::VectorIcon& icon() const; 106 107 void UpdateFromPref(); 108 void SetConflictingFeature(FeatureType feature); 109 110 protected: 111 const FeatureType type_; 112 // Some features cannot be enabled while others are on. When a conflicting 113 // feature is enabled, we cannot enable current feature. 114 FeatureType conflicting_feature_ = FeatureType::kNoConflictingFeature; 115 bool enabled_ = false; 116 const std::string pref_name_; 117 const gfx::VectorIcon* icon_; 118 AccessibilityControllerImpl* const owner_; 119 }; 120 121 // Helper struct to store information about a11y dialog -- pref name, resource 122 // ids for title and body. Also stores the information whether this dialog is 123 // mandatory for every SetEnabled call. 124 struct Dialog { 125 std::string pref_name; 126 int title_resource_id; 127 int body_resource_id; 128 // Whether this dialog should be shown on every SetEnabled action. 129 bool mandatory; 130 }; 131 132 // Some features have confirmation dialog associated with them. 133 // Dialog can be applied for all SetEnabled() actions, or only to ones 134 // associated with accelerators. 135 class FeatureWithDialog : public Feature { 136 public: 137 FeatureWithDialog(FeatureType type, 138 const std::string& pref_name, 139 const gfx::VectorIcon* icon, 140 const Dialog& dialog, 141 AccessibilityControllerImpl* controller); 142 ~FeatureWithDialog() override; 143 144 // Tries to set the feature enabled, if its dialog is mandatory, shows the 145 // dailog for the first time feature is enabled. 146 void SetEnabled(bool enabled) override; 147 // If the dialog have not been accepted, we show it. When it is accepted, we 148 // call SetEnabled() and invoke |completion_callback|. 149 void SetEnabledWithDialog(bool enabled, 150 base::OnceClosure completion_callback); 151 void SetDialogAccepted(); 152 bool WasDialogAccepted() const; 153 154 private: 155 Dialog dialog_; 156 }; 157 158 AccessibilityControllerImpl(); 159 ~AccessibilityControllerImpl() override; 160 161 // See Shell::RegisterProfilePrefs(). 162 static void RegisterProfilePrefs(PrefRegistrySimple* registry); 163 164 void Shutdown(); 165 166 void AddObserver(AccessibilityObserver* observer); 167 void RemoveObserver(AccessibilityObserver* observer); 168 169 Feature& GetFeature(FeatureType feature) const; 170 171 // Getters for the corresponding features. 172 Feature& autoclick() const; 173 Feature& caret_highlight() const; 174 Feature& cursor_highlight() const; 175 FeatureWithDialog& dictation() const; 176 Feature& floating_menu() const; 177 Feature& focus_highlight() const; 178 FeatureWithDialog& fullscreen_magnifier() const; 179 FeatureWithDialog& docked_magnifier() const; 180 FeatureWithDialog& high_contrast() const; 181 Feature& large_cursor() const; 182 Feature& mono_audio() const; 183 Feature& spoken_feedback() const; 184 Feature& select_to_speak() const; 185 Feature& sticky_keys() const; 186 Feature& switch_access() const; 187 Feature& virtual_keyboard() const; 188 Feature& cursor_color() const; 189 190 void SetDisplayRotationAcceleratorDialogBeenAccepted(); 191 bool HasDisplayRotationAcceleratorDialogBeenAccepted() const; 192 193 bool IsAutoclickSettingVisibleInTray(); 194 bool IsEnterpriseIconVisibleForAutoclick(); 195 196 void SetAutoclickEventType(AutoclickEventType event_type); 197 AutoclickEventType GetAutoclickEventType(); 198 void SetAutoclickMenuPosition(FloatingMenuPosition position); 199 FloatingMenuPosition GetAutoclickMenuPosition(); 200 void RequestAutoclickScrollableBoundsForPoint(gfx::Point& point_in_screen); 201 void MagnifierBoundsChanged(const gfx::Rect& bounds_in_screen); 202 203 void SetFloatingMenuPosition(FloatingMenuPosition position); 204 FloatingMenuPosition GetFloatingMenuPosition(); 205 FloatingAccessibilityController* GetFloatingMenuController(); 206 207 PointScanController* GetPointScanController(); 208 209 // Update the autoclick menu bounds if necessary. This may need to happen when 210 // the display work area changes, or if system ui regions change (like the 211 // virtual keyboard position). 212 void UpdateAutoclickMenuBoundsIfNeeded(); 213 214 bool IsCaretHighlightSettingVisibleInTray(); 215 bool IsEnterpriseIconVisibleForCaretHighlight(); 216 217 bool IsCursorHighlightSettingVisibleInTray(); 218 bool IsEnterpriseIconVisibleForCursorHighlight(); 219 220 bool IsDictationSettingVisibleInTray(); 221 bool IsEnterpriseIconVisibleForDictation(); 222 223 bool IsFocusHighlightSettingVisibleInTray(); 224 bool IsEnterpriseIconVisibleForFocusHighlight(); 225 226 bool IsFullScreenMagnifierSettingVisibleInTray(); 227 bool IsEnterpriseIconVisibleForFullScreenMagnifier(); 228 229 bool IsDockedMagnifierSettingVisibleInTray(); 230 bool IsEnterpriseIconVisibleForDockedMagnifier(); 231 232 bool IsHighContrastSettingVisibleInTray(); 233 bool IsEnterpriseIconVisibleForHighContrast(); 234 235 bool IsLargeCursorSettingVisibleInTray(); 236 bool IsEnterpriseIconVisibleForLargeCursor(); 237 238 bool IsMonoAudioSettingVisibleInTray(); 239 bool IsEnterpriseIconVisibleForMonoAudio(); 240 241 void SetSpokenFeedbackEnabled(bool enabled, 242 AccessibilityNotificationVisibility notify); 243 bool IsSpokenFeedbackSettingVisibleInTray(); 244 bool IsEnterpriseIconVisibleForSpokenFeedback(); 245 246 bool IsSelectToSpeakSettingVisibleInTray(); 247 bool IsEnterpriseIconVisibleForSelectToSpeak(); 248 249 void RequestSelectToSpeakStateChange(); 250 SelectToSpeakState GetSelectToSpeakState() const; 251 252 bool IsStickyKeysSettingVisibleInTray(); 253 bool IsEnterpriseIconVisibleForStickyKeys(); 254 255 // Switch access may be disabled in prefs but still running when the disable 256 // dialog is displaying. 257 bool IsSwitchAccessRunning() const; 258 bool IsSwitchAccessSettingVisibleInTray(); 259 bool IsEnterpriseIconVisibleForSwitchAccess(); 260 void SetAccessibilityEventRewriter( 261 AccessibilityEventRewriter* accessibility_event_rewriter); 262 bool IsPointScanEnabled(); 263 264 bool IsVirtualKeyboardSettingVisibleInTray(); 265 bool IsEnterpriseIconVisibleForVirtualKeyboard(); 266 267 void SetTabletModeShelfNavigationButtonsEnabled(bool enabled); tablet_mode_shelf_navigation_buttons_enabled()268 bool tablet_mode_shelf_navigation_buttons_enabled() const { 269 return tablet_mode_shelf_navigation_buttons_enabled_; 270 } 271 272 void ShowFloatingMenuIfEnabled() override; 273 dictation_active()274 bool dictation_active() const { return dictation_active_; } 275 276 // Returns true if accessibility shortcuts have been disabled. accessibility_shortcuts_enabled()277 bool accessibility_shortcuts_enabled() const { return shortcuts_enabled_; } 278 279 // Triggers an accessibility alert to give the user feedback. 280 void TriggerAccessibilityAlert(AccessibilityAlert alert); 281 282 // Triggers an accessibility alert with the given |message|. 283 void TriggerAccessibilityAlertWithMessage(const std::string& message); 284 285 // Plays an earcon. Earcons are brief and distinctive sounds that indicate 286 // that their mapped event has occurred. The |sound_key| enums can be found in 287 // chromeos/audio/chromeos_sounds.h. 288 void PlayEarcon(int sound_key); 289 290 // Initiates play of shutdown sound. Returns the TimeDelta duration. 291 base::TimeDelta PlayShutdownSound(); 292 293 // Forwards an accessibility gesture from the touch exploration controller to 294 // ChromeVox. 295 void HandleAccessibilityGesture(ax::mojom::Gesture gesture, 296 gfx::PointF location); 297 298 // Toggle dictation. 299 void ToggleDictation(); 300 301 // Cancels all current and queued speech immediately. 302 void SilenceSpokenFeedback(); 303 304 // Called when we first detect two fingers are held down, which can be used to 305 // toggle spoken feedback on some touch-only devices. 306 void OnTwoFingerTouchStart(); 307 308 // Called when the user is no longer holding down two fingers (including 309 // releasing one, holding down three, or moving them). 310 void OnTwoFingerTouchStop(); 311 312 // Whether or not to enable toggling spoken feedback via holding down two 313 // fingers on the screen. 314 bool ShouldToggleSpokenFeedbackViaTouch() const; 315 316 // Plays tick sound indicating spoken feedback will be toggled after 317 // countdown. 318 void PlaySpokenFeedbackToggleCountdown(int tick_count); 319 320 // Returns true if that accessibility feature pref |path| is being controlled 321 // by a policy and false otherwise. 322 bool IsEnterpriseIconVisibleInTrayMenu(const std::string& path); 323 324 // Returns true if at least one of the primary settings of the accessibility 325 // features is going to be visible in the accessibility tray menu. 326 bool IsPrimarySettingsViewVisibleInTray(); 327 328 // Returns true if at least one of the additional settings of the 329 // accessibility features is going to be visible in the accessibility tray 330 // menu. 331 bool IsAdditionalSettingsViewVisibleInTray(); 332 333 // Returns true if there exist one of the additional accessibility features 334 // and one of the primary accessibility features which are going to visible on 335 // accessibility tray menu. 336 bool IsAdditionalSettingsSeparatorVisibleInTray(); 337 338 // Starts point scanning, to select a point onscreen without using a mouse 339 // (as used by Switch Access). 340 void StartPointScanning(); 341 342 // AccessibilityController: 343 void SetClient(AccessibilityControllerClient* client) override; 344 void SetDarkenScreen(bool darken) override; 345 void BrailleDisplayStateChanged(bool connected) override; 346 void SetFocusHighlightRect(const gfx::Rect& bounds_in_screen) override; 347 void SetCaretBounds(const gfx::Rect& bounds_in_screen) override; 348 void SetAccessibilityPanelAlwaysVisible(bool always_visible) override; 349 void SetAccessibilityPanelBounds(const gfx::Rect& bounds, 350 AccessibilityPanelState state) override; 351 void SetSelectToSpeakState(SelectToSpeakState state) override; 352 void SetSelectToSpeakEventHandlerDelegate( 353 SelectToSpeakEventHandlerDelegate* delegate) override; 354 void ShowSelectToSpeakPanel(const gfx::Rect& anchor, bool is_paused) override; 355 void HideSelectToSpeakPanel() override; 356 void HideSwitchAccessBackButton() override; 357 void HideSwitchAccessMenu() override; 358 void ShowSwitchAccessBackButton(const gfx::Rect& anchor) override; 359 void ShowSwitchAccessMenu(const gfx::Rect& anchor, 360 std::vector<std::string> actions_to_show) override; 361 void ActivatePointScan() override; 362 void SetDictationActive(bool is_active) override; 363 void ToggleDictationFromSource(DictationToggleSource source) override; 364 void HandleAutoclickScrollableBoundsFound( 365 gfx::Rect& bounds_in_screen) override; 366 base::string16 GetBatteryDescription() const override; 367 void SetVirtualKeyboardVisible(bool is_visible) override; 368 void PerformAcceleratorAction(AcceleratorAction accelerator_action) override; 369 void NotifyAccessibilityStatusChanged() override; 370 bool IsAccessibilityFeatureVisibleInTrayMenu( 371 const std::string& path) override; 372 void DisablePolicyRecommendationRestorerForTesting() override; 373 374 // SessionObserver: 375 void OnSigninScreenPrefServiceInitialized(PrefService* prefs) override; 376 void OnActiveUserPrefServiceChanged(PrefService* prefs) override; 377 378 // Test helpers: 379 AccessibilityEventRewriter* GetAccessibilityEventRewriterForTest(); GetSwitchAccessBubbleControllerForTest()380 SwitchAccessMenuBubbleController* GetSwitchAccessBubbleControllerForTest() { 381 return switch_access_bubble_controller_.get(); 382 } 383 void DisableSwitchAccessDisableConfirmationDialogTesting() override; 384 SelectToSpeakMenuBubbleController* GetSelectToSpeakMenuBubbleControllerForTest()385 GetSelectToSpeakMenuBubbleControllerForTest() { 386 return select_to_speak_bubble_controller_.get(); 387 } 388 389 private: 390 // Populate |features_| with the feature of the correct type. 391 void CreateAccessibilityFeatures(); 392 393 // Propagates the state of |feature| according to |feature->enabled()|. 394 void OnFeatureChanged(FeatureType feature); 395 396 // TabletModeObserver: 397 void OnTabletModeStarted() override; 398 void OnTabletModeEnded() override; 399 400 // Observes either the signin screen prefs or active user prefs and loads 401 // initial settings. 402 void ObservePrefs(PrefService* prefs); 403 404 // Updates the actual feature status based on the prefs value. 405 void UpdateFeatureFromPref(FeatureType feature); 406 407 void UpdateAutoclickDelayFromPref(); 408 void UpdateAutoclickEventTypeFromPref(); 409 void UpdateAutoclickRevertToLeftClickFromPref(); 410 void UpdateAutoclickStabilizePositionFromPref(); 411 void UpdateAutoclickMovementThresholdFromPref(); 412 void UpdateAutoclickMenuPositionFromPref(); 413 void UpdateFloatingMenuPositionFromPref(); 414 void UpdateLargeCursorFromPref(); 415 void UpdateCursorColorFromPrefs(); 416 void UpdateSwitchAccessKeyCodesFromPref(SwitchAccessCommand command); 417 void UpdateSwitchAccessAutoScanEnabledFromPref(); 418 void UpdateSwitchAccessAutoScanSpeedFromPref(); 419 void UpdateSwitchAccessAutoScanKeyboardSpeedFromPref(); 420 void UpdateAccessibilityHighlightingFromPrefs(); 421 void UpdateShortcutsEnabledFromPref(); 422 void UpdateTabletModeShelfNavigationButtonsFromPref(); 423 424 void SwitchAccessDisableDialogClosed(bool disable_dialog_accepted); 425 void MaybeCreateSelectToSpeakEventHandler(); 426 void ActivateSwitchAccess(); 427 void DeactivateSwitchAccess(); 428 void SyncSwitchAccessPrefsToSignInProfile(); 429 void UpdateKeyCodesAfterSwitchAccessEnabled(); 430 431 // Client interface in chrome browser. 432 AccessibilityControllerClient* client_ = nullptr; 433 434 std::unique_ptr<Feature> features_[kFeatureCount]; 435 436 base::TimeDelta autoclick_delay_; 437 int large_cursor_size_in_dip_ = kDefaultLargeCursorSize; 438 439 bool dictation_active_ = false; 440 bool shortcuts_enabled_ = true; 441 bool tablet_mode_shelf_navigation_buttons_enabled_ = false; 442 443 SelectToSpeakState select_to_speak_state_ = 444 SelectToSpeakState::kSelectToSpeakStateInactive; 445 std::unique_ptr<SelectToSpeakEventHandler> select_to_speak_event_handler_; 446 SelectToSpeakEventHandlerDelegate* select_to_speak_event_handler_delegate_ = 447 nullptr; 448 std::unique_ptr<SelectToSpeakMenuBubbleController> 449 select_to_speak_bubble_controller_; 450 451 // List of key codes that Switch Access should capture. 452 std::vector<int> switch_access_keys_to_capture_; 453 std::unique_ptr<SwitchAccessMenuBubbleController> 454 switch_access_bubble_controller_; 455 AccessibilityEventRewriter* accessibility_event_rewriter_ = nullptr; 456 bool no_switch_access_disable_confirmation_dialog_for_testing_ = false; 457 bool switch_access_disable_dialog_showing_ = false; 458 bool skip_switch_access_notification_ = false; 459 460 // Used to control the highlights of caret, cursor and focus. 461 std::unique_ptr<AccessibilityHighlightController> 462 accessibility_highlight_controller_; 463 464 // Used to display accessibility floating menu. 465 std::unique_ptr<FloatingAccessibilityController> floating_menu_controller_; 466 // By default, floating accessibility menu is not shown unless 467 // ShowFloatingMenuIfEnabled() is called. This is used in kiosk mode to 468 // postpone the showing of the menu till the splash screen closes. This value 469 // makes floating menu visible as soon as it is enabled. 470 bool always_show_floating_menu_when_enabled_ = false; 471 472 // Used to control point scanning, or selecting a point onscreen without using 473 // a mouse (as done by Switch Access). 474 std::unique_ptr<PointScanController> point_scan_controller_; 475 476 // Used to force the backlights off to darken the screen. 477 std::unique_ptr<ScopedBacklightsForcedOff> scoped_backlights_forced_off_; 478 479 base::ObserverList<AccessibilityObserver> observers_; 480 481 // The pref service of the currently active user or the signin profile before 482 // user logs in. Can be null in ash_unittests. 483 PrefService* active_user_prefs_ = nullptr; 484 // This has to be the first one to be destroyed so we don't get updates about 485 // any prefs during destruction. 486 std::unique_ptr<PrefChangeRegistrar> pref_change_registrar_; 487 488 base::WeakPtrFactory<AccessibilityControllerImpl> weak_ptr_factory_{this}; 489 490 DISALLOW_COPY_AND_ASSIGN(AccessibilityControllerImpl); 491 }; 492 493 } // namespace ash 494 495 #endif // ASH_ACCESSIBILITY_ACCESSIBILITY_CONTROLLER_IMPL_H_ 496