diff --git a/android/src/screens/ChatScreen.tsx b/android/src/screens/ChatScreen.tsx index 5a9c162..d43da84 100644 --- a/android/src/screens/ChatScreen.tsx +++ b/android/src/screens/ChatScreen.tsx @@ -1203,6 +1203,21 @@ const ChatScreen: React.FC = () => { clearAckTimer(cmid); pendingPayloads.current.delete(cmid); } + // Stille Antwort (speak=false, z.B. Fast-Path „nächster Titel") → es + // kommt KEIN TTS, also feuert onPlaybackFinished nie. Genau daran haengt + // aber das Wake-Word-Re-Arm — sonst bleibt das Ohr nach dem Steuerbefehl + // grau haengen (Reproduktion Stefan im Auto). Hier dieselbe Logik wie + // bei TTS-Ende anstossen: aktiv → Konversations-Fenster (resume), + // sonst → Konversation beenden (re-arm). + const _isSilent = (message.payload as any).speak === false + || ((message.payload as any).answeredBy === 'fast-path'); + if (_isSilent && wakeWordService.isConversing()) { + if (AppState.currentState === 'active') { + wakeWordService.resume(); + } else { + wakeWordService.endConversation().catch(() => {}); + } + } } // TTS-Audio abspielen wenn vorhanden — respektiert geraetelokalen Mute/Disable diff --git a/bridge/aria_bridge.py b/bridge/aria_bridge.py index 22f7cbd..f723676 100644 --- a/bridge/aria_bridge.py +++ b/bridge/aria_bridge.py @@ -1366,6 +1366,11 @@ class ARIABridge: "projectId": (payload.get("projectId") or "") if isinstance(payload, dict) else "", # Quell-Backend (local/claude/fast-path) fuer den Diagnostic-Badge. "answeredBy": (payload.get("answeredBy") or "") if isinstance(payload, dict) else "", + # Wird diese Antwort vorgelesen? Fast-Path/Steuerbefehl = False. + # Die App braucht das: ohne TTS feuert onPlaybackFinished nie, + # und daran haengt das Wake-Word-Re-Arm — sonst bleibt das Ohr + # nach einem Fast-Path-Befehl grau haengen. + "speak": bool(payload.get("speak", True)) if isinstance(payload, dict) else True, }, "timestamp": int(asyncio.get_event_loop().time() * 1000), })