Search code examples
androidtalkbackaccessibility

disabling view announcement when focused (Talkback Enabled)


I'm using a custom dynamic contentDescription for my textview, so it has been implemented in my java class and not in my xml file.

private void enableButton(TextView textView, boolean enabled) {
    if (textView == null) {
        return;
    }
        if (!enabled) {
            textView.setContentDescription(textView.getResources().getString(R.string.install_disabled));
        } else {
            textView.setContentDescription(textView.getResources().getString(R.string.install));
        }
    textView.setEnabled(enabled);
}

After I'm enabling my textview to be clickable, and when talkback is enabled, focusing on my textview is announcing the state of my textview which is "disabled". Is there a way to not announce that state?

I do not want to set the accessibility to be not important because I still want my dynamic contentDescription to be recited when talkback users focus on the textview.

Suggestion: I believe the culprit is the "setEnabled" method that is somehow triggering and announcing the state of the textview, but I'm still not able to stop it from reciting that last.


Solution

  • My first answer is: LEAVE IT ALONE! The "disabled" announcement tells a TalkBack user that there is a user interface control there, that under some circumstances can be interacted with, but is not currently active. Given your description, this is exactly what you have. To remove the announcement is actually going to make things WORSE from an accessibility perspective, the explanations for why this is the case are covered in WCAG 1.3.1.

    Definitions:

    Button = android.widget.Button

    button = a user interface component that does something when you click it.

    Text = a user interface component that conveys information, but is not active

    Long story short, the fact that the control is ever in a state that it can be active and "not disabled" is significant information on its own, and SHOULD be shared with the user. ESPECIALLY since you're using a "TextView" to implement this. This isn't a super uncommon practice, but one of the ways TalkBack calculates roles (button, link, image, text, etc) is by looking at the Class/Type of object. So, when it sees a TextView, it is going to assume Text, unless you inform it otherwise. Now, since you have added click listeners to your TextView (or Gesture Recognizers, or whatever) TalkBack may be able to figure out that the thing you're dealing with is actually a "button", and it may not. REGARDLESS, the fact that this "button" (lower case B!) is not active is important state to share with the user, and communicates to them the fact that they can somehow enable it and come back and interact with it later. This is immensely important information! Imagine if every button/link on a WebPage looked exactly like plane text? How would you know what to interact with?

    Now, I will show you the different pieces of this puzzle, as information, but I really do encourage you to leave the announcement alone. This is coming from someone who routinely speaks at Accessibility conferences on Native Android development, PLEASE LEAVE THIS ANNOUNCEMENT IN. To not do so shows a misunderstanding of how users with sight impairments want to perceive controls within your application, and the information that is important to them.

    The setEnabled() function on a TextView corresponds directly with the isEnabled() property of AccessibilityNodeInfo.

    In order to implement the behavior you want, you want the AccessibilityNodeInfo representation of your TextView to be different from that of the actual representation of your TextView. In order to do this you can use AccessibilityDelegate, I'm actually not sure which callback you want to use. In one of these the node is likely to be "sealed" and in one of them it might not be sealed yet. You obviously want the one where the node is not yet sealed! Regardless the general approach is:

    textView.setAccessibilityDelegate(new View.AccessibilityDelegate() {
        @Override
        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) {
    
            // Let the default implementation populate the info.
            super.onInitializeAccessibilityNodeInfo(host, info);
    
            // Override this particular property
            info.setEnabled(whateverYouThinkItShouldBe);
        }
    });