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