diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs index 303a6d4041d48..f003e753939be 100644 --- a/docs/html/resources/resources_toc.cs +++ b/docs/html/resources/resources_toc.cs @@ -278,6 +278,23 @@ + +
  • +
    + Implementing Accessibility + new!
    + +
  • + diff --git a/docs/html/training/accessibility/accessible-app.jd b/docs/html/training/accessibility/accessible-app.jd new file mode 100644 index 0000000000000..f4087b828bfd8 --- /dev/null +++ b/docs/html/training/accessibility/accessible-app.jd @@ -0,0 +1,194 @@ + +page.title=Developing Accessible Applications +parent.title=Implementing Accessibility +parent.link=index.html + +trainingnavtop=true +next.title=Developing an Accessibility Service +next.link=service.html + +@jd:body + + + + +
    +
    + +

    This lesson teaches you to

    +
      +
    1. Add Content Descriptions
    2. +
    3. Design for Focus Navigation
    4. +
    5. Fire Accessibility Events
    6. +
    7. Test Your Application
    8. +
    + + +

    You should also read

    + + + +
    +
    + +

    Android has several accessibility-focused features baked into the platform, +which make it easy to optimize your application for those with visual or +physical disabilities. However, it's not always obvious what the correct +optimizations are, or the easiest way to leverage the framework toward this +purpose. This lesson shows you how to implement the strategies and platform +features that make for a great accessibility-enabled Android application.

    + +

    Add Content Descriptions

    +

    A well-designed user interface (UI) often has elements that don't require an explicit +label to indicate their purpose to the user. A checkbox next to an item in a +task list application has a fairly obvious purpose, as does a trash can in a file +manager application. However, to your users with vision impairment, other UI +cues are needed.

    + +

    Fortunately, 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 {@link android.view.View#setContentDescription(CharSequence) +setContentDescription()} method, like this:

    + +
    +String contentDescription = "Select " + strValues[position];
    +label.setContentDescription(contentDescription);
    +
    + +

    This addition to your code is the simplest accessibility improvement you can make to your +application, but one of the most useful. 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 +{@link android.view.View#setFocusable View.setFocusable()} method on that UI +control, or by setting the {@code + android:focusable} +attrubute in your XML layout files.

    + +

    Also, each UI control has 4 attributes, +{@code + android:nextFocusUp}, +{@code + android:nextFocusDown}, +{@code + android:nextFocusLeft}, +and {@code + 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”
    +    ... />
    +
    + +

    Verify that your application works intuitively in these situations. The +easiest way is to simply run your application in the Android emulator, and +navigate around the UI with the emulator's arrow keys, using the OK button as a +replacement for touch to select UI controls.

    + +

    Fire Accessibility Events

    +

    If you're using the view components in the Android framework, an +{@link android.view.accessibility.AccessibilityEvent} is created whenever you +select an item or change focus in your UI. These events are examined by the +accessibility service, enabling it to provide features like text-to-speech to +the user.

    + +

    If you write a custom view, make sure it fires events at the appropriate +times. Generate events by calling {@link +android.view.View#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 {@link +android.view.accessibility.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 +{@link android.view.accessibility.AccessibilityEvent#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.

    + +

    Also, attempt to navigate your application using a directional controller, +instead of the touch screen. You can use a physical device with a d-pad or +trackball if one is available. If not, use the Android emulator and it's +simulated keyboard controls.

    + +

    Between the service providing feedback and the directional navigation through +your application, you should get a sense of what your application is like to +navigate without any visual cues. Fix problem areas as they appear, and you'll +end up with with a more accessible Android application.

    diff --git a/docs/html/training/accessibility/index.jd b/docs/html/training/accessibility/index.jd new file mode 100644 index 0000000000000..d5178a9a2551d --- /dev/null +++ b/docs/html/training/accessibility/index.jd @@ -0,0 +1,56 @@ +page.title=Implementing Accessibility + +trainingnavtop=true +startpage=true +next.title=Developing Accessible Applications +next.link=accessible-app.html + +@jd:body + +
    +
    + +

    Dependencies and prerequisites

    + + +

    You should also read

    + + +
    +
    + +

    When it comes to reaching as wide a userbase 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 class 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.

    + +

    Lessons

    + +
    +
    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.
    + +
    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.
    + +
    + diff --git a/docs/html/training/accessibility/service.jd b/docs/html/training/accessibility/service.jd new file mode 100644 index 0000000000000..f62506b1baa22 --- /dev/null +++ b/docs/html/training/accessibility/service.jd @@ -0,0 +1,286 @@ + +page.title=Developing an Accessibility Service +parent.title=Implementing Accessibility +parent.link=index.html + +trainingnavtop=true +previous.title=Developing Accessible Applications +previous.link=accessible-app.html + +@jd:body + +
    +
    + +

    This lesson teaches you to

    +
      +
    1. Create Your Accessibility Service
    2. +
    3. Configure Your Accessibility Service
    4. +
    5. Respond to AccessibilityEvents
    6. +
    7. Query the View Heirarchy for More Context
    8. +
    + +

    You should also read

    + + +
    +
    + + +

    Accessibility services are a feature of the Android framework designed to +provide alternative navigation feedback to the user on behalf of applications +installed on Android devices. An accessibility service can communicate to the +user on the application's behalf, such as converting text to speech, or haptic +feedback when a user is hovering on an important area of the screen. This +lesson covers how to create an accessibility service, process information +received from the application, and report that information back 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 {@link +android.accessibilityservice.AccessibilityService}.

    + +
    +package com.example.android.apis.accessibility;
    +
    +import android.accessibilityservice.AccessibilityService;
    +
    +public class MyAccessibilityService extends AccessibilityService {
    +...
    +    @Override
    +    public void onAccessibilityEvent(AccessibilityEvent event) {
    +    }
    +
    +    @Override
    +    public void onInterrupt() {
    +    }
    +
    +...
    +}
    +
    + +

    Like any other service, you also declare it in the manifest file. +Remember to specify that it handles the {@code android.accessibilityservice} intent, +so that the service is called when applications fire an +{@link android.view.accessibility.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

    +

    Setting the configuration variables for your accessibility service tells the +system how and when you want it to run. Which event types would you like to +respond to? Should the service be active for all applications, or only specific +package names? What different feedback types does it use?

    + +

    You have two options for how to set these variables. The +backwards-compatible option is to set them in code, using {@link +android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. +To do that, override the {@link +android.accessibilityservice.AccessibilityService#onServiceConnected()} method +and configure your service in there.

    + +
    +@Override
    +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);
    +
    +}
    +
    + +

    Starting with Android 4.0, there is a second option available: configure the +service using an XML file. Certain configuration options like +{@link android.R.attr#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 {@code 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 {@link +android.view.accessibility.AccessibilityEvent} actually arrives! Start by +overriding the {@link +android.accessibilityservice.AccessibilityService#onAccessibilityEvent} method. +In that method, use {@link +android.view.accessibility.AccessibilityEvent#getEventType} to determine the +type of event, and {@link +android.view.accessibility.AccessibilityEvent#getContentDescription} to extract +any label text associated with the fiew that fired the event. + +

    +@Override
    +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. One of the new features in Android +4.0 (API Level 14) is the ability for an +{@link android.accessibilityservice.AccessibilityService} to query the view +hierarchy, collecting information about the 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 {@link +android.view.accessibility.AccessibilityNodeInfo} object using {@link +android.view.accessibility.AccessibilityEvent#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: +

      +
    1. Immediately grab the parent of the view where the event originated
    2. +
    3. In that view, look for a label and a check box as children views
    4. +
    5. If it finds them, create a string to report to the user, indicating + the label and whether it was checked or not.
    6. +
    7. If at any point a null value is returned while traversing the view + hierarchy, the method quietly gives up.
    8. +
    + +
    +
    +// Alternative onAccessibilityEvent, that uses AccessibilityNodeInfo
    +
    +@Override
    +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 {@link android.os.Vibrator} to provide haptic +feedback!