diff options
author | Alexander Lucas <alexlucas@google.com> | 2012-03-06 18:13:07 -0800 |
---|---|---|
committer | Alexander Lucas <alexlucas@google.com> | 2012-04-02 17:25:10 -0700 |
commit | df6c8270e300e2d04f57e24bb2b34c33edce4a0d (patch) | |
tree | 0ce203d4d1e189bbdea58ebe24b2c1a6f7dec409 /docs/html/training | |
parent | 0e87370077b322b3a88c345a44c6bf3d56617071 (diff) | |
download | frameworks_base-df6c8270e300e2d04f57e24bb2b34c33edce4a0d.zip frameworks_base-df6c8270e300e2d04f57e24bb2b34c33edce4a0d.tar.gz frameworks_base-df6c8270e300e2d04f57e24bb2b34c33edce4a0d.tar.bz2 |
initial accessibility class commit
Change-Id: Ib3b8f6e8f70f856e4bb9aae60b5d58d0f3e320be
Diffstat (limited to 'docs/html/training')
-rw-r--r-- | docs/html/training/accessibility/accessible-app.jd | 194 | ||||
-rw-r--r-- | docs/html/training/accessibility/index.jd | 56 | ||||
-rw-r--r-- | docs/html/training/accessibility/service.jd | 286 |
3 files changed, 536 insertions, 0 deletions
diff --git a/docs/html/training/accessibility/accessible-app.jd b/docs/html/training/accessibility/accessible-app.jd new file mode 100644 index 0000000..f4087b8 --- /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 + + + + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#contentdesc">Add Content Descriptions</a></li> + <li><a href="#focus">Design for Focus Navigation</a></li> + <li><a href="#events">Fire Accessibility Events</a></li> + <li><a href="#testing">Test Your Application</a></li> +</ol> + +<!-- other docs (NOT javadocs) --> +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/ui/accessibility/apps.html">Making + Applications Accessible</a></li> +</ul> + + +</div> +</div> + +<p>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.</p> + +<h2 id="contentdesc">Add Content Descriptions</h2> +<p>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.</p> + +<p>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 <a + href="https://market.android.com/details?id=com.google.android.marvin.talkback">TalkBack</a>. +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 <a + href="{@docRoot}reference/android/view.View#attr_android:contentDescription">android:contentDescription</a> attribute, like in this +example:</p> +<pre> +<Button + android:id=”@+id/pause_button” + android:src=”@drawable/pause” + android:contentDescription=”@string/pause”/> +</pre> + +<p>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:</p> + +<pre> +String contentDescription = "Select " + strValues[position]; +label.setContentDescription(contentDescription); +</pre> + +<p>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 <em>everything</em> 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.</p> + +<p>Try it out! Download <a + href="https://market.android.com/details?id=com.google.android.marvin.talkback">TalkBack</a> +(an accessibility service published by Google) and enable it in <strong>Settings + > Accessibility > TalkBack</strong>. Then navigate around your own +application and listen for the audible cues provided by TalkBack.</p> + +<h2 id="focus">Design for Focus Navigation</h2> +<p>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.</p> + +<p>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 <a + href="{@docRoot}android.view.View#attr_android:focusable">{@code + android:focusable}</a> +attrubute in your XML layout files.</p> + +<p>Also, each UI control has 4 attributes, +<a href="{@docRoot}reference/android/view/View#attr_android:nextFocusUp">{@code + android:nextFocusUp}</a>, +<a + href="{@docRoot}reference/android/view/View#attr_android:nextFocusDown">{@code + android:nextFocusDown}</a>, +<a + href="{@docRoot}reference/android/view/View#attr_android:nextFocusLeft">{@code + android:nextFocusLeft}</a>, +and <a + href="{@docRoot}reference/android/view/View#attr_android:nextFocusRight">{@code + android:nextFocusRight}</a>, +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. </p> + +<p>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.</p> + + +<pre> +<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” + ... /> +</pre> + +<p>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.</p> + +<h2 id="events">Fire Accessibility Events</h2> +<p>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.</p> + +<p>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. + +<p>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:</p> +<pre> +public void onTextChanged(String before, String after) { + ... + if (AccessibilityManager.getInstance(mContext).isEnabled()) { + sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED); + } + ... +} +</pre> + +<h2 id="testing">Test Your Application</h2> +<p>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 <a + href="https://play.google.com/store/details?id=com.google.android.marvin.talkback">Talkback</a>, +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.</p> + +<p>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.</p> + +<p>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.</p> diff --git a/docs/html/training/accessibility/index.jd b/docs/html/training/accessibility/index.jd new file mode 100644 index 0000000..d5178a9 --- /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 + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>Dependencies and prerequisites</h2> +<ul> + <li>Android 2.0 (API Level 5) or higher</li> +Playback</a></li> +</ul> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/ui/accessibility/index.html">Accessibility</a></li> +</ul> + +</div> +</div> + +<p>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.</p> + +<p>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 <strong>any</strong> Android application, not +just your own.</p> + +<h2>Lessons</h2> + +<dl> + <dt><b><a href="accessible-app.html">Developing Accessible Applications</a></b></dt> + <dd>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.</dd> + + <dt><b><a href="service.html">Developing Accessibility Services</a></b></dt> + <dd>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.</dd> + +</dl> + diff --git a/docs/html/training/accessibility/service.jd b/docs/html/training/accessibility/service.jd new file mode 100644 index 0000000..f62506b --- /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 + +<div id="tb-wrapper"> +<div id="tb"> + +<h2>This lesson teaches you to</h2> +<ol> + <li><a href="#create">Create Your Accessibility Service</a></li> + <li><a href="#configure">Configure Your Accessibility Service</a></li> + <li><a href="#events">Respond to AccessibilityEvents</a></li> + <li><a href="#query">Query the View Heirarchy for More Context</a></li> +</ol> + +<h2>You should also read</h2> +<ul> + <li><a href="{@docRoot}guide/topics/ui/accessibility/services.html">Building + Accessibility Services</a></li> +</ul> + +</div> +</div> + + +<p>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.</p> + + +<h2 id="create">Create Your Accessibility Service</h2> +<p>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}.</p> + +<pre> +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() { + } + +... +} +</pre> + +<p>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}.</p> + +<pre> +<application ...> +... +<service android:name=".MyAccessibilityService"> + <intent-filter> + <action android:name="android.accessibilityservice.AccessibilityService" /> + </intent-filter> + . . . +</service> +... +</application> +</pre> + +<p>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.</p> + +<h2 id="configure">Configure Your Accessibility Service</h2> +<p>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?</p> + +<p>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.</p> + +<pre> +@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); + +} +</pre> + +<p>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:</p> + +<pre> +<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" +/> +</pre> + +<p>If you go the XML route, be sure to reference it in your manifest, by adding +a <a +href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data></a> 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:</p> + +<pre> +<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> +</pre> + +<h2 id="events">Respond to AccessibilityEvents</h2> +<p>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.</pre> + +<pre> +@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); + ... +} +</pre> + +<h2 id="query">Query the View Heirarchy for More Context</h2> +<p>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:</p> +<pre> +android:canRetrieveWindowContent="true" +</pre> +<p>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 <em>behave accordingly</em>. The +following example is a snippet of code that, when it receives an event, does +the following: +<ol> + <li>Immediately grab the parent of the view where the event originated</li> + <li>In that view, look for a label and a check box as children views</li> + <li>If it finds them, create a string to report to the user, indicating + the label and whether it was checked or not.</li> + <li>If at any point a null value is returned while traversing the view + hierarchy, the method quietly gives up.</li> +</ol> + +<pre> + +// 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); +} + +</pre> + +<p>Now you have a complete, functioning accessibility service. Try configuring +how it interacts with the user, by adding Android's <a + href="http://developer.android.com/resources/articles/tts.html">text-to-speech + engine</a>, or using a {@link android.os.Vibrator} to provide haptic +feedback!</p> |