package com.ariacockpit import android.app.Notification import android.app.NotificationChannel import android.app.NotificationManager import android.app.PendingIntent import android.app.Service import android.content.Intent import android.os.Build import android.os.IBinder import android.util.Log import androidx.core.app.NotificationCompat /** * Foreground-Service der den App-Prozess waehrend TTS-Wiedergabe am Leben * haelt — Android killt sonst den Prozess sobald die App im Hintergrund ist * und ARIA verstummt mitten im Satz. * * Notification ist persistent (ongoing) waehrend der Service laeuft. * Tap auf die Notification bringt MainActivity zurueck nach vorne. * * foregroundServiceType="mediaPlayback" ist Pflicht ab Android 14, sonst * wirft startForeground() eine SecurityException. */ class AriaPlaybackService : Service() { companion object { private const val TAG = "AriaPlaybackService" private const val CHANNEL_ID = "aria_playback" private const val NOTIFICATION_ID = 1042 const val EXTRA_REASON = "reason" // "tts" | "wake" | "rec" | "" } private var currentReason: String = "" override fun onCreate() { super.onCreate() ensureNotificationChannel() } override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { val reason = intent?.getStringExtra(EXTRA_REASON) ?: "" currentReason = reason Log.i(TAG, "Foreground-Service start/update (reason=$reason)") try { startForeground(NOTIFICATION_ID, buildNotification(reason)) } catch (e: Exception) { Log.e(TAG, "startForeground fehlgeschlagen", e) stopSelf() } // START_NOT_STICKY: wenn Android den Service killt, NICHT automatisch // wieder starten — die App entscheidet wann der Service noetig ist. return START_NOT_STICKY } override fun onDestroy() { Log.i(TAG, "Foreground-Service gestoppt") super.onDestroy() } override fun onBind(intent: Intent?): IBinder? = null private fun ensureNotificationChannel() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val nm = getSystemService(NotificationManager::class.java) ?: return if (nm.getNotificationChannel(CHANNEL_ID) == null) { val channel = NotificationChannel( CHANNEL_ID, "ARIA Audio-Wiedergabe", NotificationManager.IMPORTANCE_LOW, ).apply { description = "Notification waehrend ARIA spricht (haelt die App im Hintergrund am Leben)" setShowBadge(false) } nm.createNotificationChannel(channel) } } } private fun buildNotification(reason: String): Notification { val launchIntent = Intent(this, MainActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP } val pendingFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT else PendingIntent.FLAG_UPDATE_CURRENT val pendingIntent = PendingIntent.getActivity(this, 0, launchIntent, pendingFlags) val (title, body) = when (reason) { "tts" -> "ARIA spricht" to "Antwort wird abgespielt — antippen oeffnet die App" "rec" -> "ARIA hoert zu" to "Sprachaufnahme laeuft — antippen oeffnet die App" "wake" -> "ARIA bereit" to "Wake-Word lauscht passiv — antippen oeffnet die App" else -> "ARIA aktiv" to "Hintergrund-Modus — antippen oeffnet die App" } return NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle(title) .setContentText(body) .setSmallIcon(R.mipmap.ic_launcher) .setContentIntent(pendingIntent) .setOngoing(true) .setShowWhen(false) .setPriority(NotificationCompat.PRIORITY_LOW) .setCategory(NotificationCompat.CATEGORY_SERVICE) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .build() } }