Files
GPS2Audio/app/src/main/kotlin/de/waypointaudio/ui/BackgroundReliabilityDialog.kt
T
2026-05-30 05:42:44 +00:00

266 lines
11 KiB
Kotlin

package de.waypointaudio.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import de.waypointaudio.R
import de.waypointaudio.data.BackgroundReliabilityStore
import de.waypointaudio.util.AppVersion
import de.waypointaudio.util.BackgroundReliability
import kotlinx.coroutines.launch
/**
* v2.5.13 — Dezenter Hinweis "Hintergrundwiedergabe absichern".
*
* Anzeigeregeln:
* - Nur sichtbar, wenn die App NICHT von Akkuoptimierungen ausgenommen ist
* (sonst gibt es nichts zu tun) UND der Nutzer den Hinweis für diese
* Version noch nicht weggeklickt hat.
* - Auf Xiaomi/Redmi/Poco wird die spezifische Schrittliste (Akku, Autostart,
* Recents-Schloss, Notifications) gezeigt — sonst eine generische Variante.
* - "Verstanden" merkt den aktuellen versionCode, "Nicht mehr anzeigen" ist
* eine Checkbox, weil so vorhandene Dialoge in der App (Versionshinweis,
* Terms) bereits funktionieren — konsistente UX.
* - Blockiert die Wiedergabe nicht: der Nutzer kann den Dialog jederzeit
* schließen, und das Playback läuft währenddessen weiter.
*/
@Composable
fun BackgroundReliabilityDialogHost(
/**
* Auslöser, ob der Hinweis überhaupt geprüft werden soll. Wird die
* Komponente immer in der Composition gehalten, übernimmt sie selbst
* die Anzeige-Entscheidung; das ist gewollt einfach.
*/
triggerCheck: Boolean,
onDismissed: () -> Unit = {}
) {
val context = LocalContext.current
val store = remember(context) {
BackgroundReliabilityStore(context.applicationContext)
}
val versionInfo = remember(context) { AppVersion.get(context) }
val dismissedCode by store.dismissedVersionCode.collectAsState(initial = -1L)
val scope = rememberCoroutineScope()
var visible by remember { mutableStateOf(false) }
var dontShowAgain by remember { mutableStateOf(false) }
LaunchedEffect(triggerCheck, dismissedCode, versionInfo.versionCode) {
if (!triggerCheck) return@LaunchedEffect
if (dismissedCode < 0L) return@LaunchedEffect
if (dismissedCode >= versionInfo.versionCode) return@LaunchedEffect
// Bereits ausgenommen → kein Bedarf, Nutzer mit dem Hinweis zu nerven.
if (BackgroundReliability.isIgnoringBatteryOptimizations(context)) return@LaunchedEffect
visible = true
}
if (!visible) return
BackgroundReliabilityDialog(
isXiaomi = BackgroundReliability.isXiaomiFamily(),
isOptimizationsIgnored = BackgroundReliability.isIgnoringBatteryOptimizations(context),
dontShowAgain = dontShowAgain,
onDontShowAgainChanged = { dontShowAgain = it },
onOpenBattery = { BackgroundReliability.openBatteryOptimizationSettings(context) },
onOpenAppDetails = { BackgroundReliability.openAppDetailsSettings(context) },
onOpenNotifications = { BackgroundReliability.openNotificationSettings(context) },
onOpenXiaomiAutostart = { BackgroundReliability.openXiaomiAutostartSettings(context) },
onDismiss = {
if (dontShowAgain) {
scope.launch {
store.setDismissedVersionCode(versionInfo.versionCode)
}
}
visible = false
onDismissed()
}
)
}
/**
* Manuell aufrufbarer Dialog für den Einstellungen/About-Bereich. Ignoriert
* den "für Version verstanden"-Speicher und zeigt den Hinweis immer.
*/
@Composable
fun BackgroundReliabilityManualDialog(
onClose: () -> Unit
) {
val context = LocalContext.current
BackgroundReliabilityDialog(
isXiaomi = BackgroundReliability.isXiaomiFamily(),
isOptimizationsIgnored = BackgroundReliability.isIgnoringBatteryOptimizations(context),
dontShowAgain = false,
onDontShowAgainChanged = {},
showDontShowAgain = false,
onOpenBattery = { BackgroundReliability.openBatteryOptimizationSettings(context) },
onOpenAppDetails = { BackgroundReliability.openAppDetailsSettings(context) },
onOpenNotifications = { BackgroundReliability.openNotificationSettings(context) },
onOpenXiaomiAutostart = { BackgroundReliability.openXiaomiAutostartSettings(context) },
onDismiss = onClose
)
}
@Composable
private fun BackgroundReliabilityDialog(
isXiaomi: Boolean,
isOptimizationsIgnored: Boolean,
dontShowAgain: Boolean,
onDontShowAgainChanged: (Boolean) -> Unit,
onOpenBattery: () -> Unit,
onOpenAppDetails: () -> Unit,
onOpenNotifications: () -> Unit,
onOpenXiaomiAutostart: () -> Unit,
onDismiss: () -> Unit,
showDontShowAgain: Boolean = true
) {
AlertDialog(
onDismissRequest = onDismiss,
title = {
Text(
text = stringResource(R.string.bg_reliability_title),
style = MaterialTheme.typography.headlineSmall
)
},
text = {
Column(
modifier = Modifier.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text(
text = stringResource(R.string.bg_reliability_intro),
style = MaterialTheme.typography.bodyMedium
)
Card(
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surfaceVariant
)
) {
Box(Modifier.padding(12.dp)) {
Text(
text = if (isOptimizationsIgnored) {
stringResource(R.string.bg_reliability_status_ok)
} else {
stringResource(R.string.bg_reliability_status_optimized)
},
style = MaterialTheme.typography.bodySmall
)
}
}
if (isXiaomi) {
Text(
text = stringResource(R.string.bg_reliability_xiaomi_heading),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
)
BulletLine(stringResource(R.string.bg_reliability_xiaomi_item_battery))
BulletLine(stringResource(R.string.bg_reliability_xiaomi_item_autostart))
BulletLine(stringResource(R.string.bg_reliability_xiaomi_item_lock))
BulletLine(stringResource(R.string.bg_reliability_xiaomi_item_notify))
} else {
Text(
text = stringResource(R.string.bg_reliability_generic_heading),
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.SemiBold
)
BulletLine(stringResource(R.string.bg_reliability_generic_item_battery))
BulletLine(stringResource(R.string.bg_reliability_generic_item_notify))
}
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
OutlinedButton(
onClick = onOpenBattery,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.bg_reliability_btn_battery))
}
OutlinedButton(
onClick = onOpenNotifications,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.bg_reliability_btn_notify))
}
OutlinedButton(
onClick = onOpenAppDetails,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.bg_reliability_btn_app))
}
if (isXiaomi) {
OutlinedButton(
onClick = onOpenXiaomiAutostart,
modifier = Modifier.fillMaxWidth()
) {
Text(stringResource(R.string.bg_reliability_btn_autostart))
}
}
}
if (showDontShowAgain) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth()
.padding(top = 4.dp)
) {
Checkbox(
checked = dontShowAgain,
onCheckedChange = onDontShowAgainChanged
)
Text(
text = stringResource(R.string.bg_reliability_dont_show_again),
style = MaterialTheme.typography.bodyMedium
)
}
}
}
},
confirmButton = {
TextButton(onClick = onDismiss) {
Text(stringResource(R.string.bg_reliability_understood))
}
}
)
}
@Composable
private fun BulletLine(text: String) {
Row(verticalAlignment = Alignment.Top) {
Text(
text = "",
style = MaterialTheme.typography.bodyMedium
)
Text(
text = text,
style = MaterialTheme.typography.bodyMedium
)
}
}