Jetpack Compose Accessibility: Getting Started
Almost everyone will experience a disability that impacts their ability to use a mobile phone at some point in their life. Adding accessibility features to your app will help broaden access to it. By Victoria Gonda.
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Sign up/Sign in
With a free Kodeco account you can download source code, track your progress, bookmark, personalise your learner profile and more!
Create accountAlready a member of Kodeco? Sign in
Contents
Jetpack Compose Accessibility: Getting Started
30 mins
- Getting Started
- Understanding Why Accessibility Matters
- Exploring the Bon Appetit App
- Enabling Accessibility Tools
- Creating an Emulator (Optional)
- Downloading Accessibility Services (Optional)
- Enabling Accessibility Services
- Navigating With TalkBack
- Understanding Semantics
- Writing an Accessibility Test
- Adding the Content Description
- Omitting Content Descriptions
- Understanding Jetpack Compose and Accessibility
- Grouping
- Heading Navigation
- Handling Actions
- Adding State Descriptions
- Testing Selectable
- Adding Action Labels
- Using Foundational Modifiers
- Understanding Touch Targets
- Testing Comprehensively
- Where to Go From Here
Downloading Accessibility Services (Optional)
If you’re working with an emulator, you’ll probably need to download accessibility services. Open the Play Store app and sign in with your Google account. Then, search for Android Accessibility Suite from Google.
Install the app, and you’re ready to move on.
Enabling Accessibility Services
You can enable most accessibility services through your device settings. Go to Settings ‣ Accessibility and notice the options available.
Depending on your device, some options you might see are display size and color correction. You’re encouraged to try out each of these services with your app. For this tutorial, you’re looking for TalkBack.
Tap TalkBack to open the TalkBack settings. Before enabling it, you need to know a few things, as you won’t be able to navigate your phone as you normally would.
Navigating With TalkBack
TalkBack is most often used by people with vision impairments. You navigate using gestures, and TalkBack reads the content out loud.
You swipe right to get to the next element, swipe left to move to the previous element and double-tap to select the current item. To select something, you can single-tap anywhere on the screen.
- Alt + Arrow Right to navigate to the next item.
- Alt + Arrow Left to navigate to the previous item.
- Alt + ENTER to select.
- Alt + Arrow Right to navigate to the next item.
- Alt + Arrow Left to navigate to the previous item.
- Alt + ENTER to select.
Use the toggle to turn on TalkBack and walk through the tutorial.
You can turn TalkBack on and off in two other ways: using a hardware shortcut and using adb. By default, you can press and hold both volume keys to toggle.
Alternatively, use the following adb commands to enable and disable it:
# Enable TalkBack
adb shell settings put secure enabled_accessibility_services com.google.android.marvin.talkback/com.google.android.marvin.talkback.TalkBackService
# Disable TalkBack
adb shell settings put secure enabled_accessibility_services com.android.talkback/com.google.android.marvin.talkback.TalkBackService
Once you’re feeling somewhat comfortable navigating, you’re ready to move on.
Understanding Semantics
There are two trees created from your composables. There’s the Composition that describes the UI, as well as the Semantics that describes the semantic meaning of your UI.
Both accessibility and testing services use the semantics tree. The accessibility services use the tree to provide information to the people using the services, and the testing framework uses it to make assertions.
Because both tests and accessibility services use the same tree, you can write tests for your accessibility and prevent regressions.
Before digging into fixing the accessibility of the app, first write a test to prove the correctness — yay, Test-Driven Development!
Writing an Accessibility Test
Try using TalkBack in the Bon Appetit menu app, and you might notice that you don’t audibly receive any meaningful information about the images. For the attributes, it just says “Attribute”. Without that information, it might come as a surprise to you that the Droid O’s are spicy!
You need to inform the accessibility services what these images are. You do that through content descriptions. You’ll learn more about these in the next section. For now, add a test for it.
Open MenuItemTest.kt and add the following test at the end of the class:
@Test
fun hasAttributeContentDescription() {
// 1
val contentDescription = "Gluten Free"
composeTestRule.setContent {
// 2
MenuItem(
dish = testDish,
onDishSelected = {},
)
}
// 3
composeTestRule
.onNodeWithContentDescription(contentDescription)
.assertIsDisplayed()
}
Here, you:
- Begin the test by describing the content description you’ll assert.
- Set the composable content to a
MenuItem
using thetestDish
. Look at thetestDish
, and notice that it’s gluten-free. - Assert that the gluten-free content description exists.
Run the test and see that it fails.
No surprise there! You’ve yet to add the content description.
Adding the Content Description
Content descriptions inform the semantic tree of what an element is so it can describe it to the user. It’s most often used for icons and images. While the accessibility services can use the text of a textual element for information, it doesn’t automatically have information about a graphic.
A content description should describe the meaning of an icon rather than what the icon is. For example, you should use “Back” for a left arrow and “Edit” for a pencil.
In Bon Appetit, you’ll want to use “Spicy” instead of “thermometer” and “Gluten-free” instead of “circle slash croissant”.
Lucky for you, these content descriptions are already included in the resources and data class for you.
Open DishAttributes.kt and find the Image
composable. Notice that the content description currently says "Attribute"
. This explains the unhelpful message you received before when using TalkBack.
Replace "Attribute"
with stringResource(it.descriptionRes)
. The resulting Image
composable should look like this:
Image(
painter = painterResource(it.iconRes),
contentDescription = stringResource(it.descriptionRes),
modifier = Modifier.height(16.dp)
)
And that’s all! Adding content descriptions only takes one line.
Now, run the automated test you already wrote to make sure it passes.
Also, build and run the app to make sure you receive the menu item attribute information when using TalkBack.
Omitting Content Descriptions
There are more images than just the attributes to consider. For example, there’s the leading image of the dish.
Sometimes the content in an app can be perfectly understood without the image. Maybe the image is there for additional decoration, or the information from the image can be found elsewhere. In this case, it makes sense for the accessibility services to ignore the image.
Because each dish has a description, you’ll treat the dish image as a simple decoration for this tutorial.
Open MenuItem.kt and find DishImage()
. It contains an Image
with a content description of "Image"
. Not very helpful!
To have the accessibility services ignore this image since the description is elsewhere, set the content description to null
. The resulting Image
should look like this:
Image(
painter = painterResource(dish.imageRes),
contentDescription = null,
modifier = Modifier.size(42.dp)
)
Now, TalkBack and other accessibility services will ignore this element. Build and run the app, and you’ll notice that you’re unable to focus on the image.
Before moving on, another place also has an unhelpful content description. In AddOrRemoveIcon()
, set the two content descriptions to null
. You’ll describe this state a different way in State Descriptions.