d78b668e31
Stefan-Anforderung: Background-Wake-Word-Pipeline klappt noch nicht, ADB nicht zur Hand → Debug via RVS-Log-Pipeline. Logger: - reportAppDebug(scope, message) analog zu reportAppError aber level=info, kein console.error, fuer Live-Diagnose Strategische Log-Punkte: - wakeword.ts: start() emits 'wake.start armed' - wakeword.ts: onWakeDetected emits 'wake.detect state=X' beim Native-Trigger-Empfang - ChatScreen.tsx wake-callback: 'wake.cb callback fired', 'wake.cb startRecording=X', 'wake.cb gong played' - backgroundAudio.ts: 'bg.start slot=X', 'bg.stop service stopped', 'bg.start.fail msg' wenn Service nicht hochkommt Abruf live via curl http://172.0.2.33:3001/api/app-log?lines=100 Damit kann Stefan nach APK-Build (mit allen Native-Fixes + Logger) im Background-Test exakt sehen wo es klemmt: - Kommt 'wake.detect' im Hintergrund an? (WakeLock-Frage) - Kommt 'wake.cb callback fired'? (JS-Bridge-Frage) - Geht 'bg.start slot=wake' durch? (Service-Start-Frage) APK neu bauen. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
86 lines
3.2 KiB
TypeScript
86 lines
3.2 KiB
TypeScript
/**
|
|
* Background-Audio + Hintergrund-Persistenz: ARIAs TTS, Mic-Aufnahme,
|
|
* Wake-Word-Lauschen UND der allgemeine Hintergrund-Modus laufen
|
|
* weiter wenn die App minimiert ist. Wir starten dafuer einen Foreground-
|
|
* Service mit foregroundServiceType=mediaPlayback|microphone, der eine
|
|
* persistente Notification zeigt solange irgendein Slot aktiv ist.
|
|
*
|
|
* Mehrere Komponenten koennen den Service unabhaengig "halten":
|
|
* - 'tts' : ARIA spricht
|
|
* - 'rec' : Aufnahme laeuft
|
|
* - 'wake' : Wake-Word lauscht passiv (Ohr aktiv)
|
|
* - 'location' : Background-GPS-Tracking (opt-in in Settings)
|
|
* - 'background' : Persistenter Hintergrund-Modus (Settings-Toggle).
|
|
* Haelt JS-Engine + WebSocket auch ohne Audio am Leben
|
|
* → Trigger-Replies, Reconnects, Push-Reaktionen.
|
|
*
|
|
* Solange mindestens ein Slot aktiv ist, laeuft der Service. Wenn alle
|
|
* Slots leer sind, wird er gestoppt. Der Notification-Text passt sich an
|
|
* den hoechstprioren Slot an (tts > rec > wake > location > background).
|
|
*/
|
|
|
|
import { NativeModules } from 'react-native';
|
|
|
|
interface BackgroundAudioNative {
|
|
start(reason: string): Promise<boolean>;
|
|
stop(): Promise<boolean>;
|
|
}
|
|
|
|
const { BackgroundAudio } = NativeModules as { BackgroundAudio?: BackgroundAudioNative };
|
|
|
|
type Slot = 'tts' | 'rec' | 'wake' | 'location' | 'background';
|
|
|
|
const slots = new Set<Slot>();
|
|
|
|
// Prioritaet fuer den Notification-Text — hoechste zuerst. 'background'
|
|
// ist die fallback-Anzeige wenn nichts anderes laeuft.
|
|
const PRIORITY: Slot[] = ['tts', 'rec', 'wake', 'location', 'background'];
|
|
|
|
function topReason(): string {
|
|
for (const s of PRIORITY) {
|
|
if (slots.has(s)) return s;
|
|
}
|
|
return '';
|
|
}
|
|
|
|
async function applyState(): Promise<void> {
|
|
if (!BackgroundAudio) return;
|
|
if (slots.size === 0) {
|
|
try { await BackgroundAudio.stop(); } catch {}
|
|
console.log('[BackgroundAudio] Service gestoppt (keine Slots)');
|
|
import('./logger').then(m => m.reportAppDebug('bg.stop', 'service stopped')).catch(()=>{});
|
|
return;
|
|
}
|
|
const reason = topReason();
|
|
try {
|
|
await BackgroundAudio.start(reason);
|
|
console.log('[BackgroundAudio] Service aktiv (slot=%s, slots=%s)',
|
|
reason, [...slots].join('+'));
|
|
import('./logger').then(m => m.reportAppDebug('bg.start', `slot=${reason} all=[${[...slots].join(',')}]`)).catch(()=>{});
|
|
} catch (err: any) {
|
|
console.warn('[BackgroundAudio] start fehlgeschlagen:', err?.message || err);
|
|
import('./logger').then(m => m.reportAppDebug('bg.start.fail', err?.message || String(err))).catch(()=>{});
|
|
}
|
|
}
|
|
|
|
export async function acquireBackgroundAudio(slot: Slot): Promise<void> {
|
|
if (slots.has(slot)) return;
|
|
slots.add(slot);
|
|
await applyState();
|
|
}
|
|
|
|
export async function releaseBackgroundAudio(slot: Slot): Promise<void> {
|
|
if (!slots.has(slot)) return;
|
|
slots.delete(slot);
|
|
await applyState();
|
|
}
|
|
|
|
export function backgroundAudioActive(): boolean {
|
|
return slots.size > 0;
|
|
}
|
|
|
|
// --- Legacy API (nur tts-Slot) — fuer Aufruf-Sites die noch nichts vom Slot-
|
|
// system wissen. Mappt auf den 'tts'-Slot. ---
|
|
export const startBackgroundAudio = () => acquireBackgroundAudio('tts');
|
|
export const stopBackgroundAudio = () => releaseBackgroundAudio('tts');
|