Erweitert den Foreground-Service um den microphone-Type damit nicht nur TTS, sondern auch Wake-Word-Lauschen und aktive Aufnahmen weiterlaufen wenn die App im Hintergrund ist. Slot-System (backgroundAudio.ts): - 'tts' : ARIA spricht - 'rec' : Aufnahme laeuft - 'wake' : Wake-Word lauscht passiv (Ohr aktiv) Mehrere Slots koennen unabhaengig acquired/released werden, der Service laeuft solange mindestens einer aktiv ist. Notification-Text passt sich dynamisch an den hoechstprioren Slot an (tts > rec > wake). Wiring (ChatScreen): - onPlaybackStarted/Finished → 'tts' Slot - audioService.onStateChange (recording) → 'rec' Slot - wakeWordService.onStateChange (off→armed/conversing) → 'wake' Slot AndroidManifest: - foregroundServiceType="mediaPlayback|microphone" (Pflicht ab Android 14 fuer Background-Mic-Zugriff) - FOREGROUND_SERVICE_MICROPHONE Permission Doku: - issue.md Erledigt-Sektion in "Bugs / Fixes", "App Features" und "Infrastruktur" gesplittet - README: Background-Service-Beschreibung erweitert Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
60 lines
2.1 KiB
Kotlin
60 lines
2.1 KiB
Kotlin
package com.ariacockpit
|
|
|
|
import android.content.Intent
|
|
import android.os.Build
|
|
import android.util.Log
|
|
import com.facebook.react.bridge.Promise
|
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
import com.facebook.react.bridge.ReactMethod
|
|
|
|
/**
|
|
* RN-Bridge fuer den AriaPlaybackService.
|
|
*
|
|
* Wird vom JS waehrend einer TTS-Wiedergabe gestartet damit Android den
|
|
* App-Prozess nicht killt wenn die App im Hintergrund ist (= ARIA spricht
|
|
* weiter, auch wenn Stefan die App minimiert hat).
|
|
*
|
|
* Service stoppt entweder explizit per stop() oder wird von Android
|
|
* mitgekillt wenn der Prozess weg ist (was bei Foreground-Service nur
|
|
* passiert wenn der User die App force-stopped).
|
|
*/
|
|
class BackgroundAudioModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
|
|
override fun getName() = "BackgroundAudio"
|
|
|
|
companion object { private const val TAG = "BackgroundAudio" }
|
|
|
|
@ReactMethod
|
|
fun start(reason: String, promise: Promise) {
|
|
try {
|
|
val ctx = reactApplicationContext
|
|
val intent = Intent(ctx, AriaPlaybackService::class.java)
|
|
intent.putExtra(AriaPlaybackService.EXTRA_REASON, reason ?: "")
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
|
ctx.startForegroundService(intent)
|
|
} else {
|
|
ctx.startService(intent)
|
|
}
|
|
promise.resolve(true)
|
|
} catch (e: Exception) {
|
|
Log.w(TAG, "start fehlgeschlagen: ${e.message}")
|
|
promise.reject("START_FAILED", e.message ?: "Unbekannter Fehler", e)
|
|
}
|
|
}
|
|
|
|
@ReactMethod
|
|
fun stop(promise: Promise) {
|
|
try {
|
|
val ctx = reactApplicationContext
|
|
ctx.stopService(Intent(ctx, AriaPlaybackService::class.java))
|
|
promise.resolve(true)
|
|
} catch (e: Exception) {
|
|
Log.w(TAG, "stop fehlgeschlagen: ${e.message}")
|
|
promise.reject("STOP_FAILED", e.message ?: "Unbekannter Fehler", e)
|
|
}
|
|
}
|
|
|
|
@ReactMethod fun addListener(eventName: String) {}
|
|
@ReactMethod fun removeListeners(count: Int) {}
|
|
}
|