1 2page.title=Developing an Accessibility Service 3parent.title=Implementing Accessibility 4parent.link=index.html 5 6trainingnavtop=true 7previous.title=Developing Accessible Applications 8previous.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 34provide alternative navigation feedback to the user on behalf of applications 35installed on Android devices. An accessibility service can communicate to the 36user on the application's behalf, such as converting text to speech, or haptic 37feedback when a user is hovering on an important area of the screen. This 38lesson covers how to create an accessibility service, process information 39received from the application, and report that information back to the 40user.</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 45as a standalone Android project. The steps to creating the service are the same 46in either situation. Within your project, create a class that extends {@link 47android.accessibilityservice.AccessibilityService}.</p> 48 49<pre> 50package com.example.android.apis.accessibility; 51 52import android.accessibilityservice.AccessibilityService; 53 54public class MyAccessibilityService extends AccessibilityService { 55... 56 @Override 57 public void onAccessibilityEvent(AccessibilityEvent event) { 58 } 59 60 @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. 69Remember to specify that it handles the {@code android.accessibilityservice} intent, 70so that the service is called when applications fire an 71{@link android.view.accessibility.AccessibilityEvent}.</p> 72 73<pre> 74<application ...> 75... 76<service android:name=".MyAccessibilityService"> 77 <intent-filter> 78 <action android:name="android.accessibilityservice.AccessibilityService" /> 79 </intent-filter> 80 . . . 81</service> 82... 83</application> 84</pre> 85 86<p>If you created a new project for this service, and don't plan on having an 87application, you can remove the starter Activity class (usually called MainActivity.java) from your source. Remember to 88also 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 92system how and when you want it to run. Which event types would you like to 93respond to? Should the service be active for all applications, or only specific 94package names? What different feedback types does it use?</p> 95 96<p>You have two options for how to set these variables. The 97backwards-compatible option is to set them in code, using {@link 98android.accessibilityservice.AccessibilityService#setServiceInfo(android.accessibilityservice.AccessibilityServiceInfo)}. 99To do that, override the {@link 100android.accessibilityservice.AccessibilityService#onServiceConnected()} method 101and configure your service in there.</p> 102 103<pre> 104@Override 105public 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>Starting with Android 4.0, there is a second option available: configure the 136service using an XML file. Certain configuration options like 137{@link android.R.attr#canRetrieveWindowContent} are only available if you 138configure your service using XML. The same configuration options above, defined 139using XML, would look like this:</p> 140 141<pre> 142<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/> 150</pre> 151 152<p>If you go the XML route, be sure to reference it in your manifest, by adding 153a <a 154href="{@docRoot}guide/topics/manifest/meta-data-element.html"><meta-data></a> tag to 155your service declaration, pointing at the XML file. If you stored your XML file 156in {@code res/xml/serviceconfig.xml}, the new tag would look like this:</p> 157 158<pre> 159<service android:name=".MyAccessibilityService"> 160 <intent-filter> 161 <action android:name="android.accessibilityservice.AccessibilityService" /> 162 </intent-filter> 163 <meta-data android:name="android.accessibilityservice" 164 android:resource="@xml/serviceconfig" /> 165</service> 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 170so it knows what to do when an {@link 171android.view.accessibility.AccessibilityEvent} actually arrives! Start by 172overriding the {@link 173android.accessibilityservice.AccessibilityService#onAccessibilityEvent} method. 174In that method, use {@link 175android.view.accessibility.AccessibilityEvent#getEventType} to determine the 176type of event, and {@link 177android.view.accessibility.AccessibilityEvent#getContentDescription} to extract 178any label text associated with the view that fired the event.</pre> 179 180<pre> 181@Override 182public 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. One of the new features in Android 2054.0 (API Level 14) is the ability for an 206{@link android.accessibilityservice.AccessibilityService} to query the view 207hierarchy, collecting information about the UI component that generated an event, and 208its parent and children. In order to do this, make sure that you set the 209following line in your XML configuration:</p> 210<pre> 211android:canRetrieveWindowContent="true" 212</pre> 213<p>Once that's done, get an {@link 214android.view.accessibility.AccessibilityNodeInfo} object using {@link 215android.view.accessibility.AccessibilityEvent#getSource}. This call only 216returns an object if the window where the event originated is still the active 217window. If not, it will return null, so <em>behave accordingly</em>. The 218following example is a snippet of code that, when it receives an event, does 219the following: 220<ol> 221 <li>Immediately grab the parent of the view where the event originated</li> 222 <li>In that view, look for a label and a check box as children views</li> 223 <li>If it finds them, create a string to report to the user, indicating 224 the label and whether it was checked or not.</li> 225 <li>If at any point a null value is returned while traversing the view 226 hierarchy, the method quietly gives up.</li> 227</ol> 228 229<pre> 230 231// Alternative onAccessibilityEvent, that uses AccessibilityNodeInfo 232 233@Override 234public void onAccessibilityEvent(AccessibilityEvent event) { 235 236 AccessibilityNodeInfo source = event.getSource(); 237 if (source == null) { 238 return; 239 } 240 241 // Grab the parent of the view that fired the event. 242 AccessibilityNodeInfo rowNode = getListItemNodeInfo(source); 243 if (rowNode == null) { 244 return; 245 } 246 247 // Using this parent, get references to both child nodes, the label and the checkbox. 248 AccessibilityNodeInfo labelNode = rowNode.getChild(0); 249 if (labelNode == null) { 250 rowNode.recycle(); 251 return; 252 } 253 254 AccessibilityNodeInfo completeNode = rowNode.getChild(1); 255 if (completeNode == null) { 256 rowNode.recycle(); 257 return; 258 } 259 260 // Determine what the task is and whether or not it's complete, based on 261 // the text inside the label, and the state of the check-box. 262 if (rowNode.getChildCount() < 2 || !rowNode.getChild(1).isCheckable()) { 263 rowNode.recycle(); 264 return; 265 } 266 267 CharSequence taskLabel = labelNode.getText(); 268 final boolean isComplete = completeNode.isChecked(); 269 String completeStr = null; 270 271 if (isComplete) { 272 completeStr = getString(R.string.checked); 273 } else { 274 completeStr = getString(R.string.not_checked); 275 } 276 String reportStr = taskLabel + completeStr; 277 speakToUser(reportStr); 278} 279 280</pre> 281 282<p>Now you have a complete, functioning accessibility service. Try configuring 283how it interacts with the user, by adding Android's <a 284 href="http://android-developers.blogspot.com/2009/09/introduction-to-text-to-speech-in.html">text-to-speech 285 engine</a>, or using a {@link android.os.Vibrator} to provide haptic 286feedback!</p> 287