You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取TalkBack完成播报朗读的Accessibility Callback?

How to Get a Callback When TalkBack Finishes Announcing

Great question! Using a fixed 1-second delay is a fragile workaround because TalkBack users can adjust speech speed, which will throw off your timing. Here are two reliable solutions to trigger your focus switch exactly when the announcement finishes:

Method 1: Use TextToSpeech Instead of announceForAccessibility (Works on Most Versions)

If you're open to replacing announceForAccessibility with TextToSpeech, you can leverage its built-in callback to know exactly when the speech ends. Here's how to implement it:

  1. Initialize the TextToSpeech instance in your Activity/Fragment:
private lateinit var tts: TextToSpeech

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ... your existing initialization code

    tts = TextToSpeech(this) { status ->
        if (status == TextToSpeech.SUCCESS) {
            // Set language to system default (matches most user settings)
            val langResult = tts.setLanguage(Locale.getDefault())
            if (langResult == TextToSpeech.LANG_MISSING_DATA || langResult == TextToSpeech.LANG_NOT_SUPPORTED) {
                Log.e("TTS", "Selected language is not supported")
            }
        } else {
            Log.e("TTS", "Failed to initialize TextToSpeech")
        }
    }

    // Set up the callback for when speech completes
    tts.setOnUtteranceProgressListener(object : UtteranceProgressListener() {
        override fun onStart(utteranceId: String?) {
            // Optional: Handle speech start if needed
        }

        override fun onDone(utteranceId: String?) {
            // Speech finished, switch focus on the UI thread
            runOnUiThread {
                specificView.requestFocus()
                specificView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
            }
        }

        override fun onError(utteranceId: String?) {
            // Optional: Handle speech errors
        }
    })
}
  1. Trigger the announcement with a unique utterance ID (so you can identify your specific message):
val announcementText = "some action failed."
val ttsParams = HashMap<String, String>().apply {
    put(TextToSpeech.Engine.KEY_PARAM_UTTERANCE_ID, "action_failed_announcement")
}
tts.speak(announcementText, TextToSpeech.QUEUE_FLUSH, ttsParams)
  1. Don't forget to clean up the TextToSpeech instance when your component is destroyed:
override fun onDestroy() {
    super.onDestroy()
    tts.stop()
    tts.shutdown()
}

Note: This uses the system's TextToSpeech engine, which usually aligns with TalkBack's voice settings (speed, pitch) since most users keep them synced. If you need perfect alignment with TalkBack's exact voice, you might need additional checks, but this works for most cases.

Method 2: Listen for Accessibility Events (API 26+)

For Android 8.0 (API level 26) and above, you can listen directly for TalkBack's announcement events using AccessibilityManager. This lets you stick with announceForAccessibility while getting a completion trigger:

  1. Set up the accessibility event listener in your Activity/Fragment:
private lateinit var accessibilityManager: AccessibilityManager
private val announcementListener = AccessibilityEventListener { event ->
    // Check if this is an announcement event and matches your message
    if (event.eventType == AccessibilityEvent.TYPE_ANNOUNCEMENT) {
        val announcedText = event.text.joinToString("")
        if (announcedText == "some action failed.") {
            // Announcement finished, switch focus
            specificView.requestFocus()
            specificView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
            // Unregister the listener to avoid duplicate triggers
            accessibilityManager.removeAccessibilityEventListener(announcementListener)
        }
    }
}

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // ... your existing initialization code
    accessibilityManager = getSystemService(Context.ACCESSIBILITY_SERVICE) as AccessibilityManager
}
  1. Register the listener right before triggering your announcement:
val announcementText = "some action failed."
accessibilityManager.addAccessibilityEventListener(announcementListener)
view!!.announceForAccessibility(announcementText)
  1. Clean up the listener when it's no longer needed:
override fun onDestroy() {
    super.onDestroy()
    if (::accessibilityManager.isInitialized) {
        accessibilityManager.removeAccessibilityEventListener(announcementListener)
    }
}

Note: Make sure the announcement text matches exactly (including punctuation) to avoid triggering on unrelated announcements. For API levels below 26, this method isn't available, so you'd need to fall back to Method 1 or a dynamic delay (though dynamic delays are still less reliable).

Extra Tips

  • For full compatibility across all API levels, combine both methods: use the accessibility event listener for API 26+, and TextToSpeech for older versions.
  • Always perform UI operations (like requesting focus) on the main thread, since accessibility callbacks might run on background threads.

内容的提问来源于stack exchange,提问作者Dhara Vamja

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:49:31