Inclusive Android Interfaces with Custom Accessibility Services

Share this article

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.

Frequently Asked Questions (FAQs) about Inclusive Android Interfaces with Custom Accessibility Services

What is an Accessibility Service in Android?

An Accessibility Service in Android is a special kind of service that assists users with disabilities in interacting with devices and apps. It receives callbacks from the system when AccessibilityEvents are fired, such as when a button is clicked, a screen is touched, or a text is spoken. These services can interpret these events and provide appropriate feedback to the user. This could be in the form of spoken text, haptic feedback, or visual cues.

How do I create a custom Accessibility Service?

Creating a custom Accessibility Service involves extending the AccessibilityService class and implementing its methods. You also need to declare the service in your app’s manifest file with an element. This element should specify the types of events the service wants to receive and the feedback it provides.

How can I capture all events from an Accessibility Service?

To capture all events from an Accessibility Service, you need to override the onAccessibilityEvent() method in your service. This method is called whenever an AccessibilityEvent is fired. You can then process the event as needed, such as by reading out the text of a button that was clicked or vibrating the device when a screen is touched.

What is an AccessibilityEvent in Android?

An AccessibilityEvent in Android is a system-level event that is fired when something notable happens in the user interface. For example, when a button is clicked, a screen is touched, or a text is spoken. These events are used by Accessibility Services to provide feedback to the user.

How can I make my app more accessible?

Making your app more accessible involves considering the needs of users with disabilities at every stage of the design and development process. This could involve providing alternative text for images, ensuring your app can be navigated with a keyboard, providing captions for videos, and making sure your app is compatible with screen readers and other assistive technologies.

How do I test the accessibility of my app?

Android provides several tools to help you test the accessibility of your app. These include the Accessibility Scanner, which can identify potential accessibility issues, and the TalkBack screen reader, which can help you understand how your app is experienced by users with visual impairments.

What is the role of the onServiceConnected() method in an Accessibility Service?

The onServiceConnected() method in an Accessibility Service is called when the service is first connected. This is where you can initialize your service and set up any necessary resources. For example, you might start a thread to listen for AccessibilityEvents or set up a speech synthesizer to provide spoken feedback.

How do I stop an Accessibility Service?

To stop an Accessibility Service, you can call the stopSelf() method from within the service. This will stop the service and release any resources it is using. Note that once a service is stopped, it cannot be restarted without the user going into the system settings and enabling it again.

Can I use an Accessibility Service to interact with other apps?

Yes, an Accessibility Service can interact with other apps by receiving and processing AccessibilityEvents from those apps. However, this requires the user to grant your service the necessary permissions, and it should be used responsibly to respect the user’s privacy and security.

What is the difference between an Accessibility Service and a regular Service in Android?

The main difference between an Accessibility Service and a regular Service in Android is that an Accessibility Service is designed to assist users with disabilities, while a regular Service is a general-purpose component that can perform long-running operations in the background. An Accessibility Service receives AccessibilityEvents from the system and provides feedback to the user, while a regular Service does not interact with the user interface directly.

Valdio VeliuValdio Veliu
View Author

Valdio recently graduated in Computer Engineering. He is a mobile developer, who is passionate about mobile technologies and learning new things. He has worked with languages such as C, Java, php and is currently focused on Java, mobile and web development

accessibilitychriswinclusive designux
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week