diff --git a/android/src/services/wakeword.ts b/android/src/services/wakeword.ts index bfd8886..e535fae 100644 --- a/android/src/services/wakeword.ts +++ b/android/src/services/wakeword.ts @@ -344,23 +344,38 @@ class WakeWordService { /** Konversation beenden — User hat im Window nichts gesagt. * Mit Wake-Word: zurueck zu 'armed' (Listener wieder an). * Ohne: zurueck zu 'off'. + * + * WICHTIG: setzt bargeListening=false BEVOR OpenWakeWord.start() laeuft. + * Grund: wenn endConversation aus dem onPlaybackFinished-Handler kommt, + * feuert direkt danach ein zweiter Listener (stopBargeListening) — der + * wuerde sonst OpenWakeWord.stop() rufen weil bargeListening noch true + * ist, und unseren frisch re-armierten Listener killen. */ async endConversation(): Promise { if (this.state !== 'conversing') { - // Nicht in conversing — typ. nach App-Resume bevor Streaming endete. - // Trotzdem loggen damit wir's im Diagnostic sehen. import('./logger').then(m => m.reportAppDebug('wake.end', `endConversation called but state=${this.state} → noop`)).catch(()=>{}); return; } + const wasBarge = this.bargeListening; + // Flag NULLEN bevor wir die Listener triggern. Sonst killt der parallele + // stopBargeListening-Listener (TTS-end) gleich danach unseren Native- + // OpenWakeWord, weil er bargeListening=true sieht und annimmt er muss + // den Listener stoppen. + this.bargeListening = false; import('./logger').then(m => m.reportAppDebug('wake.end', - `endConversation called, nativeReady=${this.nativeReady}, calling OpenWakeWord.start()`)).catch(()=>{}); + `endConversation called, wasBarge=${wasBarge}, nativeReady=${this.nativeReady}`)).catch(()=>{}); if (this.nativeReady && OpenWakeWord) { + // Wenn wakeword schon laeuft (war Barge-Listener waehrend TTS): + // OpenWakeWord.start() ist idempotent (Kotlin checkt running.get() + // und resolved sofort). Wir koennen es trotzdem rufen — billiger + // als state extra zu fragen, garantiert dass nach diesem Pfad + // Native auch wirklich an ist falls es out-of-band gestoppt wurde. try { await OpenWakeWord.start(); - console.log('[WakeWord] Konversation zu Ende — zurueck zu armed'); + console.log('[WakeWord] Konversation zu Ende — zurueck zu armed (wasBarge=%s)', wasBarge); import('./logger').then(m => m.reportAppDebug('wake.end', - `OpenWakeWord.start() OK → state=armed, keyword=${this.keyword}`)).catch(()=>{}); + `OpenWakeWord.start() OK → state=armed, wasBarge=${wasBarge}`)).catch(()=>{}); ToastAndroid.show(`Lausche wieder auf "${KEYWORD_LABELS[this.keyword]}"`, ToastAndroid.SHORT); this.setState('armed'); return;