Home | History | Annotate | Download | only in accessibility
      1 
      2 page.title=Developing an Accessibility Service
      3 parent.title=Implementing Accessibility
      4 parent.link=index.html
      5 
      6 trainingnavtop=true
      7 previous.title=Developing Accessible Applications
      8 previous.link=accessible-app.html
      9 
     10 @jd:body
     11 
     12 <div id="tb-wrapper">
     13 <div id="tb">
     14 
     15 <h2>This lesson teaches you to</h2>
     16 <ol>
     17   <li><a href="#create">Create Your Accessibility Service</a></li>
     18   <li><a href="#configure">Configure Your Accessibility Service</a></li>
     19   <li><a href="#events">Respond to AccessibilityEvents</a></li>
     20   <li><a href="#query">Query the View Heirarchy for More Context</a></li>
     21 </ol>
     22 
     23 <h2>You should also read</h2>
     24 <ul>
     25   <li><a href="{@docRoot}guide/topics/ui/accessibility/services.html">Building
     26   Accessibility Services</a></li>
     27 </ul>
     28 
     29 </div>
     30 </div>
     31 
     32 
     33 <p>Accessibility services are a feature of the Android framework designed to
     34 provide alternative navigation feedback to the user on behalf of applications
     35 installed on Android devices.  An accessibility service can communicate to the
     36 user on the application's behalf, such as converting text to speech, or haptic
     37 feedback when a user is hovering on an important area of the screen.  This
     38 lesson covers how to create an accessibility service, process information
     39 received from the application, and report that information back to the
     40 user.</p>
     41 
     42 
     43 <h2 id="create">Create Your Accessibility Service</h2>
     44 <p>An accessibility service can be bundled with a normal application, or created
     45 as a standalone Android project.  The steps to creating the service are the same
     46 in either situation.  Within your project, create a class that extends {@link
     47 android.accessibilityservice.AccessibilityService}.</p>
     48 
     49 <pre>
     50 package com.example.android.apis.accessibility;
     51 
     52 import android.accessibilityservice.AccessibilityService;
     53 
     54 public class MyAccessibilityService extends AccessibilityService {
     55 ...
     56     &#64;Override
     57     public void onAccessibilityEvent(AccessibilityEvent event) {
     58     }
     59 
     60     &#64;Override
     61     public void onInterrupt() {
     62     }
     63 
     64 ...
     65 }
     66 </pre>
     67 
     68 <p>Like any other service, you also declare it in the manifest file.
     69 Remember to specify that it handles the {@code android.accessibilityservice} intent,
     70 so that the service is called when applications fire an
     71 {@link android.view.accessibility.AccessibilityEvent}.</p>
     72 
     73 <pre>
     74 &lt;application ...&gt;
     75 ...
     76 &lt;service android:name=".MyAccessibilityService"&gt;
     77      &lt;intent-filter&gt;
     78          &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
     79      &lt;/intent-filter&gt;
     80      . . .
     81 &lt;/service&gt;
     82 ...
     83 &lt;/application&gt;
     84 </pre>
     85 
     86 <p>If you created a new project for this service, and don't plan on having an
     87 application, you can remove the starter Activity class (usually called MainActivity.java) from your source.  Remember to
     88 also remove the corresponding activity element from your manifest.</p>
     89 
     90 <h2 id="configure">Configure Your Accessibility Service</h2>
     91 <p>Setting the configuration variables for your accessibility service tells the
     92 system how and when you want it to run.  Which event types would you like to
     93 respond to?  Should the service be active for all applications, or only specific
     94 package names?  What different feedback types does it use?</p>
     95 
     96 <p>You have two options for how to set these variables.  The
     97 backwards-compatible option is to set them in code, using {@link
     98 android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}.
     99 To do that, override the {@link
    100 android.accessibilityservice.AccessibilityService#onServiceConnected()} method
    101 and configure your service in there.</p>
    102 
    103 <pre>
    104 &#64;Override
    105 public void onServiceConnected() {
    106     // Set the type of events that this service wants to listen to.  Others
    107     // won't be passed to this service.
    108     info.eventTypes = AccessibilityEvent.TYPE_VIEW_CLICKED |
    109             AccessibilityEvent.TYPE_VIEW_FOCUSED;
    110 
    111     // If you only want this service to work with specific applications, set their
    112     // package names here.  Otherwise, when the service is activated, it will listen
    113     // to events from all applications.
    114     info.packageNames = new String[]
    115             {"com.example.android.myFirstApp", "com.example.android.mySecondApp"};
    116 
    117     // Set the type of feedback your service will provide.
    118     info.feedbackType = AccessibilityServiceInfo.FEEDBACK_SPOKEN;
    119 
    120     // Default services are invoked only if no package-specific ones are present
    121     // for the type of AccessibilityEvent generated.  This service *is*
    122     // application-specific, so the flag isn't necessary.  If this was a
    123     // general-purpose service, it would be worth considering setting the
    124     // DEFAULT flag.
    125 
    126     // info.flags = AccessibilityServiceInfo.DEFAULT;
    127 
    128     info.notificationTimeout = 100;
    129 
    130     this.setServiceInfo(info);
    131 
    132 }
    133 </pre>
    134 
    135 <p>The second option is to configure the
    136 service using an XML file.  Certain configuration options like
    137 {@link android.R.attr#canRetrieveWindowContent} are only available if you
    138 configure your service using XML.  The same configuration options above, defined
    139 using XML, would look like this:</p>
    140 
    141 <pre>
    142 &lt;accessibility-service
    143      android:accessibilityEventTypes="typeViewClicked|typeViewFocused"
    144      android:packageNames="com.example.android.myFirstApp, com.example.android.mySecondApp"
    145      android:accessibilityFeedbackType="feedbackSpoken"
    146      android:notificationTimeout="100"
    147      android:settingsActivity="com.example.android.apis.accessibility.TestBackActivity"
    148      android:canRetrieveWindowContent="true"
    149 /&gt;
    150 </pre>
    151 
    152 <p>If you go the XML route, be sure to reference it in your manifest, by adding
    153 a <a
    154 href="{@docRoot}guide/topics/manifest/meta-data-element.html">&lt;meta-data&gt;</a> tag to
    155 your service declaration, pointing at the XML file.  If you stored your XML file
    156 in {@code res/xml/serviceconfig.xml}, the new tag would look like this:</p>
    157 
    158 <pre>
    159 &lt;service android:name=".MyAccessibilityService"&gt;
    160      &lt;intent-filter&gt;
    161          &lt;action android:name="android.accessibilityservice.AccessibilityService" /&gt;
    162      &lt;/intent-filter&gt;
    163      &lt;meta-data android:name="android.accessibilityservice"
    164      android:resource="@xml/serviceconfig" /&gt;
    165 &lt;/service&gt;
    166 </pre>
    167 
    168 <h2 id="events">Respond to AccessibilityEvents</h2>
    169 <p>Now that your service is set up to run and listen for events, write some code
    170 so it knows what to do when an {@link
    171 android.view.accessibility.AccessibilityEvent} actually arrives!  Start by
    172 overriding the {@link
    173 android.accessibilityservice.AccessibilityService#onAccessibilityEvent} method.
    174 In that method, use {@link
    175 android.view.accessibility.AccessibilityEvent#getEventType} to determine the
    176 type of event, and {@link
    177 android.view.accessibility.AccessibilityRecord#getContentDescription} to extract
    178 any label text associated with the view that fired the event.</pre>
    179 
    180 <pre>
    181 &#64;Override
    182 public void onAccessibilityEvent(AccessibilityEvent event) {
    183     final int eventType = event.getEventType();
    184     String eventText = null;
    185     switch(eventType) {
    186         case AccessibilityEvent.TYPE_VIEW_CLICKED:
    187             eventText = "Focused: ";
    188             break;
    189         case AccessibilityEvent.TYPE_VIEW_FOCUSED:
    190             eventText = "Focused: ";
    191             break;
    192     }
    193 
    194     eventText = eventText + event.getContentDescription();
    195 
    196     // Do something nifty with this text, like speak the composed string
    197     // back to the user.
    198     speakToUser(eventText);
    199     ...
    200 }
    201 </pre>
    202 
    203 <h2 id="query">Query the View Heirarchy for More Context</h2>
    204 <p>This step is optional, but highly useful. The Android platform provides the ability for an
    205 {@link android.accessibilityservice.AccessibilityService} to query the view
    206 hierarchy, collecting information about the UI component that generated an event, and
    207 its parent and children.  In order to do this, make sure that you set the
    208 following line in your XML configuration:</p>
    209 <pre>
    210 android:canRetrieveWindowContent="true"
    211 </pre>
    212 <p>Once that's done, get an {@link
    213 android.view.accessibility.AccessibilityNodeInfo} object using {@link
    214 android.view.accessibility.AccessibilityRecord#getSource}.  This call only
    215 returns an object if the window where the event originated is still the active
    216 window.  If not, it will return null, so <em>behave accordingly</em>.  The
    217 following example is a snippet of code that, when it receives an event, does
    218 the following:
    219 <ol>
    220   <li>Immediately grab the parent of the view where the event originated</li>
    221   <li>In that view, look for a label and a check box as children views</li>
    222   <li>If it finds them, create a string to report to the user, indicating
    223   the label and whether it was checked or not.</li>
    224   <li>If at any point a null value is returned while traversing the view
    225   hierarchy, the method quietly gives up.</li>
    226 </ol>
    227 
    228 <pre>
    229 
    230 // Alternative onAccessibilityEvent, that uses AccessibilityNodeInfo
    231 
    232 &#64;Override
    233 public void onAccessibilityEvent(AccessibilityEvent event) {
    234 
    235     AccessibilityNodeInfo source = event.getSource();
    236     if (source == null) {
    237         return;
    238     }
    239 
    240     // Grab the parent of the view that fired the event.
    241     AccessibilityNodeInfo rowNode = getListItemNodeInfo(source);
    242     if (rowNode == null) {
    243         return;
    244     }
    245 
    246     // Using this parent, get references to both child nodes, the label and the checkbox.
    247     AccessibilityNodeInfo labelNode = rowNode.getChild(0);
    248     if (labelNode == null) {
    249         rowNode.recycle();
    250         return;
    251     }
    252 
    253     AccessibilityNodeInfo completeNode = rowNode.getChild(1);
    254     if (completeNode == null) {
    255         rowNode.recycle();
    256         return;
    257     }
    258 
    259     // Determine what the task is and whether or not it's complete, based on
    260     // the text inside the label, and the state of the check-box.
    261     if (rowNode.getChildCount() &lt; 2 || !rowNode.getChild(1).isCheckable()) {
    262         rowNode.recycle();
    263         return;
    264     }
    265 
    266     CharSequence taskLabel = labelNode.getText();
    267     final boolean isComplete = completeNode.isChecked();
    268     String completeStr = null;
    269 
    270     if (isComplete) {
    271         completeStr = getString(R.string.checked);
    272     } else {
    273         completeStr = getString(R.string.not_checked);
    274     }
    275     String reportStr = taskLabel + completeStr;
    276     speakToUser(reportStr);
    277 }
    278 
    279 </pre>
    280 
    281 <p>Now you have a complete, functioning accessibility service.  Try configuring
    282 how it interacts with the user, by adding Android's <a
    283   href="http://android-developers.blogspot.com/2009/09/introduction-to-text-to-speech-in.html">text-to-speech
    284   engine</a>, or using a {@link android.os.Vibrator} to provide haptic
    285 feedback!</p>
    286