This is the fourth part of our series about providing accessibility in Android applications with Jetpack Compose. You can catch up on the previous parts.
In the last, fourth part, you’ll learn about list and link semantics. Moreover you’ll see how to test your app with various tools like TalkBack, Switch Access, Android Ally and Accessibility Scanner.
Why is accessibility so important?
The European Accessibility Act (EAA), also known as Directive 2019/882, will come into effect on June 28, 2025. So, if you’re developing mobile apps, it’s worth knowing how to ensure accessibility in your projects. Not doing so could lead to fines, legal issues, and significant operational challenges within the EU market. Meeting these standards not only keeps you in the clear legally but also helps you reach a wider audience and create a better experience for all users.
Read also: What is Accessibility in Mobile Apps? Legal Requirements in App Development Series
At Droids On Roids, we create digital products for clients around the globe, spanning industries from fintech and mCommerce to ecology. No matter the field, we know that accessibility is always crucial to an app’s success. That’s why we’re sharing our knowledge on this subject in a series of articles.
Let’s move on!
List and grid semantics
It is useful for TalkBack users to know when they are interacting with a list or a grid. There are semantic properties that you can use to mark a layout as a collection. Talkback will announce the total number of items in the collection and, moreover, it’ll add the current item’s position. In the case of a grid, it will also announce the number of columns. There is even the possibility of pointing out how many cells the item spans.
In Jetpack Compose, LazyColumn, LazyRow, LazyVerticalGrid, and LazyHorizontalGrid automatically apply list semantics. If you are using a Column or Row to create a list, you need to apply the semantics yourself.
There are two semantics properties that you need to use to apply list semantics to a layout. The first one is the collectionInfo
, which is applied to the enclosing layout composable. You set the number of rows and columns here. You can also pass -1 if that number is unknown. It may be useful in the case of endless, paginated lists. Note that, for one-dimensional lists, you should set the second parameter to one, not zero! For example, the regular, horizontally scrollable list has one column and many rows.
The second one is the collectionItemInfo
, which is applied to each list item composable. You set the row and column indices here. However, note that they are zero-based! You can also set the row and column span if the item spans many cells. Keep in mind that regular items span one cell, not zero!
It looks like this in action:
The code for the list from the video:
Column(
modifier = modifier.semantics {
collectionInfo = CollectionInfo(rowCount = 2, columnCount = 1)
}
) {
Text("Header")
Text(
modifier = Modifier.semantics {
collectionItemInfo = CollectionItemInfo(
rowIndex = 0, rowSpan = 1,
columnIndex = 0, columnSpan = 1
)
},
text = "First item"
)
Text(
modifier = Modifier.semantics {
collectionItemInfo = CollectionItemInfo(
rowIndex = 1, rowSpan = 1,
columnIndex = 0, columnSpan = 1
)
},
text = "Second"
)
}
Notice the header. Despite the fact it is in the same Column, it does not count as an indexed list item. But still, it is a part of the list.
Links
To make links accessible, you need to mark them appropriately. Links are not only the clickable elements. They have their own semantics. TalkBack and SwitchAccess display dedicated menus with a list of links.
Jetpack Compose has the AnnotatedString
, enabling you to create text with links. To mark the part of the text as a link, use the pushLink or addLink functions of the AnnotatedString.Builder
. They take the LinkAnnotation as the parameter.
See it in action:
Here is the code for the text with links:
Text(
text = buildAnnotatedString {
append("See the ")
pushLink(
LinkAnnotation.Url(
url = "https://thedroidsonroids.com/blog/",
styles = TextLinkStyles(
style = SpanStyle(color = Color.Blue, textDecoration = TextDecoration.Underline)
)
)
)
append("Droids on Roids blog")
pop()
append(", and our ")
pushLink(
LinkAnnotation.Url(
url = "https://www.thedroidsonroids.com/portfolio",
styles = TextLinkStyles(
style = SpanStyle(color = Color.Red, textDecoration = TextDecoration.Underline)
)
)
)
append("portfolio")
pop()
append(".")
}
)
Notice the pop()
function calls. They close the link. Without them, the text after the link would also become a part of the link. Moreover, the span style you applied appeared in the TalkBack link menu as well.
The url passed to LinkAnnotation.Url
is then opened using the current UriHandler. But you can also provide your own link click callback using the LinkInteractionListener
parameter.
There is also a more generic LinkAnnotation
subtype. It is LinkAnnotation.Clickable
, and it looks like this:
LinkAnnotation.Clickable(
tag = "portfolio",
linkInteractionListener = { tag ->
// Handle the click event
},
)
It is useful when you want to handle the link click event yourself. You can use one listener for many links. The tag parameter is a string that you can use to distinguish between the links.
Note that the snippets above use the APIs added in Compose 1.7.0. It is in beta at the time of writing this article. I decided to include them because the older approach, using ClickableText
, has been deprecated.
Accessibility testing tools
Accessibility testing is important for ensuring that Android apps are usable by all users, including those with disabilities. There are tools that are very helpful in this area:
TalkBack, the most popular screen reader for Android, enables users to interact with their devices using gestures and spoken feedback.
Switch Access provides an alternative navigation method for users with motor impairments. It enables them to control their devices using physical switches or gestures.
Accessibility Scanner scans the app’s UI for accessibility issues and provides suggestions for improvements. It helps developers identify and fix potential problems.
Additionally, the Android Ally plugin for Android Studio offers a suite of tools for simulating TalkBack actions and adjusting system settings.
Let’s dive into more detail about each of these tools.
TalkBack
TalkBack is the most popular screen reader **for Android. It is a part of the official **Android Accessibility Suite developed by Google. In addition to reading screen content aloud, users can interact with the device using gestures. With TalkBack, your app can be used without even seeing the screen. As a developer, you can also use TalkBack to test your app’s accessibility.
Before you enable TalkBack, keep in mind that it significantly changes the way you interact with the phone. Mainly, single taps don’t immediately trigger actions. They can only change the focus, and TalkBack then reads the content of the currently focused element. You need to double-tap to fire the click events. It makes sense because, if the user is blind, they need to know what they are about to click.
To enable TalkBack, go to the device settings, then Accessibility, and turn it on. You can also enable the “Display speech output” option. In the TalkBack Developer settings, you can see the text that TalkBack reads on the toasts, exactly like it was in the screencasts in this article.
Switch Access
**Switch Access** is another accessibility service for Android. It is designed for users with motor impairments, enabling them to interact with the device using one or more switches. The switches can be physical buttons on the phone, an external keyboard or even on dedicated devices like one in this photo:
Bluetooth switch, source.
The volume up and down buttons are the popular option when it comes to using physical buttons as the switches. You can also use them in app testing. Apart from these buttons, there are also Camera Switches. They use the camera to track the user’s head movements or other gestures.
Users can navigate through the screen using the switches. Finally, they can select the focused element which, once selected, is activated by one of the switches. Take a look how it works in action in the following video:
The video shows the Group Selection navigation with two switches. There are two groups: Blue and Red. Each of them has a distinct physical button assigned as a switch. For example, the Blue group can use the volume up button, and the Red group can use the volume down button.
After the user presses one of the buttons, each of the focusable elements of the app gets highlighted with the group’s color. Each group contains half of the elements. After choosing one of the groups, it gets divided into two smaller groups. Elements not belonging to the chosen group are out of the scope. Note that they lose the colored frames. Users repeat this process until there is only one element in the group. Then they can select it.
When Switch Access is enabled the menu button appears on the screen. This helps users to access the functionalities outside of the app, such as the navigation bar, or notifications. Note, however, that they don’t fall into any group. The menu may look like this:
The Group Selection is not the only navigation mode. There is also point scanning mode. Take a look at how it works:
Pressing the switch button the first time starts the scanning. The line first moves horizontally, and the user presses the blue group switch to stop the scanning on this axis. Then, the same process repeats for the vertical axis.
Accessibility Scanner
**Accessibility Scanner** is a tool developed by Google. It is available on the Google Play Store. It scans the screen content for accessibility issues and suggests fixes. Additionally, it works on installed and running apps, so you have to build and deploy the app to the device to use it. It also works on composable previews.
Here it is in action:
The code producing the screen from the video:
Box(
modifier = Modifier
.background(Color.LightGray)
) {
Text("X", color = Color.Gray)
}
Image(
painter = painterResource(id = R.drawable.ic_launcher_foreground),
contentDescription = "",
modifier = Modifier.size(64.dp)
)
Accessibility shortcuts
Have you noticed a small icon on the right side of the screen on the latest video? It is the shortcut to enable accessibility apps. It is not limited to the Accessibility Scanner, as you can also add TalkBack, Switch Access, and other accessibility services. You can enable it in each app settings screen, like on the following screenshot:
Enabling the accessibility shortcut.
You can move the shortcut bar to the left side of the screen or to any vertical position on the edge. You can also hide it partially by dragging it closer to the edge, which is useful when you don’t want it to obscure the content.
Android Ally
Android Ally is a third-party tool for accessibility testing. It is available as a Android Studio/IntelliJ plugin. It adds a pane with various TalkBack actions, such as showing a menu, swiping or navigating back. You can also *easily change the granularity of navigation *(by words, by paragraphs, headings, etc.).
All those actions are reachable using various gestures, like swiping with three fingers or swiping down then left, for example. It may be convenient for regular (especially blind) users. On the other hand, it may make the testing on emulators or remote devices nearly impossible. From the developer’s perspective, it is usually much easier to use the buttons in IDE.
Android Ally also provides the ability to change some of the system settings. This is not just limited to TalkBack either, as you can even enable the dark mode, change display density or change the font scale.
See more info about the Android Ally in the videos made by the author on the YouTube channel.
Android mobile app accessibility — wrap-up
In this article, you learned how to create accessible links using AnnotatedString. It makes the elements not only clickable but also recognizable by accessibility services. This ensures users navigating with TalkBack or Switch Access can interact with links seamlessly.
I’ve also mentioned testing your apps with dedicated tools like TalkBack, Switch Access, and Accessibility Scanner. By using them, you ensure that your implementations meet the needs of users with disabilities. You can also use the **Android Ally plugin for Android Studio **to simplify the testing processes.
In these articles, I’ve described how to enhance the accessibility of Android apps, from understanding the principles of accessibility to implementing TalkBack-friendly composables. I hope you found this series helpful and that you’ll continue to focus on accessibility in your Android apps. **By adopting the practices and techniques discussed, you can create more inclusive apps. **This will help you not only reach a wider audience, but also make a positive impact on the lives of users with disabilities.
Originally published at https://www.thedroidsonroids.com on July 29, 2024.
Top comments (0)