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.

5 (2) · 1 Review

Download materials
Save for later
Share
You are currently viewing page 2 of 4 of this article. Click here to view the first page.

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.

Android Accessibility Suite logo.

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.

Note: The path to get to the accessibility settings on your device might be slightly different.

Accessibility settings on Android phone

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.

Note: If you don’t see TalkBack as an option, make sure you follow the steps under Downloading Accessibility Services.

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.

TalkBack settings with TalkBack shortcut enabled

Navigating With TalkBack

TalkBack is most often used by people with vision impairments. You navigate using gestures, and TalkBack reads the content out loud.

Note: Optionally, you can have the same content that’s being read to you printed on the screen. If you have developer options turned on, go to Settings ‣ Accessibility ‣ TalkBack ‣ Settings ‣ Developer settings ‣ Display speech output to enable this.

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.
Note: If you’re using an emulator, you can also navigate using the keyboard:

Use the toggle to turn on TalkBack and walk through the tutorial.

Note: If the tutorial doesn’t automatically show up for you, you can access it from the TalkBack screen via Settings ‣ Tutorial and help ‣ 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:

  1. Begin the test by describing the content description you’ll assert.
  2. Set the composable content to a MenuItem using the testDish. Look at the testDish, and notice that it’s gluten-free.
  3. Assert that the gluten-free content description exists.

Run the test and see that it fails.

Could not find any node that satisfies contentDescription.

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.

Note: You should always localize content descriptions, just as the other copy in your app.

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.

MenuItemTest and hasAttributeContentDescription both have check marks showing they passed the test

Also, build and run the app to make sure you receive the menu item attribute information when using TalkBack.

Spicy content description read.

Omitting Content Descriptions

There are more images than just the attributes to consider. For example, there’s the leading image of the dish.

10x Breakfast menu item with leading image circled

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.

Note: Learning to write meaningful content descriptions adds richness to your apps. The object, action, context strategy is a good guide to follow.

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.