Android Accessibility – The Missing Manual

This presentation was created for the Bangalore Accessibility Week at Intuit, October 2015. It’s a collection of hard to discover information on making an Android application accessible.


DevicesTesting is done both on actual devices and with automation, such as Calabases and Android Lint. Intuit’s product development also relies heavily on user testing, including users with disabilities.

Intuit also has a mobile device library that allows anyone within Intuit to check out a mobile device for testing. This has significantly lowered equipment cost and makes it much easier to test applications on an assortment of phones. This can be important as phone manufacturers may break accessibility, such as the Samsung keyboard.

Android Lint

Android lint allows you to run accessibility tests within your development environment. It’s easy to find and fix the issues.


This session, Improve your Android App’s Accessibility, from Google IO 2015 introduces AccessibilityChecks and teases the upcoming testing app.


TalkBack is the built in screen reader for Android devices. ChromeVox is the screen reader used in Chrome and on ChromeOS

Turn on TalkBack

This video shows how to enable TalkBack and how to use it as a developer. It also shows how to use the context menu for reading the entire screen and turning of TalkBack temporarily.

Two Fingered Gestures

It is easy to use custom gestures with Android. If the app depends on a single finger gesture, TalkBack will pass the same gesture with two fingers. This is very helpful with signatures and swipe navigation.

Android Accessibility Shortcut

Android provides a convenient shortcut for enabling TalkBack on a device. This short video shows how to use it.



Place this declaration on the container that includes data that changes dynamically. The new content will be announced when it appears.


This short video shows how a live-region’s content is announced whenever it is changed by deselecting a row within the table. This is an HTML page with aria-live=“assertive”


  • Swipes and other hard to discover actions
  • Actions are activated from the Local Context Menu
  • While this could be used to provide hints for actions, I haven’t found the documentation/examples on how this is accomplished.
Create AccessibilityAction
 * @param actionId The id for this action. This should either be one of
 * the standard actions or a specific action for your app. In that case it
 * is required to use a resource identifier.
 public AccessibilityAction(int id, CharSequence label)
 new AccessibilityAction(, getString(R.string.dismiss));
 new AccessibilityAction(ACTION_CLICK, getString(R.string.play_song));
// Constants for all the standard actions with default label:
Handling a Custom Action
eventView.setAccessibilityDelegate(new AccessibilityDelegate {
 public onInitializeAccessibilityNodeInfo(
        View host, AccessibilityNodeInfo info) {
   super.onInitializeAccessibilityNodeInfo(host, info);
   info.addAction(new AccessibilityAction(,
     public boolean performAccessibilityAction(
            View host, int action, Bundle args) {
      if (action == {} // Logic for action


diagram of UI layers within Android and setting noHideDescendants on unused layers. Use Auto on current layer

If you are using a stacked interface, where the hidden layers are still receiving focus, you can use importantForAccessibility to define the layers that are not important. Hamburger menus would be the most common use for this technique.

Set the visible layer to “auto”, which lets Android manage it’s functionality. Set the “hidden” layers to “noHideDesendants”, which should remove the layer and its children from the Accessibility API. Switch these values when you hide/show layers.

android:importantForAccessibility = "auto"
android:importantForAccessibility = "yes"
android:importantForAccessibility = "no"
android:importantForAccessibility = "noHideDescendants"


diagram of a modal layer within Android. Use <a href=setModal(true) to make sure it acts like a modal view”>

For popups, such as a set of options A better option, however, may be to use a ListPopupWindow or PopupWindow if you’re attempting to display information in a modal context. Just be sure to setModal(true) to ensure the contents of that window, and only the contents of that window are focusable with TalkBack.


Forms are critical for a great user experience and we need to make sure our users understand what each input represents.

Form Labels

Which is correct for your app?

  • android:hint
  • android:labelFor
  • android:contentDescription


The hint is like the HTML “placeholder” attribute. It is a visible label within the input and is surfaced to the Accessibility API when the input first receives focus. However, the hint is ignored when the input has a value. SSB Bart published a great article on this topic:
Android Accessibility Properties and TalkBack

Android form using android:hint, but no visual label

  • This create a placeholder text string within an input
  • This was the preferred method but is a hack
  • The hint is removed when a user adds a value to the input
  • Still a valid method of adding a label to an input


The strongest method for adding a label to an input is the “labelFor” method. If your app has a visual text label, add “labelFor” to this text view and point it to the form input. The user will always know what the form input represents.
Android app using visual labels

 android:text="Invoice amount"/>
 android:hint="Invoice Amount"/>


ContentDescription is much like HTML’s “aria-label”. It’s an invisible text string that is surfaced directly to the Accessibility API. Android documentation specifically warns against using the contentDescription directly on the input.

Note: For EditText fields, provide an android:hint attribute instead of a content description, to help users understand what content is expected when the text field is empty. When the field is filled, TalkBack reads the entered content to the user, instead of the hint text.
Making Applications Accessible – Android Developer Portal

  • Invisible description for TalkBack
  • Should not be used directly on an input
  • You can use it on an input’s container and combine with labelFor


It is possible to use “contentDescription” and “labelFor” to include a hidden label for your application. For instance, this pattern works with the recently introduced “textinputLayout” for Material Design layouts. This same pattern should work with a basic container around a form input.
Material design for form inputs include the android:hint as a visual label.

   android:seterror="Create a valid email address"

This is the pattern suggested for Material Design in Marshmallow. It has some bugs with Android support, but these should be solved soon. More information: Accessible Android Inputs with Material Design

Checking for TalkBack

AccessibilityManager am = (AccessibilityManager)
boolean isAccessibilityEnabled = am.isEnabled();
boolean isExploreByTouchEnabled = am.isTouchExplorationEnabled();

You can check to see if the user has talkBack enabled and then make modifications to your application. For instance, this could be used to add continue and back buttons to a swipe-based navigation interface.

More Android Documentation

Accessible Android Inputs with Material Design

Update – August 2016

I first wrote this post shortly after textinputlayout was introduced and there was a distinct lack of documentation. My original code example for setting the error was incorrect. Per Victor Tsaran:

There is no such attribute as android:setError. There is a method called setError in the View class that can set the error message dynamically. That method actually works with TalkBack.
Victor Tsaran

I will be updating the code examples soon. For now, do not include the android:seterror line shown below. I’m leaving it right now for archival purposes.

Google has done an admirable job defining the Material Design style guide. They’ve also begun rolling out new APIs that make it much easier to implement the interaction designs within Android and HTML. However, there are still some gaps. This article looks at the popular Text Input for Android interaction. Please note: the code in this article is not fully documented and the best practice may change as the Google Accessibility team updates their documentation. Consider this a beta pattern and I will gladly update it as we learn better practices.
Continue Reading Accessible Android Inputs with Material Design

The low-vision experience with an iPad

I’ve met many small business owners with low vision that love using an iPad, or other tablet, as their key device. The iPad allows entrepreneurs to use a single device to handle all of their business tasks, such as point of sale, payroll, accounting, purchasing, and creating estimates on location. As a person with low vision, the built-in zooming functionality makes it easy to explore the screen and interact with the elements.

Karo Caran introduces how a person with low vision uses an iPad in this video

She recently created this new vidoe for a presentation on design for low-vision users. There are some key takeaways, such as consistency of banners and using background colors to differentiate between header, body, and footer.

Inheriting user preferences

While zooming is a great way to review content, not all low vision users want to use their applications in this manner. Many simply want to user the built in settings for higher contrast, bigger text, and reduced transparency to use their iPad without continually zooming in and out.

Your applications can automatically inherit these functions if you use standard iOS/Android components and the iOS Dynamic Type. If the app isn’t using Dynamic Type, we’ll need to check for a user’s display preferences and modify the fonts, colors, animation, etc.

Bold Text

The following code checks to see if a person has bold text enabled in their preferences. If so, use Avenir medium instead of light

+ (NSString *)fontName {
    if (UIAccessibilityIsBoldTextEnabled()) {
            return @"Avenir-Medium";
    return @"Avenir-Light";

Darker Colors

We can detect if a user prefers darker colors, this is especially helpful for links that sit on colored backgrounds

+ (UIColor *)detailColor {
    if (UIAccessibilityDarkerSystemColorsEnabled()) {
        return [UIColor blackColor];
    return [UIColor grayColor];

Additional Checks

Block level links and screen reader support

Daniel Göransson introduced VoiceOver’s contradictory support for block level links on a mailing list for Apple accessibility. Specifically, Safari + VoiceOver on OSX allows the link to be announced as a single object, whereas on iOS, the link is announced as individual elements.

Prior to HTML5, the link tag was an inline element and was not meant to contain block level elements, such as headers. This led engineers to create multiple links as designs included teasers and id card modules. These may include a thumbnail photo, header, abstract, and read more link. Bruce Lawson describes this code simplicity: Block level links in HTML5.

Göransson describes the desktop vs. phone behavior:

In Mac Safari and VoiceOver using tab key all items get announced and works perfectly. But in iOS Safari and VO using swipe to next item or touch exploring, each item get announced separately.

This is actually the intended behavior, as Chris Fleizach explains:

iOS is much flatter than MacOS, so we want to give individual access to each element within a link. Whereas, on the Mac, you can navigate inside of that link group if you so desire.

So how could we trigger mobile users to hear the content as a single chunk instead of individual elements? The following code snippets are inspired by Göransson’s emails. He deserves credit for the ideas, I can accept blame for mis-representation.

Please Note: you may find it easier to view these examples outside the WordPress framework. Visit the block level example page.

Base code snippet

This example has a header, paragraph, and image. I’ve placed alt=”” on the image, as the surrounding text provides adequate description.

<a href="">
  <h4>The Deësis mosaic</h4>
    <img src="..." alt="">
    The Deësis mosaic ... </p>
  <p class="more">See the larger image on Flickr<p>

The Deësis mosaic

The Deësis mosaic (Δέησις, “Entreaty”) probably dates from 1261. It was commissioned to mark the end of 57 years of Roman Catholic use and the return to the Orthodox faith. It is the third panel situated in the imperial enclosure of the upper galleries. It is widely considered the finest in Hagia Sophia, because of the softness of the features, the humane expressions and the tones of the mosaic.

See the larger image on Flickr

Using ARIA to make the experience consistent

ARIA roles allow developers to define the purpose of content. Ideally, you should use the correct tag before even considering ARIA attributes. Use the role when HTML doesn’t provide an adequate tag (tabs), or the HTML equivalent is not as well supported by accessibility APIs (nav). Secondarily, roles are used to restore the semantics of code when the correct tag is not used (div based buttons).

This example is correctly using the link tag, as the user will be taken to a new source of content, such as a web site. I had initially suggested using role=”button”, as that would flatten the experience and should announce the entire content, regardless of platform.

<a href="" role="button">
  <h4>The Deësis mosaic</h4>
    <img src="..." alt="">
    The Deësis mosaic ... </p>
  <p class="more">See the larger image on Flickr<p>

The Deësis mosaic

The Deësis mosaic …

Using role=”button.

This would change the user’s expectation, as it announces this as a button instead of a link. It implies clicking on the object would trigger an action. But what is the action? Buttons should have a clear and concise call to action (“print”, “share”, “close”). This also means the link would appear within the quick menu for forms, rather than links.

Combining with aria-hidden

Göransson also noticed an unusual problem when placing aria-hidden=”true” on the image. This removed the image’s area from the touchable region.

Also when using anything with aria-hidden=”true” inside that <a>, the area where hidden item is located will become untouchable even if it’s still inside a <a>. For example, if I float an icon/image with aria-hidden to the left — that part of the <a> will be subtracted from the hitbox.

<a href="" role="button">
  <h4>The Deësis mosaic</h4>
    <img src="..." aria-hidden="true" alt="">
    The Deësis mosaic ... </p>
  <p class="more">See the larger image on Flickr<p>

The Deësis mosaic

The Deësis mosaic …

Added aria-hidden=”true” to the image.

Victor Tsaran re-iterated Chris Fleizach’s suggestion of using role=”text” to allow the block to be announced as a single unit.

I am not sure you can achieve this in Mobile Safari without perhaps having to resort to the role=text. The Voice Over navigation of the DOM is much flatter on iOS than on Mac OS and you are inserting a bunch of block elements into an inline one.

<a href="" role="text">
  <h4>The Deësis mosaic</h4>
    <img src="..." aria-hidden="true" alt="">
    The Deësis mosaic ... </p>
  <p class="more">See the larger image on Flickr<p>

The Deësis mosaic

The Deësis mosaic …

Using role=”text” on the link

But this changes the semantics of the link to a text node, removing it from the link menu.

Göransson’s next attempt wraps the link’s content in a div with role=”text”. This keeps the link’s semantics and the entire content is announced. However, this also removes the semantics of the h4, removing it from the header menu.

<a href="">
  <div role="text">
    <h4>The Deësis mosaic</h4>
      <img src="..." aria-hidden="true" alt="">
      The Deësis mosaic ... </p>
    <p class="more">See the larger image on Flickr<p>

The Deësis mosaic

The Deësis mosaic …

Wrapping the content within a div that has role=”text”

What is the answer?

This exploration could be summed up by Sina Barham’s tweet about trying to change the expected gestures.

While we could solve the issue of making a block level link work consistently across mobile and desktop browsers, that may not be the ideal solution. The user’s expectations are context dependent, this is especially true with navigation.