This works for me. addAccessibilityStateChangeListener
does not fire when activating TalkBack with long-hold volume-button shortcut, probably because accessibility is already enabled, but TalkBack navigation isn't. Its probably enough to just add listener for addTouchExplorationStateChangeListener
, but to be safe I also added a addAccessibilityStateChangeListener
listener.
I deliberately do not use a StateFlow
which remembers the state the last broadcast value, in case our change-listener does not fire in all situations where we need it. This way my GUI can check isAccessibilityTalkBackEnabled
on creation, and listen via accessibilityTalkBackEnabledUpdates.collect{}
for updates, and we will always at least have the state that was at time of creating the GUI-component. If we used StateFlow and listener didn't work, we would have state that was present when app was launched (or more precisely when the class DefaultAndroidApplicationServices was initiated).
interface AndroidApplicationServices {
val accessibilityTalkBackEnabledUpdates: SharedFlow<Boolean>
val isAccessibilityTalkBackEnabled: Boolean
}
class DefaultAndroidApplicationServices(context: Context) : AndroidApplicationServices {
private val am = context.getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
override val accessibilityTalkBackEnabledUpdates = MutableSharedFlow<Boolean>()
override val isAccessibilityTalkBackEnabled: Boolean
get() {
return if (am.isEnabled) {
// am.isEnabled is true on my test-device, when TalkBack is off, but below check works
am.getEnabledAccessibilityServiceList(AccessibilityServiceInfo.FEEDBACK_SPOKEN).isNotEmpty()
} else false
}
init {
am.addAccessibilityStateChangeListener {
CoroutineScope(Dispatchers.Main).launch {
accessibilityTalkBackEnabledUpdates.emit(isAccessibilityTalkBackEnabled)
}
}
am.addTouchExplorationStateChangeListener {
CoroutineScope(Dispatchers.Main).launch {
accessibilityTalkBackEnabledUpdates.emit(isAccessibilityTalkBackEnabled)
}
}
}
}