Skip to content

Commit

Permalink
[Switch Access] Add documentation
Browse files Browse the repository at this point in the history
TBR=dmazzoni@chromium.org

AX-Relnotes: n/a.
Bug: None.
Change-Id: I242cdd57d3f762684c613a826e6787a26f18e35c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2446657
Reviewed-by: Anastasia Helfinstein <anastasi@google.com>
Commit-Queue: Anastasia Helfinstein <anastasi@google.com>
Cr-Commit-Position: refs/heads/master@{#813375}
  • Loading branch information
anastasi-google authored and Commit Bot committed Oct 2, 2020
1 parent 71b8dea commit 0652d35
Showing 1 changed file with 397 additions and 0 deletions.
397 changes: 397 additions & 0 deletions docs/accessibility/switch_access.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,397 @@
# Switch Access (for developers)

Switch Access is a Chrome OS feature to control the computer with 1 or 2
**switches**, or button-like inputs. It is targeted at supporting users with
motor or mobility impairments, for whom other methods of controlling the device
are not feasible.


## Using Switch Access

Go to Chrome OS settings > Accessibility settings > "Manage accessibility
features" > Enable "Switch Access". You can assign switches to actions and
enable or disable automatic scanning on the Switch Access settings subpage.
For most development purposes, buttons on either the built-in keyboard or an
external keyboard can be used as switches, rather than needing a dedicated
switch device.

With this feature enabled, you can navigate to and interact with **actionable**
elements (or **nodes**) onscreen. Because there are so many nodes onscreen at
any given moment, they are organized into a nested system of **groups** based on
proximity and other semantic information.


### Navigation

Switch Access supports two methods of navigation between nodes: manual scanning
and automatic scanning (or **auto-scan**). Manual scanning means the user
navigates from one element to the next by pressing one of their switches.
Automatic scanning means that Switch Access moves from one element to the next
after a set period of time (the **scanning speed**).

The user can identify which element is currently focused because it is
surrounded by a **focus ring**, which is two concentric rounded rectangles of
contrasting colors (currently fixed at light blue and black) that surround the
element. Sometimes, a second focus ring will appear onscreen, which has dashed
rather than solid lines. This dashed focus ring provides a preview of which node
will be focused, if the user selects the current node. It also provides a hint
that the current node is a navigational node, and is not directly actionable.

There are two types of navigational nodes: nodes that group other nodes
together, and the **back button**. The back button allows a user to exit the
current group, and move outwards towards the top-level node.


### Selection

All users have one switch dedicated to selecting elements. For some users, this
is the only input they have (they rely on auto-scan to advance to the next
node). So a user needs to be able to perform any action based on some series of
select actions. To support this, when multiple actions are available for a
single actionable node, we open the **action menu**, which displays the
available actions for the given node. The user can then navigate to the action
they wish to perform, and when they select an action the action is performed,
and in most cases the menu is closed.

Sometimes there is only one action available for a given node. In this case, the
action menu does not open. Instead, the available action is performed
automatically when the user presses *select*.


## Reporting bugs

Use bugs.chromium.org, filing bugs under the component
[OS>Accessibility>SwitchAccess](https://bugs.chromium.org/p/chromium/issues/list?sort=-opened&colspec=ID%20Pri%20M%20Stars%20ReleaseBlock%20Component%20Status%20Owner%20Summary%20OS%20Modified&q=component%3AOS%3EAccessibility%3ESwitchAccess%20&can=2).

## Developing

### Code location

Switch Access code lives mainly in four places:

- A component extension to do the bulk of the logic and processing,
`chrome/browser/resources/chromeos/accessibility/switch_access/`

- In the `AccessibilityEventRewriter`,
`ash/events/accessibility_event_rewriter.h`

- The Switch Access menu and back button code, in `ash/system/accessibility/`

- The Switch Access settings page,
`chrome/browser/resources/settings/chromeos/os_a11y_page/switch_access_subpage.*`


### Tests

Tests are in `unit_tests`, `ash_unittests`, and `browser_tests`:

```
out/Release/unit_tests --gtest_filter="*SwitchAccess*"
out/Release/ash_unittests --gtest_filter="*SwitchAccess*"
out/Release/browser_tests --gtest_filter="*SwitchAccess*"
```


### Debugging

Developers can add log lines to any of the C++ files and see output in the
console. To debug the Switch Access extension, the easiest way is from an
external browser. Start Chrome OS on Linux with this command-line flag:

```
out/Release/chrome --remote-debugging-port=9222
```

Now open http://localhost:9222 in a separate browser, and debug the Switch
Access extension background page from there.


## How it works

Like [Chromevox](chromevox.md) and [Select to Speak](select_to_speak.md),
Switch Access is implemented mainly as a component Chrome extension which
is always loaded and running in the background when enabled, and unloaded
when disabled. Unlike Chromevox and Select to Speak, the settings for
Switch Access are not located within the component extension. Instead,
they are found with the other settings in the Settings app.

There are a handful of tasks which, for various reasons, are handled in
C++ code. These are:

- Event forwarding. In order to capture the events before other system
functions, we use an Event Rewriter (shared with Chromevox).

- Rendering the menu. To allow the menu UI to match other system UI, the process
of creating the menu UI is done in C++.

The Switch Access extension does the following, at a high level:

1. Listens for SwitchAccessCommands, which are the user-initiated commands like
"select" or "next" that the user performs by pressing their switch.

2. Maps those commands to the appropriate behavior:

- If it is a navigation command, the focused node is changed and focus rings
are updated.

- If it is a select command, the available actions are determined.

If there is only one, it is performed (Note that for navigational nodes, this
means entering or exiting a group). If more than one action is available, the
menu is opened and focus jumps into the menu.

3. Listens for focus events, and moves Switch Access focus to follow system
focus.

4. Listens for location changes and other tree changes that affect the current
node or its parent or siblings, and update the focused node and focus rings when
necessary.


## Switch Access extension structure

### Managers

Switch Access divides tasks by function, each being handled by a manager class.
This section details what those managers are, their guiding principle, and what
tasks they are responsible for.

#### ActionManager

The ActionManager is responsible for what happens after a user presses their
*Select* switch. This includes determining what actions are available,
performing actions, and specifying what actions to display in and where to show
the action menu and sub-menus when needed.

The specifics of opening and closing the menu are not handled here, but are
encapsulated in the MenuManager. However, all calls to open or close the menu
should be made on the ActionManager, rather than the MenuManager directly,
because the ActionManager maintains state about sub-menus and similar, which
needs to remain in sync with the menu displayed.

#### AutoScanManager

The AutoScanManager handles the process of automatically scanning from one
element to the next. If the user has enabled auto-scan, it sets an interval
to call `NavigationManager.moveForward()` periodically. It also resets the
interval each time a command is received, or a focus event causes the focused
node to change.

#### FocusRingManager

The FocusRingManager determines what focus rings should be drawn and where to
show them, and calls the `accessibilityPrivate` API.

#### MenuManager

The MenuManager handles the details of displaying the action menu, given a
list of actions and a location. It also waits for the menu to load, and jumps
Switch Access focus to the menu when it is ready.

Calls to open or close a menu should be handled by the ActionManager, rather
than by the MenuManager directly, to keep state in sync.

#### NavigationManager

The NavigationManager handles tracking and changing the current Switch Access
focus. This includes handling moving forward or backward from a user command,
moving forward from auto-scan, jumping to a UI element when opened (such as the
keyboard or action menu), and following system focus. It also handles entering
and exiting groups when navigational nodes are selected.

The NavigationManager also has a method `getTreeForDebugging()` which can be
used to print either the current group or the entire tree to the console, to
help with debugging.

#### PreferenceManager

The PreferenceManager handles accessing user preferences, and updating
Switch Access' behavior accordingly. It also verifies whether the user has
a configuration that allows for full navigation.

#### TextNavigationManager

The TextNavigationManager handles tasks surrounding navigating through text.
Currently only text within editable text fields is supported, and only behind
the flag `enable-experimental-accessibility-switch-access-text`.


### Nodes

In addition to dividing the tasks by function, Switch Access delegates many
tasks to the nodes themselves. This allows specific behaviors to be changed
by changing what nodes are created based on the circumstances, and keeps the
ActionManager and NavigationManager from getting bloated with special cases,
and all the permutations of those special cases.

All of the nodes used by Switch Access have some relation to an **automation
node**, the underlying tree structure shared by all accessibility component
extensions. Most have a one-to-one association (one automation node for each
Switch Access node), but sometimes there are multiple Switch Access nodes for
the same automation node (such as when breaking a long list into smaller
groups).

#### SAChildNode and SARootNode

These are the two base types. They define the functions available on nodes
when they are functioning as either a selectable node, or the current group.

SAChildNode is an abstract class, but SARootNode can be used directly to
represent a group that does not have an underlying automation node.

#### BasicNode and BasicRootNode

These types implement a default behavior for nodes that have a one-to-one
association with an automation node. Information is derived directly from
the automation node to implement the interface provided by SAChildNode and
SARootNode.

#### BackButtonNode

The BackButtonNode is special because it is part of Switch Access' UI, and
it is shown for each group. Therefore, it automatically finds the automation
node corresponding to the back button by searching the tree, and instead
takes the SARootNode for its group in its constructor. It uses this to
determine where to show the back button, and what to do if it is selected.

#### ComboBoxNode

ComboBoxNode extends BasicNode, and only overrides a small number of functions
where combo boxes have special behavior. Specifically, combo boxes require some
special logic around moving focus into the dropdown when it opens.

#### DesktopNode

Represents the largest group possible, the entire computer desktop. Behaves
mostly like a SARootNode, but does not have a back button.

#### EditableTextNode

Editable text nodes have a very different set of actions. Currently supported
are opening the keyboard and using dictation, but many more actions (such as
selection and copy/paste) are available with the flag
`enable-experimental-accessibility-switch-access-text`.

#### GroupNode

A GroupNode represents a subset of the children of a single automation node,
used to break a single node with many children into intermediate nodes for
easier navigation.

Currently the only place this is used is to break the keyboard into rows, but
ideally this should be done for any group with more than some number of
children.

#### KeyboardNode and KeyboardRootNode

A KeyboardNode represents a button within the virtual keyboard. Because the
keys do not support actions through the automation API, the KeyboardNode
simulate a mouse press at the center of the button.

The KeyboardRootNode represents the keyboard as a whole. It handles finding
the buttons from the keyboard and grouping them into rows, as well as
finding the automation node representing the virtual keyboard and monitoring
the visibility of the keyboard, so if the user opens/closes the keyboard
other than via Switch Access we still detect it.

#### ModalDialogRootNode

Currently used when a menu is opened, it behaves exactly like a BasicRootNode
except that when the user exits, it fires an ESC key event to close the menu
or modal dialog.

#### SliderNode

Adds support for custom sliders by sending left/right arrow key events to
decrement/increment.

#### TabNode and ActionableTabNode

These two classes are primarily to allow the close button to both be
accessible via Switch Access while clearly displaying visually how to select
the tab. This is done by having TabNode be a group (when it contains a button),
and create an ActionableTabNode as a child whose location is just the portion of
the tab that doesn't overlap with the close button.

#### WindowRootNode

The WindowRootNode focuses a window when it is entered. Otherwise it behaves
exactly like a BasicRootNode.


### Other

There are some other classes that support Switch Access without being a manager
or node type directly.

#### background.js

This file is the first one run. Its primary job is to create an instance of
SwitchAccess, although it als overifies that there is not more than one instance
of Switch Access running simultaneously (this would normally happen on the sign
in page).

#### Commands

Commands translates a SwitchAccessCommand from the user into a call to the
appropriate function.

#### History

History helps store and recover state about what the current node and group are.
When a group is exited, the history provides the previous position to return to
(after verifying that it is still valid). It also builds a new history when
focus moves, capturing how the user would have gotten to that same node.

#### Metrics

A utility for recording metrics.

#### SACache

Implements a dynamic programming strategy for when walking the automation tree to
find interesting nodes. A cache is created for a single query, and should not be
used in nonconsecutive function calls, as the underlying data can change and the
SACache does not account for this.

#### SAConstants

This file contains most or all of the constants used throughout Switch Access.

#### SwitchAccess

Primarily, this class creates the managers that need to be explicitly initialized,
as well as the Commands object. It also handles creating errors (so we can track
metrics on how often different errors happen), and has a function to find a node
matching a predicate (or wait for it to be created).

#### SwitchAccessPredicate

Has a variety of predicates determining things about automation nodes, all
generally specific to Switch Access. Many of these functions utilize SACache.
It also creates the restrictions that can be passed to AutomationTreeWalker to
find the appropriate children for a given group node.


## For Googlers

For more, Googlers could check out the Switch Access feature design docs for
more details on design as well as UMA.

- Overall product design, [go/cros-switch](go/cros-switch)

- The navigation strategy, [go/cros-switch-navigation](go/cros-switch-navigation)

- The action menu, [go/cros-switch-menu](go/cros-switch-menu)

- The "preview" dashed focus ring, [go/cros-switch-dashed-focus](go/cros-switch-dashed-focus)

- The UX description slideshow, [go/cros-switch-access-ux](go/cros-switch-access-ux)

- The UI specification, [go/cros-switch-spec](go/cros-switch-spec)

- Testing, [go/cros-switch-testing](go/cros-switch-testing)

- Improved Text Input (still experimental), [go/cros-switch-text-input](go/cros-switch-text-input)

- Point Scanning (still under development), [go/cros-switch-point-scanning](go/cros-switch-point-scanning)

0 comments on commit 0652d35

Please sign in to comment.