Android SpeechRecognizer: cancel() and stopListening() being completely ignored (but only at Android 9 and 10)

huangapple 未分类评论48阅读模式
英文:

Android SpeechRecognizer: cancel() and stopListening() being completely ignored (but only at Android 9 and 10)

问题

package com.primelan.primebot.botVoiceRecognition.voice_recognition

import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.speech.RecognitionListener
import android.speech.RecognizerIntent
import android.speech.SpeechRecognizer
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat

class VoiceRecognition(private val activity: Activity, language: String = "pt_BR") : RecognitionListener {

    private val AudioLogTag = "AudioInput"

    var voiceRecognitionIntentHandler: VoiceRecognitionIntentHandler? = null
    var voiceRecognitionOnResultListener: VoiceRecognitionOnResultListener? = null
    var voiceRecognitionLayoutChanger: VoiceRecognitionLayoutChanger? = null

    var isListening = false

    private var enableResult = true
    private val intent: Intent
    private var speech: SpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(activity)

    init {
        speech.setRecognitionListener(this)

        intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
        intent.putExtra(
            RecognizerIntent.EXTRA_LANGUAGE_MODEL,
            RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
        )
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language)
    }

    fun listen(): Boolean {
        if (ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.RECORD_AUDIO), 1)
            return false
        }

        speech.startListening(intent)

        Log.i(AudioLogTag, "startListening")

        return true
    }

    fun endListening(){
        Log.i(AudioLogTag, "stopListening")

        speech.stopListening()
        isListening = false
    }

    fun cancelListening(){
        Log.i(AudioLogTag, "cancelListening")

        speech.cancel()
        voiceRecognitionLayoutChanger?.endListeningChangeLayout()
        isListening = false
    }

    override fun onReadyForSpeech(p0: Bundle?) {
        Log.i(AudioLogTag, "onReadyForSpeech")

        voiceRecognitionLayoutChanger?.startListeningChangeLayout()
        isListening = true
    }

    override fun onRmsChanged(p0: Float) {
    }

    override fun onBufferReceived(p0: ByteArray?) {
        Log.i(AudioLogTag, "onBufferReceived: $p0")
    }

    override fun onPartialResults(p0: Bundle?) {
        Log.i(AudioLogTag, "onPartialResults")
    }

    override fun onEvent(p0: Int, p1: Bundle?) {
        Log.i(AudioLogTag, "onEvent")
    }

    override fun onBeginningOfSpeech() {
        Log.i(AudioLogTag, "onBeginningOfSpeech")
    }

    override fun onEndOfSpeech() {
        Log.i(AudioLogTag, "onEndOfSpeech")

        voiceRecognitionLayoutChanger?.endListeningChangeLayout()
        isListening = false

        enableResult = true
    }

    override fun onError(p0: Int) {
        speech.cancel()
        val errorMessage = getErrorText(p0)
        Log.d(AudioLogTag, "FAILED: $errorMessage")
        voiceRecognitionLayoutChanger?.endListeningChangeLayout()
        isListening = false
    }

    override fun onResults(p0: Bundle?) {
        if(enableResult) {
            enableResult = false
            Log.i(AudioLogTag, "onResults")

            val results: ArrayList<String> =
                p0?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION) as ArrayList<String>

            val voiceIntent: Int? = voiceRecognitionIntentHandler?.getIntent(results[0])
            if (voiceIntent != null && voiceIntent != 0) {
                voiceRecognitionIntentHandler?.handle(voiceIntent)
                return
            }

            voiceRecognitionOnResultListener!!.onResult(results[0])
        }
    }

    private fun getErrorText(errorCode: Int): String {
        val message: String
        when (errorCode) {
            SpeechRecognizer.ERROR_AUDIO -> message = "Audio recording error"
            SpeechRecognizer.ERROR_CLIENT -> message = "Client side error"
            SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS -> message = "Insufficient permissions"
            SpeechRecognizer.ERROR_NETWORK -> message = "Network error"
            SpeechRecognizer.ERROR_NETWORK_TIMEOUT -> message = "Network timeout"
            SpeechRecognizer.ERROR_NO_MATCH -> message = "No match"
            SpeechRecognizer.ERROR_RECOGNIZER_BUSY -> message = "RecognitionService busy"
            SpeechRecognizer.ERROR_SERVER -> message = "Error from server"
            SpeechRecognizer.ERROR_SPEECH_TIMEOUT -> message = "No speech input"
            else -> message = "Didn't understand, please try again."
        }
        return message
    }

    fun onPause() {
        voiceRecognitionLayoutChanger?.endListeningChangeLayout()
        isListening = false

        speech.cancel()
        Log.i(AudioLogTag, "pause")
    }

    fun onDestroy() {
        speech.destroy()
    }
}

Note: I've removed the HTML-encoded characters (e.g., ") for better readability.

英文:

I have a project using RecognitionListener written in Kotlin. The speech-to-text function is a success but there are some problems after testing it at an Android 9 or 10 device.

After I start listening with startListening() function, it actually stops after some time of inactivity (within 1 second). I added some functions so the user can stop with a button and have the results, or cancel with a button and ignore the results. It worked pretty well testing at an Android 8 device. But when I tested at an Android 10 device, it never worked. (tested on Android 9 too and different devices with Android 10... same problem)

Here is the VoiceRecognition speech-to-text class code:

package com.primelan.primebot.botVoiceRecognition.voice_recognition

import android.Manifest
import android.app.Activity
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.speech.RecognitionListener
import android.speech.RecognizerIntent
import android.speech.SpeechRecognizer
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat

class VoiceRecognition(private val activity: Activity, language: String = &quot;pt_BR&quot;) : RecognitionListener {

    private val AudioLogTag = &quot;AudioInput&quot;

    var voiceRecognitionIntentHandler: VoiceRecognitionIntentHandler? = null
    var voiceRecognitionOnResultListener: VoiceRecognitionOnResultListener? = null //Must have this
    var voiceRecognitionLayoutChanger: VoiceRecognitionLayoutChanger? = null

    var isListening = false

    private var enableResult = true
    private val intent: Intent
    private var speech: SpeechRecognizer = SpeechRecognizer.createSpeechRecognizer(activity)

    init {
        speech.setRecognitionListener(this)

        intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
        intent.putExtra(
            RecognizerIntent.EXTRA_LANGUAGE_MODEL,
            RecognizerIntent.LANGUAGE_MODEL_FREE_FORM
        )
        intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, language)
    }

    //It is important to put this function inside a clickListener
    fun listen(): Boolean {
        if (ContextCompat.checkSelfPermission(activity, Manifest.permission.RECORD_AUDIO) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(activity, arrayOf(Manifest.permission.RECORD_AUDIO), 1)
            return false
        }

        speech.startListening(intent)

        Log.i(AudioLogTag, &quot;startListening&quot;)

        return true
    }

    //Use this if you want to stop listening but still get recognition results
    fun endListening(){
        Log.i(AudioLogTag, &quot;stopListening&quot;)

        speech.stopListening()
        isListening = false
    }

    fun cancelListening(){
        Log.i(AudioLogTag, &quot;cancelListening&quot;)

        speech.cancel()
        voiceRecognitionLayoutChanger?.endListeningChangeLayout()
        isListening = false
    }

    override fun onReadyForSpeech(p0: Bundle?) {
        Log.i(AudioLogTag, &quot;onReadyForSpeech&quot;)

        voiceRecognitionLayoutChanger?.startListeningChangeLayout()
        isListening = true
    }

    override fun onRmsChanged(p0: Float) {
//        Log.i(LOG_TAG, &quot;onRmsChanged: $p0&quot;)
//        progressBar.setProgress((Int) p0)
    }

    override fun onBufferReceived(p0: ByteArray?) {
        Log.i(AudioLogTag, &quot;onBufferReceived: $p0&quot;)
    }

    override fun onPartialResults(p0: Bundle?) {
        Log.i(AudioLogTag, &quot;onPartialResults&quot;)
    }

    override fun onEvent(p0: Int, p1: Bundle?) {
        Log.i(AudioLogTag, &quot;onEvent&quot;)
    }

    override fun onBeginningOfSpeech() {
        Log.i(AudioLogTag, &quot;onBeginningOfSpeech&quot;)
    }

    override fun onEndOfSpeech() {
        Log.i(AudioLogTag, &quot;onEndOfSpeech&quot;)

        voiceRecognitionLayoutChanger?.endListeningChangeLayout()
        isListening = false

        enableResult = true
    }

    override fun onError(p0: Int) {
        speech.cancel()
        val errorMessage = getErrorText(p0)
        Log.d(AudioLogTag, &quot;FAILED: $errorMessage&quot;)
        voiceRecognitionLayoutChanger?.endListeningChangeLayout()
        isListening = false
    }

    override fun onResults(p0: Bundle?) {
        if(enableResult) {
            enableResult = false
            Log.i(AudioLogTag, &quot;onResults&quot;)

            val results: ArrayList&lt;String&gt; =
                p0?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION) as ArrayList&lt;String&gt;

            val voiceIntent: Int? = voiceRecognitionIntentHandler?.getIntent(results[0])
            if (voiceIntent != null &amp;&amp; voiceIntent != 0) {
                voiceRecognitionIntentHandler?.handle(voiceIntent)
                return
            }

            voiceRecognitionOnResultListener!!.onResult(results[0])
        }
    }

    private fun getErrorText(errorCode: Int): String {
        val message: String
        when (errorCode) {
            SpeechRecognizer.ERROR_AUDIO -&gt; message = &quot;Audio recording error&quot;

            SpeechRecognizer.ERROR_CLIENT -&gt; message = &quot;Client side error&quot;

            SpeechRecognizer.ERROR_INSUFFICIENT_PERMISSIONS -&gt; message = &quot;Insufficient permissions&quot;

            SpeechRecognizer.ERROR_NETWORK -&gt; message = &quot;Network error&quot;

            SpeechRecognizer.ERROR_NETWORK_TIMEOUT -&gt; message = &quot;Network timeout&quot;

            SpeechRecognizer.ERROR_NO_MATCH -&gt; message = &quot;No match&quot;

            SpeechRecognizer.ERROR_RECOGNIZER_BUSY -&gt; message = &quot;RecognitionService busy&quot;

            SpeechRecognizer.ERROR_SERVER -&gt; message = &quot;Error from server&quot;

            SpeechRecognizer.ERROR_SPEECH_TIMEOUT -&gt; message = &quot;No speech input&quot;

            else -&gt; message = &quot;Didn&#39;t understand, please try again.&quot;
        }
        return message
    }

    //Use it in your overriden onPause function.
    fun onPause() {
        voiceRecognitionLayoutChanger?.endListeningChangeLayout()
        isListening = false

        speech.cancel()
        Log.i(AudioLogTag, &quot;pause&quot;)
    }

    //Use it in your overriden onDestroy function.
    fun onDestroy() {
        speech.destroy()
    }
}

As you can see, the functions that I mentioned are endListening() and cancelListening(). When those functions are called, you can actually see the Log, but the cancel() or the stopListening() won't have any effects if the current device is using android 9 or 10. Even the line voiceRecognitionLayoutChanger?.endListeningChangeLayout() from cancelListening() is executed and brings the desired results (changes a layout inside the app).

Is there a problem with android.speech library? Or... is there a way to fix it?

huangapple
  • 本文由 发表于 2020年5月5日 04:10:04
  • 转载请务必保留本文链接:https://java.coder-hub.com/61600752.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定