How to know if Android TalkBack is active?
Asked Answered
H

9

47

I'm developing an application that uses TalkBack to guide people through it. However, in those situations I want to have some subtile differences in the layout of the application so navigation is easier and also have extra voice outputs (with TextToSpeech) to help guide the user.

My problem is that I only want those changes and extra outputs if the user has TalkBack active.

Is there any way to know if it is? I didn't find anything specific to access TalkBack settings directly, but I was hoping there was some form of accessing general phone settings that could let me know what I need.

Hauger answered 25/2, 2011 at 11:49 Comment(0)
P
6

For an example, look at isScreenReaderActive() in HomeLauncher.java file in the Eyes-Free shell application (via groups thread).

To sum up: you detect all screen readers with Intents, then query the status provider of each to see if it is active.

If you really want to limit it to TalkBack only, you could try checking the ResolveInfo.serviceInfo.packageName for each result returned from queryIntentServices() to see if it matches the TalkBack package.

Polyhedron answered 25/2, 2011 at 23:6 Comment(2)
I ended up going through all the services and see if the TalkBack one was active, but your solution is much better, thanks. I did had a problem with a cracked ROM, though. No services were being returned.Hauger
Mike, I have tried this in Jelly Bean and it doesn't seem to work... the cursor seems to be empty. DO you have any idea on how to do this in Jelly Bean? I created a new question: #11832166Unskillful
C
91

The recommended way of doing this is to query the AccessibilityManager for the enabled state of accessibility services.

AccessibilityManager am = (AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE);
boolean isAccessibilityEnabled = am.isEnabled();
boolean isExploreByTouchEnabled = am.isTouchExplorationEnabled();
Conserve answered 11/9, 2012 at 3:57 Comment(4)
If the TalkBack service is suspended (using the gesture shortcuts), the AccessibilityManager will not change its enabled state to disabled. Very very small edge case, I suppose, and technically it is still enabled, but this tripped me up today.Sarcasm
If you use the TalkBack service on the Nexus Player, does isTouchExplorationEnabled() still return true?Sarcasm
To answer my own question, touch exploration is available on Nexus Player (on Lollipop at least) and it will return true if this is checked.Sarcasm
More details: if you are strictly interested in whether TalkBack is enabled, use am.isTouchExplorationEnabled(). (If Select to Speak is enabled and TalkBack disabled, am.isEnabled() will still return true.)Touched
L
12

You can create an inline function in kotlin like:

fun Context.isScreenReaderOn():Boolean{
    val am = getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
    if (am != null && am.isEnabled) {
        val serviceInfoList =
            am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN)
        if (!serviceInfoList.isEmpty())
            return true
    }
    return false}

And then you can just call it whenever you need it like:

if(context.isScreenReaderOn()){
...
}

Tested and works fine for now.

Libratory answered 28/1, 2020 at 13:44 Comment(2)
Even more kotlin to have a val instead of a function val Context.isScreenReaderOn: Boolean get() { val am = getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager if (am.isEnabled) { val serviceInfoList = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN) if (serviceInfoList.isNotEmpty()) return true } return false }Acetometer
This will still return the previous value if you return to your app after disabling/enabling talkback or screen reader from Settings app...Kwapong
S
9

Novoda have released a library called accessibilitools which does this check. It queries the accessibility manager to check if there are any accessibility services enabled that support the "spoken feedback" flag.

AccessibilityServices services = AccessibilityServices.newInstance(context);
services.isSpokenFeedbackEnabled();

public boolean isSpokenFeedbackEnabled() {
    List<AccessibilityServiceInfo> enabledServices = getEnabledServicesFor(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
    return !enabledServices.isEmpty();
}

private List<AccessibilityServiceInfo> getEnabledServicesFor(int feedbackTypeFlags) {
    return accessibilityManager.getEnabledAccessibilityServiceList(feedbackTypeFlags);
}
Sarcasm answered 28/4, 2016 at 13:3 Comment(3)
Exploring the git hub one can see lots of good lines of Android API. Particularly detecting if a voice over service is enabled using 2 lines of code was my pick. Neat and nice.Goforth
Interesting, but that repo was archived 5 days ago.Advowson
Added code from the repo. It was archived because no one's around to maintain it anymore I suppose.Sarcasm
P
6

For an example, look at isScreenReaderActive() in HomeLauncher.java file in the Eyes-Free shell application (via groups thread).

To sum up: you detect all screen readers with Intents, then query the status provider of each to see if it is active.

If you really want to limit it to TalkBack only, you could try checking the ResolveInfo.serviceInfo.packageName for each result returned from queryIntentServices() to see if it matches the TalkBack package.

Polyhedron answered 25/2, 2011 at 23:6 Comment(2)
I ended up going through all the services and see if the TalkBack one was active, but your solution is much better, thanks. I did had a problem with a cracked ROM, though. No services were being returned.Hauger
Mike, I have tried this in Jelly Bean and it doesn't seem to work... the cursor seems to be empty. DO you have any idea on how to do this in Jelly Bean? I created a new question: #11832166Unskillful
P
4
    AccessibilityManager am = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
    if (am != null && am.isEnabled()) {
        List<AccessibilityServiceInfo> serviceInfoList = am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
        if (!serviceInfoList.isEmpty())
            return true;
    }
    return false;
Palmation answered 8/2, 2019 at 19:49 Comment(0)
H
3

If you are working with compose you can add this utility extension on Context:

@Composable
internal fun Context.collectIsTalkbackEnabledAsState(): State<Boolean> {
    val accessibilityManager =
        this.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager?

    fun isTalkbackEnabled(): Boolean {
        val accessibilityServiceInfoList =
            accessibilityManager?.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN)
        return accessibilityServiceInfoList?.any {
            it.resolveInfo.serviceInfo.processName.equals(TALKBACK_PACKAGE_NAME)
        } ?: false
    }

    val talkbackEnabled = remember { mutableStateOf(isTalkbackEnabled()) }
    val accessibilityManagerEnabled = accessibilityManager?.isEnabled ?: false
    var accessibilityEnabled by remember { mutableStateOf(accessibilityManagerEnabled) }

    accessibilityManager?.addAccessibilityStateChangeListener { accessibilityEnabled = it }

    LaunchedEffect(accessibilityEnabled) {
        talkbackEnabled.value = if (accessibilityEnabled) isTalkbackEnabled() else false
    }
    return talkbackEnabled
}

private const val TALKBACK_PACKAGE_NAME = "com.google.android.marvin.talkback"

And then use it in the target composable:

@Composable
fun SomeComposable() {
    val talkbackEnabled by LocalContext.current.collectIsTalkbackEnabledAsState()

    if (talkbackEnabled) {
        /** do something here **/
    }
}
Hirst answered 20/8, 2022 at 5:30 Comment(2)
addAccessibilityStateChangeListener doesn't seem to receive the events when I toggle it on and offChristianize
this approach is very specific to android devices or devices that use the TALKBACK_PACKAGE_NAME for the processName field. The process name can change for example on my samsung test device it is "com.samsung.android.accessibility.talkback". Because of this variability I think the best answer is https://mcmap.net/q/360301/-how-to-know-if-android-talkback-is-active, since it is more genericl. Blend that with this to get compose sugary goodness.Shutdown
H
2

For me, I solved this problem in this way , it works well in my project:

  1. use getEnabledAccessibilityServiceList() to get all Accessibility service, a service whose status is open will be in this list
  2. Talkback contain a activity named com.android.talkback.TalkBackPreferencesActivity, you can traversing the list to find whether the talkback service is open

The detailed code below:

    private static final String TALKBACK_SETTING_ACTIVITY_NAME = "com.android.talkback.TalkBackPreferencesActivity";

    public static boolean accessibilityEnable(Context context) {
        boolean enable = false;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            try {
                AccessibilityManager manager = (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
                List<AccessibilityServiceInfo> serviceList = manager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN);
                for (AccessibilityServiceInfo serviceInfo : serviceList) {
                    String name = serviceInfo.getSettingsActivityName();
                    if (!TextUtils.isEmpty(name) && name.equals(TALKBACK_SETTING_ACTIVITY_NAME)) {
                        enable = true;
                    }
                }
            } catch (Exception e) {
                if (Logging.isDebugLogging()) {
                    e.printStackTrace();
                }
            }
        }
        return enable;
}
Hedger answered 28/12, 2016 at 7:8 Comment(2)
They could change the name of the settings activity in future updates.Sarcasm
But you can check the Android Accessibility Suite package name with accessibilityServiceInfo.getResolveInfo().serviceInfo.processName instead of its nameCortes
C
2

Thanks to @david-z answer (https://mcmap.net/q/360301/-how-to-know-if-android-talkback-is-active) I made this approach to know if the Android Accessibility Suite by Google is enabled

/**
 * This method checks if Google Talkback is enabled by using the [accessibilityManager]
 */
private fun isGoogleTalkbackActive(accessibilityManager : AccessibilityManager) : Boolean
{
    val accessibilityServiceInfoList = accessibilityManager.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN)
    for (accessibilityServiceInfo in accessibilityServiceInfoList)
    {
        if ("com.google.android.marvin.talkback".equals(accessibilityServiceInfo.resolveInfo.serviceInfo.processName))
        {
            return true
        }
    }
    return false
}

Remember to register the google URI as constant :) and get the Accessibility Manager instance as @caseyburkhardt says (https://mcmap.net/q/360301/-how-to-know-if-android-talkback-is-active). The difference with the @david-z answer is that I got the Android Accessibility Suite package name instead of its app name because it is more secure. If you want to review if another accessibility suite is enabled (like the Samsung Screen Reader) after this check, you can check if (accessibilityManager.isTouchExplorationEnabled)

Cheers!

Cortes answered 19/11, 2020 at 17:50 Comment(0)
T
-5

Open system setting and go to accessibility and tap to off Talk back option

Tort answered 5/11, 2017 at 8:46 Comment(1)
I think OP is asking about programmatically checking if TalkBack is active.Golden

© 2022 - 2024 — McMap. All rights reserved.