fix(wake): Race zwischen endConversation und stopBargeListening killt
Wake-Word-Listener nach jeder Konversation
Aus dem Log diagnostiziert: zwei onPlaybackFinished-Listener feuern
direkt hintereinander wenn TTS endet:
1. mein neuer Listener (Background): endConversation()
→ state=armed, OpenWakeWord.start() (idempotent)
2. existierender Listener: stopBargeListening()
→ bargeListening=true → OpenWakeWord.stop() ← killt re-armed Listener
State zeigte 'armed' (UI: Ohr-Icon ausgefuellt, sieht aktiv aus), aber
das Native-Modul war gestoppt → Stefan's "Computer" verpufft.
Fix: endConversation setzt bargeListening=false BEVOR Native gerufen
wird. stopBargeListening checkt das Flag oben:
async stopBargeListening() { if (!this.bargeListening) return; ... }
→ wird zum No-Op wenn endConversation schon gelaufen ist.
Bonus: OpenWakeWord.start() darf jetzt auch gerufen werden wenn der
Listener via barge-listening schon lief — Kotlin checkt running.get()
und resolved idempotent. Sicherer als state-vorher-Check.
This commit is contained in:
@@ -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<void> {
|
||||
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;
|
||||
|
||||
Reference in New Issue
Block a user