Inclusive Android Interfaces with Custom Accessibility Services
This article was peer reviewed by Adrian Sandu. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!
Spending time to develop an app that is accessible to all is an important task to empower potential users.
Android has built in accessibility features to help users who interact with their devices in different ways. These include autocomplete, a screen reader (TalkBack) and Text-to-speech output. So why create your own custom accessibility service?
Making your own accessibility service allows you to customize an app for a specific group of users that suits your app.
Handling Accessibility Services
The Android framework builds a event stream of everything on the screen and accessibility services subscribe to this stream, paying attention to different elements.
In this tutorial I will cover creating an accessibility service, which filters actions users make and based on those actions the service will use TextToSpeech to give feedback.
Let’s start building, you can find the code for the project on GitHub. Open Android Studio and create a new Blank Activity project.
Create a class with a file name of CustomAccessibilityService.java that extends AccessibilityService
and implements the override methods (onAccessibilityEvent
and onInterrupt
):
public class CustomAccessibilityService extends AccessibilityService {
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
}
@Override
public void onInterrupt() {
}
}
Declare the AccessibilityService in the Manifest File
Accessibility services must be explicitly declared in the application’s manifest file:
<application>
...
<!--Declare AccessibilityService-->
<service
android:name=".CustomAccessibilityService"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter>
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/service_configuration" />
</service>
...
</application>
The declaration specifies the name of the AccessibilityService
class with the permission BIND_ACCESSIBILITY_SERVICE
that distinguishes this service as an accessibility service. The meta data contains the service configuration resource file.
Configure the Accessibility Service
Configuring the accessibility service involves specifying the types of accessibility events that the service handles.
Create service_configuration.xml inside res/xml/ and add the following:
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeViewClicked|typeViewScrolled"
android:accessibilityFeedbackType="feedbackAllMask"
android:canRetrieveWindowContent="true"
android:notificationTimeout="100"
android:packageNames="valdioveliu.accessibilityservice" />
The most important setting here is the accessibilityEventTypes
which represent the event streams that this service subscribes to.
An important setting is the packageNames
declaration. If you don’t specify a package name for the accessibility service it will operate system wide. You can declare more than one packageName
for an accessibility service.
Implementing the Accessibility Service Class
The Accessibility service class is where all the magic happens. Add the following to CustomAccessibilityService.java:
public class CustomAccessibilityService extends AccessibilityService {
private TextToSpeech textToSpeech;
//Configure the Accessibility Service
@Override
protected void onServiceConnected() {
Toast.makeText(getApplication(), "onServiceConnected", Toast.LENGTH_SHORT).show();
//Init TextToSpeech
textToSpeech = new TextToSpeech(getApplication(), new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if (status == TextToSpeech.SUCCESS) {
int result = textToSpeech.setLanguage(Locale.US);
if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
Log.e("TextToSpeech", "Language not supported");
}
} else {
Log.e("TextToSpeech", "Initialization Failed! :( ");
}
}
});
}
//Respond to AccessibilityEvents
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED) {
Toast.makeText(getApplication(), event.getText().toString(), Toast.LENGTH_SHORT).show();
//TextToSpeech
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
textToSpeech.speak(event.getText().toString(), TextToSpeech.QUEUE_FLUSH, null, "TextToSpeech_ID");
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
textToSpeech.speak(event.getText().toString(), TextToSpeech.QUEUE_FLUSH, null);
}
} else if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_SCROLLED) {
//RecyclerView Scrolled
//TextToSpeech
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
textToSpeech.speak("Scrolling", TextToSpeech.QUEUE_FLUSH, null, "TextToSpeech_ID");
} else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
textToSpeech.speak("Scrolling", TextToSpeech.QUEUE_FLUSH, null);
}
}
}
@Override
public void onInterrupt() {
//Interrupt the Accessibility service
//Stop TextToSpeech
if (textToSpeech != null) {
textToSpeech.stop();
textToSpeech.shutdown();
}
}
}
The onAccessibilityEvent
method responds to incoming events, jumping in when an accessibility event that matches one of the accessibility events subscribed to is triggered by the user.
The onInterrupt
method is called when the Android framework wants to stop what the service is doing.
The onServiceConnected
sets up TextToSpeech
after the system has successfully bound to the service.
This accessibility service only works for clickable views. This means that it won’t work if a user tries to click an un-clickable view, for example a textView. To use the service in this case you need to add the following attribute to the view.
<TextView
...
android:clickable="true"
.../>
Note: To use an accessibility service on a device your user will have to enable the accessibility service in their device settings.
Conclusion
In this article I covered all that’s needed to implement an accessibility service. To truly make your app accessible I recommend you follow the Android guidelines, the detailed description of AccessibilityEvents and their methods and SitePoint’s own guide to Android accessibility features.