8.
Operable — Movement & Timing
Written by Tori Gonda
Timed content can be impossible for some people to consume. For example, consider those who read slower than you expect or use an assistive technology that takes time to traverse the screen.
Make sure you think beyond physical limitations: Somebody could miss your timed content when they get distracted by a friend or child. On top of that, some people find time limits to be stressful. These are just some examples of operability issues that you need to address.
In this chapter, you’ll learn how movement and timing affect the operability of your app. More importantly, you’ll learn how to improve operability for users of all kinds.
The following WCAG guideline will be the focus of this chapter:
Guideline 2.2 Enough Time: Provide users enough time to read and use content.
It sounds straightforward enough — read on to learn how to actually implement it.
Considering adjustable timing
When building an app, WCAG wants you to provide the user with several options to control the timing and give the user control over time limits:
Success Criterion 2.2.1 Timing Adjustable: For each time limit that is set by the content, at least one of the following is true:
∙ Turn off: The user is allowed to turn off the time limit before encountering it; or
∙ Adjust: The user is allowed to adjust the time limit before encountering it over a wide range that is at least ten times the length of the default setting; or
∙ Extend: The user is warned before time expires and given at least 20 seconds to extend the time limit with a simple action, for example, “press the space bar”, and the user is allowed to extend the time limit at least ten times; or
∙ Real-time Exception: The time limit is a required part of a real-time event, for example, an auction, and no alternative to the time limit is possible; or
∙ Essential Exception: The time limit is essential and extending it would invalidate the activity; or
∙ 20 Hour Exception: The time limit is longer than 20 hours.
Level A
This criterion gives six options to consider for time limits. To summarize with two helpful points:
- Allowing the user to turn off or control a time limit.
- Determining that the restriction doesn’t apply because it’s a requirement for the function or over twenty hours.
Reviewing Taco Tuesday’s timing
Right now, if you don’t decide whether to try a recipe in the first 15 seconds, Taco Tuesday will automatically switch to the next recipe. This behavior is not compliant with Success Criterion 2.2.1.
Open Taco Tuesday. You can pull Taco Tuesday from the starter project in the materials for this chapter, or you can use the one you’ve been working on through this book.
Build and run so that you can experience this timed behavior.
How would you change the behavior to conform to the guidance and success criterion?
Turn off
You can turn off the time limit in a couple of ways:
- Allow the user to choose not to have a time limit before encountering it, perhaps presenting the user an option during the on-boarding flow.
- Impose no time limit by default and provide an option to opt-in to a time limit in the settings.
Adjust
Let the user decide what length they want this time limit to be. Right now, the default is 15 seconds. You’d need to give an option at least ten times that length, which would be 150 seconds.
Extend
To extend, you’d need to slightly change the behavior and give the user at least 20 seconds of warning that the recipe will change. That means the default would need to be 20 seconds or more. You’d also need to show the user a countdown and provide a single-tap option to extend the limit.
Real-time exception
This doesn’t apply to Taco Tuesday because the app does not relate to something in the real world, such as a limited-duration sale or a due date.
If you have an app that falls into this category, you can make it compliant by informing the user how much time they have left.
Essential exception
Similar to the real-time exception, this scenario does not apply to Taco Tuesday. It’s relevant in time-based games where changing the timing would invalidate the game. Maybe you could gamify Taco Tuesday to make use of this guideline!
20 Hour exception
Finally, if the time limit exceeds 20 hours, you’re in the clear. How many milliseconds are in 20 hours?
Giving auto-update controls
For Taco Tuesday, you’ll fix the issues discussed above while also fixing it to comply with a related criterion:
Success Criterion 2.2.2 Pause, Stop, Hide: For moving, blinking, scrolling, or auto-updating information, all of the following are true:
∙ Moving, blinking, scrolling: For any moving, blinking or scrolling information that (1) starts automatically, (2) lasts more than five seconds, and (3) is presented in parallel with other content, there is a mechanism for the user to pause, stop, or hide it unless the movement, blinking, or scrolling is part of an activity where it is essential; and
∙ Auto-updating: For any auto-updating information that (1) starts automatically and (2) is presented in parallel with other content, there is a mechanism for the user to pause, stop, or hide it or to control the frequency of the update unless the auto-updating is part of an activity where it is essential.
Level A
Taco Tuesday auto-updates the content of the recipe cards. Per Success Criterion 2.2, you need to provide a way for the user to “pause, stop, or hide it or to control the frequency of the update.”
You’ll add a setting to allow the user to turn off the auto-advance feature. Then, in Chapter 11, “Designing for Neurodiversity”, you’ll improve this feature further to give an option for length of time.
Implementing auto-advance controls
Open root_preferences.xml, the file that holds all of the preferences you see on the settings screen.
To support multiple display options later on, add this PreferenceCategory to the top of the PreferenceScreen:
<PreferenceCategory app:title="@string/display_header">
</PreferenceCategory>
This creates an empty preference category with a header that signals it’s for display purposes.
Next, add a switch preference to your new preference category:
<SwitchPreferenceCompat
app:defaultValue="false"
app:key="auto_advance"
app:title="@string/preference_auto_advance_title" />
This adds a new switch preference to your settings screen. When it’s off, the recipes will not auto-advance, and when it’s on, they will.
Notice that you set the default to false. This is because the user currently doesn’t have an option to turn it off before they encounter the limit, which the success criterion specifies they should. So you turn it off for now.
Now, you need to use the value of the preference. Open DiscoverViewModel.kt.
Add the following property to the DiscoverViewModel constructor:
private val sharedPreferences: SharedPreferences
This creates a reference to SharedPreferences that you can read.
If the property is underlined in red it wasn’t automatically imported, import it by setting the cursor on SharedPreferences text marked red and pressing Alt-Enter.
The reference to SharedPreferences will be passed in — the dependency injection is preconfigured for you.
Next, find fetchRandomTaco() in DiscoverViewModel. Look for the statement that looks like this:
fetchTacoJob = viewModelScope.launch(Dispatchers.IO) {
delay(15000) // 15 seconds
fetchRandomTaco()
}
This code snippet is responsible for the 15-second auto-advance feature. Wrap it in the following if statement:
if (sharedPreferences.getBoolean("auto_advance", false)) {
// ...
}
This checks SharedPreferences for the settings and only advances the recipe if the user has turned on the feature.
Build and run.
Toggle your new auto-advance setting to make sure it works as expected. When it’s off, the recipes should not advance, and when on, they should advance every 15 seconds.
Managing interruptions
Interruptions are another example of events where timing matters. You may need to make adjustments to ensure your app is accessible. Consider the following criterion:
Success Criterion 2.2.4 Interruptions: Interruptions can be postponed or suppressed by the user, except interruptions involving an emergency.
Level AAA
Open Taco Tuesday to see how this works.
Every two minutes, you should see a pop-up dialog with a message. For another example of this, think about the last time an app asked you for a rating or urged you to take another action.
This behavior is disruptive. Consider how it works in Taco Tuesday:
- You’re trying to decide whether to try a recipe.
- But you’re on a time limit to decide before the next dialog shows up.
- Depending on the tools you use to interact with the app, you may need to start over after the dialog shows up.
Success Criterion 2.2.4 advises you to allow the user to delay an interruption.
To make Taco Tuesday compliant, you’ll remove the interruption entirely, but you’ll still share the information with the user. Instead of a disruptive dialog, you’ll use a banner across the top of the screen.
Replacing the dialog
You’ll start by adding a view to hold the banner.
Open activity_main.xml. In the parent ConstraintLayout, add the following TextView:
<TextView
android:id="@+id/main_banner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:padding="@dimen/space_normal"
android:textColor="?colorOnPrimary"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@tools:sample/lorem" />
This adds a full-width TextView with a background color to act as a banner. It’s in the MainActivity, so it will display above every fragment placed in this activity.
Add the following attribute to the fragment with ID nav_host_fragment in the same file:
app:layout_constraintTop_toBottomOf="@id/main_banner"
This line ensures the fragments align correctly below the banner.
Remove this attribute:
app:layout_constraintTop_toTopOf="parent"
This constrains the fragment view below the banner.
Next, you’ll populate this banner.
Open MainActivity.kt and find onCreate(). In this method, find the coroutine block that starts with lifecycleScope.launch.
Right under the randomMessage declaration, add the following:
binding.mainBanner.text = randomMessage
This sets the random message to the banner view.
Now, you can delete the dialog. In that same place, delete the following code:
if (dialog?.isShowing != true) {
dialog = AlertDialog.Builder(this@MainActivity)
.setMessage(randomMessage)
.setPositiveButton("Close", null)
.show()
}
This removes the code that displays the dialog.
Finally, remove this property from the top of MainActivity:
private var dialog: AlertDialog? = null
Here, you remove a property that’s no longer used; it referenced the dialog that no longer exists.
Build and run. Check out your new banner.
Fixing the tests
The tests will still look for the dialog you just deleted, so you need to modify the tests’ logic.
Run the tests in DiscoverFragmentTest to confirm failure. For reference, the tests are looking for a view that contains the text “Close”!
Open DiscoverFragmentTest.kt. In launchFragment(), delete the following:
Espresso.onView(ViewMatchers.withText("Close"))
.perform(ViewActions.click())
This change removes the dialog you deleted from the test criteria.
Rerun the tests to confirm that you don’t see this error.
Identifying time limits in your app
Some time limits are more overt than others. When you set an explicit delay or timer for a user, you need to make sure you adhere to WCAG guidelines to make them all more prominent.
But what about less-than-explicit cases? Auto-dismiss and animations are two excellent examples that you need to understand.
Auto-dismiss
Some Android components inherently dismiss after a set amount of time. Take the Snackbar for an example: This little view pops up on the bottom of the screen, has an auto-dismiss option and sometimes requests the user to perform some action.
Snackbars have many baked-in accessibility features. For example, they integrate with TalkBack. But you should not forget about them — even people who don’t use assistive technologies and have no disabilities could easily miss a Snackbar.
When adding a Snackbar, consider if it’s acceptable for the user to miss it or fail to react in time.
If the user must see the message or action, you need to either provide the information another way or make the Snackbar display until the user performs a specific action.
Animations
Animations are powerful tools to communicate ideas. Usually, when an animation completes, you move onto the next idea and related animation. But what if a user misses the information that goes with one of those animations?
Always make sure the user can see that information. The obvious way to do this is to create user controls to advance, pause, set the timing or go back.
Whatever you decide, make sure you follow WCAG guidelines so that your hard work is enjoyable for all users.
Mitigating risk of adverse reactions
For some users, on-screen movement causes adverse reactions. Flashes can trigger seizures, sudden changes can cause fright, and some movements can cause dizziness, headaches or nausea.
WCAG has a guideline for this:
Guideline 2.3 Seizures and Physical Reactions: Do not design content in a way that is known to cause seizures or physical reactions.
Since physical reactions can be life-threatening, Taco Tuesday doesn’t include any examples to fix. Instead of an exercise, you’ll read how to reduce the risk of your project causing negative reactions.
Eliminating flashes
If your app includes things that flash or images that rapidly change, you may need to comply with this criterion:
Success Criterion 2.3.1 Three Flashes or Below Threshold: Web pages do not contain anything that flashes more than three times in any one second period, or the flash is below the general flash and red flash thresholds.
Level A
The definition for “general flash and red flash thresholds” includes specific numbers to help you determine your app’s conformance, including guidelines related to speed, area on the screen, luminance and color.
You can find that definition at: https://www.w3.org/WAI/WCAG21/Understanding/three-flashes-or-below-threshold.html#dfn-general-flash-and-red-flash-thresholds.
Understanding compassionate motion
With this criterion, you’ll find that with animations, less really is more.
Success Criterion 2.3.3 Animation from Interactions: Motion animation triggered by interaction can be disabled, unless the animation is essential to the functionality or the information being conveyed.
Level AAA
Here are two best practices you can implement right away — people with conditions such as vestibular disorder, aka “inner ear” disorder, will appreciate it:
- Avoid unnecessary motion.
- Allow the user to turn off any non-essential animation.
Test out number two right now: Go to Accessibility Settings on your device and look for an option to turn off animations.
Now examine how the current settings affect animations in various apps. Does it turn them off? If you’re in your own app, it may or may not work for any home-grown animations. Many Android libraries already take care of this for you — some don’t.
The third best practice requires you to do more learning. You need to understand what kinds of movements are likely to trigger adverse reactions. A known trouble-maker is parallax scrolling. With this kind of scrolling, different items move at different speeds or directions. You can easily understand how that might cause a user difficulty.
Key points
- Provide users with more than enough time to complete a task.
- Give users control over any time limits your app imposes.
- Make sure a user can pause or return to any auto-updated content.
- Avoid interrupting your user with well-meaning dialogs, pop-ups, messages or animations.
- Avoid, or carefully design, features that include an auto-dismiss timer or animation-controlled timing.
- Make sure the user can easily control all speeds and extend any timers.
- Avoid adding anything that flashes to an app.
- Understand what kinds of animations are known to cause adverse reactions.
- Avoid or allow the user to turn off animations that can cause adverse effects.
Where to go from here?
This was the final chapter about making your app operable. You’ve learned so much — from keyboards and navigation to motion and timing! Pat yourself on the back.
Before moving onto Chapter 9, “Understandable”, take some time to investigate any of the Operable topics covered so far that you’re interested in or might apply to your app: https://www.w3.org/TR/WCAG21/#operable.