Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to 'interrupt' an action from being performed in AccessibilityService?

What I am trying to do/What I have done: I am trying to make a very basic version of TalkBack for visually impaired users. I have made a simple accessibility service that reads the contentDescription of a button clicked by the user and reads it out loud.

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {

    // get the source node of the event
    AccessibilityNodeInfo source = event.getSource();
    if (source == null) {
        return;
    }

    // Check if a button is clicked and speak out the content
    if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_CLICKED
        && BUTTON_CLASS_NAME.equals(source.getClassName()) {
            Log.d("Button clicked", source.getViewIdResourceName().toString());
            convertTextToSpeech(source.getContentDescription().toString());
        }

    if (source != null)
        source.recycle();
    return;
}

The problem: But, this way the user can't listen to the button's description BEFORE actually performing the action that fires when the button is clicked. By the time the user listens to the description, the button has already been clicked and an action has been performed.

Question: How do I interrupt the action (eg: opening a new activity after clicking the button) from being performed so that the user can safely explore the current views on the screen with the feedback I provide without starting new activities or firing other actions?

Sort of like what happens in Talkback: the user single taps to hear the description, and double taps to perform the action. How does Talkback prevent an action from happening unless the user double taps? I have looked into TouchExplorationMode but I guess it is mostly used for gestures rather than clicks.

like image 242
XenoChrist Avatar asked Oct 31 '22 07:10

XenoChrist


1 Answers

You are looking in the wrong place. Once onAccessibilityEvent receives an event, the action has already taken place. It is simply informing you, a click event has occurred. It is already much too late to stop it.

You do indeed want TouchExplorationMode. Here's a quick and dirty implementation of TalkBack's handling, and how it causes the UI to behave the way it does, without a lot of the extra junk and exception handling. I've only contained the portions of this that matter for this feature. There's of course a lot of other necessary scaffolding, but this would distract from the key elements.

Contents of serviceConfig.xml:

<?xml version="1.0" encoding="utf-8"?>
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
    android:accessibilityEventTypes="typeAllMask"
    android:accessibilityFlags="flagRequestTouchExplorationMode"
    android:canRequestTouchExplorationMode="true"
    />

Partial Contents of A11yService.java

@Override
public void onAccessibilityEvent(AccessibilityEvent e) {

    switch (e.getEventType()) {
        case AccessibilityEvent.TYPE_VIEW_HOVER_ENTER: {
            e.getSource().performAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS);
        }
    }
}

TalkBack then intercepts gestures in onGesture, and for swipe right or swipe left, captures them, and does the same action (accessibility focus) just on the next or previous element in the AccessibilityNode traversal. Easy peasy!

like image 134
ChrisCM Avatar answered Nov 09 '22 10:11

ChrisCM