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.
|
/** Konversation beenden — User hat im Window nichts gesagt.
|
||||||
* Mit Wake-Word: zurueck zu 'armed' (Listener wieder an).
|
* Mit Wake-Word: zurueck zu 'armed' (Listener wieder an).
|
||||||
* Ohne: zurueck zu 'off'.
|
* 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> {
|
async endConversation(): Promise<void> {
|
||||||
if (this.state !== 'conversing') {
|
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',
|
import('./logger').then(m => m.reportAppDebug('wake.end',
|
||||||
`endConversation called but state=${this.state} → noop`)).catch(()=>{});
|
`endConversation called but state=${this.state} → noop`)).catch(()=>{});
|
||||||
return;
|
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',
|
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) {
|
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 {
|
try {
|
||||||
await OpenWakeWord.start();
|
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',
|
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);
|
ToastAndroid.show(`Lausche wieder auf "${KEYWORD_LABELS[this.keyword]}"`, ToastAndroid.SHORT);
|
||||||
this.setState('armed');
|
this.setState('armed');
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user