Android RecognitionListener: onResults se llama dos veces

10

Tengo un proyecto usando RecognitionListener escrito en Kotlin. La función de voz a texto siempre fue un éxito y nunca presentó ningún problema.

Desde la semana pasada, su función onResult comenzó a llamarse dos veces. No se hicieron cambios en el proyecto. Probé versiones antiguas del proyecto (hace meses) y esas tenían el mismo problema.

Hay tres casos diferentes:

  1. Texto pequeño (1 a 8 palabras) y SpeechRecognizer se detiene automáticamente -> onResult () llamado dos veces;
  2. Texto grande (9 palabras o más) y SpeechRecognizer se detiene automáticamente -> Comportamiento normal (onResult () llamado una vez);
  3. Cualquier tamaño de texto y función SpeechRecognizer stopListening () llamada manualmente (desde el código) -> Comportamiento normal.

Aquí está el código de clase de voz a texto VoiceRecognition:

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

    private val AudioLogTag = "AudioInput"

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

    var isListening = false

    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, "startListening")

        return true
    }

    //Use this if you want to stop listening but still get recognition results
    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) {
//        Log.i(AudioLogTag, "onRmsChanged: $p0")
//        progressBar.setProgress((Int) p0)
    }

    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
    }

    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?) {

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

        Log.i(AudioLogTag, "onResults -> ${results.size}")

        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
    }

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

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

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

listen (), endListening () y cancelListening () se invocan desde un botón.

Pedro Henrique Flores
fuente
Tengo el mismo problema, el problema es solo en Samsung s8 con api 9, aquí también puedo ver resultados parciales cuando el reconocimiento está en progreso. En dispositivos más antiguos no experimento esto.
marcinj
Vi este problema desde Android 7 y superior ... Ni siquiera cambié mi proyecto ... simplemente comenzó a suceder.
Pedro Henrique Flores
"el problema es solo en Samsung s8 con api 9" - con eso quise decir en mis pruebas en dispositivos que tengo
marcinj
1
mismo problema en Pocophone F1, mi solución fue verificar si los resultados son idénticos, luego ignorar el segundo resultado si lo son
Lotan
Esto comenzó a suceder ayer en una de mis aplicaciones. Agregué un booleano para permitir que el código se ejecute solo una vez, pero me encantaría una explicación de por qué de repente comenzó a hacer esto. ¿Alguna actualización?
Gavin Wright

Respuestas:

3

Encontré este problema abierto: https://issuetracker.google.com/issues/152628934

Como comenté, supongo que es un problema con el "servicio de reconocimiento de voz" y no con la clase Android RecognitionListener.

Al-Punk
fuente
Sí, también creo que este es el problema ...
Pedro Henrique Flores
1

Esto comenzó a suceder ayer en una de mis aplicaciones. Agregué un booleano para permitir que el código se ejecute solo una vez, pero me encantaría una explicación de por qué de repente comenzó a hacer esto. ¿Alguna actualización?

Gavin Wright
fuente
0

Tuve el mismo problema y acabo de agregar una bandera booleana en mi código, pero por supuesto es una solución temporal y no sé la fuente de este problema.

object : RecognitionListener {

        var singleResult = true

        override fun onResults(results: Bundle?) {
            if (singleResult) {
                results?.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION).let {
                    // do something with result
                }
                // next result will be ignored
                singleResult = false
            }
        }
    }
Grobocop12
fuente
Por favor agregue un código de ejemplo.
m02ph3u5 hace