[Android Design Resources] hhung89:
How to make your app accessible to users with vision impairment or other physical disabilities.

Your customer base probably includes many people with disabilities, whether you are aware of it or not. According to government figures, one person in five has some functional limitation, and eight percent of all users on the Web have disabilities. In the United States alone there are more than 30 million people with disabilities who can be affected by the design of computer software, and worldwide the number is much higher. Many countries/regions, including the United States, have laws that mandate accessibility at some level.

When it comes to reaching as wide a user base as possible, it's important to pay attention to accessibility in your Android application. Cues in your user interface that may work for a majority of users, such as a visible change in state when a button is pressed, can be less optimal if the user is visually impaired.

This post shows you how to make the most of the accessibility features built into the Android framework. It covers how to optimize your app for accessibility, leveraging platform features like focus navigation and content descriptions. It also covers how to build accessibility services, that can facilitate user interaction with any Android application, not just your own.

Techniques should be applied to solve them:

1. Developing Accessible Applications

Learn to make your Android application accessible. Allow for easy navigation with a keyboard or directional pad, set labels and fire events that can be interpreted by an accessibility service to facilitate a smooth user experience.

Add Content Descriptions

It's easy to add labels to UI elements in your application that can be read out loud to your user by a speech-based accessibility service like TalkBack . If you have a label that's likely not to change during the lifecycle of the application (such as "Pause" or "Purchase"), you can add it via the XML layout, by setting a UI element's android:contentDescription attribute, like in this example:

<Button

android:id=”@+id/pause_button”

android:src=”@drawable/pause”

android:contentDescription=”@string/pause”/>

However, there are plenty of situations where it's desirable to base the content description on some context, such as the state of a toggle button, or a piece selectable data like a list item. To edit the content description at runtime, use the setContentDescription() method, like this:

String contentDescription = "Select " + strValues[position];

label.setContentDescription(contentDescription);

Try to add content descriptions wherever there's useful information, but avoid the web-developer pitfall of labelling everything with useless information. For instance, don't set an application icon's content description to "app icon". That just increases the noise a user needs to navigate in order to pull useful information from your interface.

Try it out! Download TalkBack (an accessibility service published by Google) and enable it in Settings > Accessibility > TalkBack. Then navigate around your own application and listen for the audible cues provided by TalkBack.

Design for Focus Navigation

Your application should support more methods of navigation than the touch screen alone. Many Android devices come with navigation hardware other than the touchscreen, like a D-Pad, arrow keys, or a trackball. In addition, later Android releases also support connecting external devices like keyboards via USB or bluetooth.

In order to enable this form of navigation, all navigational elements that the user should be able to navigate to need to be set as focusable. This modification can be done at runtime using the View.setFocusable() method on that UI control, or by setting the android:focusable attrubute in your XML layout files.

Also, each UI control has 4 attributes, android:nextFocusUp, android:nextFocusDown, android:nextFocusLeft, and android:nextFocusRight, which you can use to designate the next view to receive focus when the user navigates in that direction. While the platform determines navigation sequences automatically based on layout proximity, you can use these attributes to override that sequence if it isn't appropriate in your application.

For instance, here's how you represent a button and label, both focusable, such that pressing down takes you from the button to the text view, and pressing up would take you back to the button.

<Button android:id="@+id/doSomething"

android:focusable="true"

android:nextFocusDown=”@id/label”

... />

<TextView android:id="@+id/label"

android:focusable=”true”

android:text="@string/labelText"

android:nextFocusUp=”@id/doSomething”

... />

Fire Accessibility Events

If you write a custom view, make sure it fires events at the appropriate times. Generate events by calling sendAccessibilityEvent(int), with a parameter representing the type of event that occurred. A complete list of the event types currently supported can be found in the AccessibilityEvent reference documentation.

As an example, if you want to extend an image view such that you can write captions by typing on the keyboard when it has focus, it makes sense to fire an TYPE_VIEW_TEXT_CHANGED event, even though that's not normally built into image views. The code to generate that event would look like this:

public void onTextChanged(String before, String after) {

...

if (AccessibilityManager.getInstance(mContext).isEnabled()) {

sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED);

}

...

}

Test Your Application

Be sure to test the accessibility functionality as you add it to your application. In order to test the content descriptions and Accessibility events, install and enable an accessibility service. One option is Talkback , a free, open source screen reader available on Google Play. With the service enabled, test all the navigation flows through your application and listen to the spoken feedback.

2. Developing Accessibility Services

Develop an accessibility service that listens for accessibility events, mines those events for information like event type and content descriptions, and uses that information to communicate with the user. The example will use a text-to-speech engine to speak to the user.

Create Your Accessibility Service

An accessibility service can be bundled with a normal application, or created as a standalone Android project. The steps to creating the service are the same in either situation. Within your project, create a class that extends AccessibilityService.

package com.example.android.apis.accessibility;

import android.accessibilityservice.AccessibilityService;

public class MyAccessibilityService extends AccessibilityService {

...

[MENTION=439709]override[/MENTION]

public void onAccessibilityEvent(AccessibilityEvent event) {

}

[MENTION=439709]override[/MENTION]

public void onInterrupt() {

}

...

}

Like any other service, you also declare it in the manifest file. Remember to specify that it handles the android.accessibilityservice intent, so that the service is called when applications fire an AccessibilityEvent.

<application ...>

...

<service android:name=".MyAccessibilityService">

<intent-filter>

<action android:name="android.accessibilityservice.AccessibilityService" />

</intent-filter>

. . .

</service>

...

</application>

If you created a new project for this service, and don't plan on having an application, you can remove the starter Activity class (usually called MainActivity.java) from your source. Remember to also remove the corresponding activity element from your manifest.

Configure Your Accessibility Service

You have two options for how to set these variables. The backwards-compatible option is to set them in code, using setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo). To do that, override the onServiceConnected() method and configure your service in there. [MENTION=439709]override[/MENTION]

public void onServiceConnected() {

// Set the type of events that this service wants to listen to. Others

// won't be passed to this service.

info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED |

AccessibilityEvent.TYPE_VIEW_FOCUSED;

// If you only want this service to work with specific applications, set their

// package names here. Otherwise, when the service is activated, it will listen

// to events from all applications.

info.packageNames = new String[]

{"com.example.android.myFirstApp", "com.example.android.mySecondApp"};

// Set the type of feedback your service will provide.

info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;

// Default services are invoked only if no package-specific ones are present

// for the type of AccessibilityEvent generated. This service *is*

// application-specific, so the flag isn't necessary. If this was a

// general-purpose service, it would be worth considering setting the

// DEFAULT flag.

// info.flags = AccessibilityServiceInfo.DEFAULT;

info.notificationTimeout = 100;

this.setServiceInfo(info);

}

The second option is to configure the service using an XML file. Certain configuration options like canRetrieveWindowContent are only available if you configure your service using XML. The same configuration options above, defined using XML, would look like this:

<accessibility-service

android: accessibilityEventTypes="typeViewClicked|typeViewFocused"

android: packageNames="com.example.android.myFirstApp, com.example.android.mySecondApp"

android: accessibilityFeedbackType="feedbackSpoken"

android: notificationTimeout="100"

android: settingsActivity="com.example.android.apis.accessibility.TestBackActivity"

android: canRetrieveWindowContent="true"

/>

If you go the XML route, be sure to reference it in your manifest, by adding a <meta-data> tag to your service declaration, pointing at the XML file. If you stored your XML file in res/xml/serviceconfig.xml, the new tag would look like this:

<service android:name=".MyAccessibilityService">

<intent-filter>

<action android:name="android.accessibilityservice.AccessibilityService" />

</intent-filter>

<meta-data android:name="android.accessibilityservice"

android:resource="@xml/serviceconfig" />

</service>

Respond to AccessibilityEvents

Now that your service is set up to run and listen for events, write some code so it knows what to do when an AccessibilityEvent actually arrives! Start by overriding the onAccessibilityEvent(AccessibilityEvent) method. In that method, use getEventType() to determine the type of event, and getContentDescription() to extract any label text associated with the view that fired the event. [MENTION=439709]override[/MENTION]

public void onAccessibilityEvent(AccessibilityEvent event) {

final int eventType = event.getEventType();

String eventText = null;

switch(eventType) {

case AccessibilityEvent.TYPE_VIEW_CLICKED:

eventText = "Focused: ";

break;

case AccessibilityEvent.TYPE_VIEW_FOCUSED:

eventText = "Focused: ";

break;

}

eventText = eventText + event.getContentDescription();

// Do something nifty with this text, like speak the composed string

// back to the user.

speakToUser(eventText);

...

}

Query the View Heirarchy for More Context

This step is optional, but highly useful. The Android platform provides the ability for an AccessibilityService to query the view hierarchy, collecting information about the UI component that generated an event, and its parent and children. In order to do this, make sure that you set the following line in your XML configuration:

android:canRetrieveWindowContent="true"

Once that's done, get an AccessibilityNodeInfo object using getSource(). This call only returns an object if the window where the event originated is still the active window. If not, it will return null, so behave accordingly. The following example is a snippet of code that, when it receives an event, does the following:

.Immediately grab the parent of the view where the event originated

.In that view, look for a label and a check box as children views

.If it finds them, create a string to report to the user, indicating the label and whether it was checked or not.

.If at any point a null value is returned while traversing the view hierarchy, the method quietly gives up.

// Alternative onAccessibilityEvent, that uses AccessibilityNodeInfo [MENTION=439709]override[/MENTION]

public void onAccessibilityEvent(AccessibilityEvent event) {

AccessibilityNodeInfo source = event.getSource();

if (source == null) {

return;

}

// Grab the parent of the view that fired the event.

AccessibilityNodeInfo rowNode = getListItemNodeInfo(source);

if (rowNode == null) {

return;

}

// Using this parent, get references to both child nodes, the label and the checkbox.

AccessibilityNodeInfo labelNode = rowNode.getChild(0);

if (labelNode == null) {

rowNode.recycle();

return;

}

AccessibilityNodeInfo completeNode = rowNode.getChild(1);

if (completeNode == null) {

rowNode.recycle();

return;

}

// Determine what the task is and whether or not it's complete, based on

// the text inside the label, and the state of the check-box.

if (rowNode.getChildCount() < 2 || !rowNode.getChild(1).isCheckable()) {

rowNode.recycle();

return;

}

CharSequence taskLabel = labelNode.getText();

final boolean isComplete = completeNode.isChecked();

String completeStr = null;

if (isComplete) {

completeStr = getString(R.string.checked);

} else {

completeStr = getString(R.string.not_checked);

}

String reportStr = taskLabel + completeStr;

speakToUser(reportStr);

}

Now you have a complete, functioning accessibility service. Try configuring how it interacts with the user, by adding Android's text-to-speech engine, or using a Vibrator to provide haptic feedback!

3. Accessibility Checklist

Learn how to test your app for accessibility.

Testing Accessibility Features

Testing of accessibility features such as TalkBack, Explore by Touch and accessibility Gestures requires setup of your testing device. This section describes how to enable these features for accessibility testing.

Testing audible feedback

Audible accessibility feedback features on Android devices provide audio prompts that speaks the screen content as you move around an application. By enabling these features on an Android device, you can test the experience of users with blindness or low-vision using your application.

Audible feedback for users on Android is typically provided by TalkBack accessibility service and the Explore by Touch system feature. The TalkBack accessibility service comes preinstalled on most Android devices and can also be downloaded for free from Google Play.

Testing with TalkBack

The TalkBack accessibility service works by speaking the contents of user interface controls as the user moves focus onto controls. This service should be enabled as part of testing focus navigation and audible prompts.

To enable the TalkBack accessibility service:

Launch the Settings application.

Navigate to the Accessibility category and select it.

Select Accessibility to enable it.

Select TalkBack to enable it.

Note: While TalkBack is the most available Android accessibility service for users with disabilities, other accessibility services are available and may be installed by users.

Testing with Explore by Touch

The Explore by Touch system feature is available on devices running Android 4.0 and later, and works by enabling a special accessibility mode that allows users to drag a finger around the interface of an application and hear the contents of the screen spoken. This feature does not require screen elements to be focused using an directional controller, but listens for hover events over user interface controls.

To enable Explore by Touch:

Launch the Settings application.

Navigate to the Accessibility category and select it.

Select the TalkBack to enable it.

Note: On Android 4.1 (API Level 16) and higher, the system provides a popup message to enable Explore by Touch. On older versions, you must follow the step below.

Return to the Accessibility category and select Explore by Touch to enable it.

Note: You must turn on TalkBack first, otherwise this option is not available.

Testing focus navigation

Focus navigation is the use of directional controls to navigate between the individual user interface elements of an application in order to operate it. Users with limited vision or limited manual dexterity often use this mode of navigation instead of touch navigation. As part of accessibility testing, you should verify that your application can be operated using only directional controls.

You can test navigation of your application using only focus controls, even if your test devices does not have a directional controller. The Android Emulator provides a simulated directional controller that you can use to test navigation. You can also use a software-based directional controller, such as the one provided by the Eyes-Free Keyboard to simulate use of a D-pad on a test device that does not have a physical D-pad.

Testing gesture navigation

Gesture navigation is an accessibility navigation mode that allows users to navigate Android devices and applications using specific gestures. This navigation mode is available on Android 4.1 (API Level 16) and higher.

Note: Accessibility gestures provide a different navigation path than keyboards and D-pads. While gestures allow users to focus on nearly any on-screen content, keyboard and D-pad navigation only allow focus on input fields and buttons.

To enable gesture navigation:

Enable both TalkBack and the Explore by Touch feature as described in the Testing with Explore by Touch. When both of these features are enabled, accessibility gestures are automatically enabled.

You can change gesture settings using Settings > Accessibility > TalkBack > Settings > Manage shortcut gestures.

Note: Accessibility services other than TalkBack may map accessibility gestures to different user actions. If gestures are not producing the expected actions during testing, try disabling other accessibility services before proceeding.