4

I am looking to see if it's possible to change the checked/unchecked statement made during TalkBack for radio buttons in Android. When the radio button is currently in focus, TalkBack will say whether it is checked or unchecked. I wanted to see if it is possible to change it to say "selected" and "not selected".

With all the flexibility that Android has in controlling this I was just wanting to see if it can be done or not, and, if so, how to do it.

azizbekian
  • 60,783
  • 13
  • 169
  • 249
maedre45
  • 41
  • 2
  • have you poked around all the talkback settings? i know jaws (pc screen reader) lets you adjust what jaws says for certain objects, however nvda (another pc screen reader) does not. voiceover (ios) lets you create a dictionary of pronunciations. so some screen readers allow such detailed customizations and others don't. i'm not sure about talkback but it's probably worth going through all the settings. – slugolicious Dec 18 '18 at 17:09
  • I'm looking more for something to do this programatically. TalkBack is the primary system I am using, but there are others out there. I'm looking for a way of intercepting whatever the natural statement is and installing one of my own so that, no matter what system is used, it's always saying the same thing. That's the background idea of it, anyway. I know you can make a button say it's a squash by changing the class name in android for that button, and I thought that, maybe, there was a label or tag like that you could change to do the same for the checked / unchecked state. – maedre45 Dec 19 '18 at 14:23

3 Answers3

2

As can be seen in the sources, RadioButton declares itself to be a checkable accessibility node. What you can do, is reset that and mark it as selected:

    ViewCompat.setAccessibilityDelegate(radioButton, new AccessibilityDelegateCompat() {
        @Override
        public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfoCompat info) {
            super.onInitializeAccessibilityNodeInfo(host, info);
            info.setCheckable(false); // Checked/unchecked won't be pronounced
            info.setSelected(radioButton.isChecked()); // if item is selected, then "selected" will be pronounced
        }
    });
azizbekian
  • 60,783
  • 13
  • 169
  • 249
0

A slightly updated version of this answer in Kotlin.

override fun onInitializeAccessibilityEvent(event: AccessibilityEvent?) {
    super.onInitializeAccessibilityEvent(event)
    event?.isChecked = radioButton.isChecked
}

override fun onInitializeAccessibilityNodeInfo(info: AccessibilityNodeInfo?) {
    super.onInitializeAccessibilityNodeInfo(info)
    info?.isCheckable = true
    info?.isChecked = radioButton.isChecked
}
Wirling
  • 4,810
  • 3
  • 48
  • 78
0

You can custom a RadioButton class like:

class CustomRadioButton : AppCompatRadioButton {
    
    var talkBackString: CharSequence? = null

    // override to disable the "radiobutton" readout
    override fun getAccessibilityClassName(): CharSequence {
        return CustomRadioButton::class.java.simpleName
    }

    /**
     * [CustomRadioButton] default constructor
     * @param context is an activity [Context]
     * @param attrs is an [AttributeSet]
     * @param defStyle is [Int] value of style
     */
    constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(
        context,
        attrs,
        defStyle
    ) {
        setupView()
    }

    /**
     * [CustomRadioButton] default constructor
     * @param context is an activity [Context]
     * @param attrs is an [AttributeSet]
     */
    constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
        setupView()
    }

    /**
     * [CumtomRadioButton] default constructor
     * @param context is an activity [Context]
     */
    constructor(context: Context) : super(context) {
        setupView()
    }

    /**
     * This method is used to setup view's attributes
     */
    private fun setupView() {
        accessibilityDelegate = MyAccessibilityDelegate()
    }

    /**
     * Custom class AccessibilityDelegate
     */
    inner class MyAccessibilityDelegate : AccessibilityDelegate() {

        /**
         * Override function onInitializeAccessibilityNodeInfo of AccessibilityDelegate
         * @param host is [View]
         * @param info is [AccessibilityNodeInfo]
         */
        override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) {
            super.onInitializeAccessibilityNodeInfo(host, info)
            if (talkBackString.isNullOrEmpty() && host.contentDescription.isNullOrEmpty().not()) {
                talkBackString = host.contentDescription
            }
            info.apply {
                isCheckable = false
                // ...and then you can set whatever you want as a text
                //format: Radio button + desciption + state of radio button
                text = "Radio button" + "\n" + talkBackString.toString() + "\n" + getDescription()
            }
        }
    }


    override fun setChecked(checked: Boolean) {
        if (checked == isChecked) return
        super.setChecked(checked)
        // since we've disabled the checked/unchecked readouts
        // we are forced to manually announce changes to the state
        announceForAccessibility(getDescription())
    }

    private fun getDescription(): String {
        return if (isChecked) {
            "Selected"
        } else {
            "Not selected"
        }
    }
}

Layout:

<com.example.app.CustomRadioButton
        android:id="@+id/itemRadioButton"
        android:layout_width="@dimen/_30dp"
        android:layout_height="@dimen/_30dp"
        android:layout_marginVertical="@dimen/_10dp"
        android:layout_marginStart="@dimen/_20dp"
        android:focusable="false" />

Using:

itemRadiobutton.talkBackString = "Alo alo"

Then talkback will read aloud: RadioButton + Alo alo + selected/not selected