Tweak settings UI
This commit is contained in:
parent
c21a4eddb5
commit
9e8066b588
|
@ -15,7 +15,6 @@ import androidx.compose.ui.draw.alpha
|
||||||
import androidx.compose.ui.geometry.center
|
import androidx.compose.ui.geometry.center
|
||||||
import androidx.compose.ui.graphics.*
|
import androidx.compose.ui.graphics.*
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.text.TextStyle
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import it.vfsfitvnm.route.*
|
import it.vfsfitvnm.route.*
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
|
@ -372,50 +371,6 @@ fun SettingsEntry(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
|
||||||
@NonRestartableComposable
|
|
||||||
fun DisabledSettingsEntry(
|
|
||||||
title: String,
|
|
||||||
text: String,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
) {
|
|
||||||
BaseSettingsEntry(
|
|
||||||
title = title,
|
|
||||||
text = text,
|
|
||||||
modifier = modifier,
|
|
||||||
titleTextStyle = { disabled },
|
|
||||||
textStyle = { disabled },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun BaseSettingsEntry(
|
|
||||||
title: String,
|
|
||||||
text: String,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
titleTextStyle: @Composable TextStyle.() -> TextStyle = { this },
|
|
||||||
textStyle: @Composable TextStyle.() -> TextStyle = { this },
|
|
||||||
) {
|
|
||||||
val (_, typography) = LocalAppearance.current
|
|
||||||
|
|
||||||
Column(
|
|
||||||
modifier = modifier
|
|
||||||
.padding(start = 24.dp)
|
|
||||||
.padding(horizontal = 32.dp, vertical = 16.dp)
|
|
||||||
.fillMaxWidth()
|
|
||||||
) {
|
|
||||||
BasicText(
|
|
||||||
text = title,
|
|
||||||
style = typography.xs.semiBold.run { titleTextStyle() },
|
|
||||||
)
|
|
||||||
|
|
||||||
BasicText(
|
|
||||||
text = text,
|
|
||||||
style = typography.xs.semiBold.secondary.run { textStyle() }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsTitle(
|
fun SettingsTitle(
|
||||||
text: String,
|
text: String,
|
||||||
|
@ -431,6 +386,39 @@ fun SettingsTitle(
|
||||||
.padding(all = 16.dp)
|
.padding(all = 16.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SettingsDescription(
|
||||||
|
text: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val (_, typography) = LocalAppearance.current
|
||||||
|
|
||||||
|
BasicText(
|
||||||
|
text = text,
|
||||||
|
style = typography.xxs.secondary,
|
||||||
|
modifier = modifier
|
||||||
|
.padding(start = 56.dp, end = 24.dp)
|
||||||
|
.padding(bottom = 16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun SettingsGroupDescription(
|
||||||
|
text: String,
|
||||||
|
modifier: Modifier = Modifier,
|
||||||
|
) {
|
||||||
|
val (_, typography) = LocalAppearance.current
|
||||||
|
|
||||||
|
BasicText(
|
||||||
|
text = text,
|
||||||
|
style = typography.xxs.secondary,
|
||||||
|
modifier = modifier
|
||||||
|
.padding(start = 56.dp, end = 24.dp)
|
||||||
|
.padding(vertical = 8.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun SettingsEntryGroupText(
|
fun SettingsEntryGroupText(
|
||||||
title: String,
|
title: String,
|
||||||
|
|
|
@ -21,7 +21,7 @@ import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.BuildConfig
|
import it.vfsfitvnm.vimusic.BuildConfig
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.DisabledSettingsEntry
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsDescription
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntry
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntry
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntryGroupText
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntryGroupText
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.SettingsTitle
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsTitle
|
||||||
|
@ -64,13 +64,20 @@ fun AboutScreen() {
|
||||||
|
|
||||||
SettingsTitle(text = "About")
|
SettingsTitle(text = "About")
|
||||||
|
|
||||||
SettingsEntryGroupText(title = "INFO")
|
SettingsDescription(text = "v${BuildConfig.VERSION_NAME}\nby vfsfitvnm")
|
||||||
|
|
||||||
DisabledSettingsEntry(
|
SettingsEntryGroupText(title = "SOCIAL")
|
||||||
title = "ViMusic",
|
|
||||||
text = "v${BuildConfig.VERSION_NAME}",
|
SettingsEntry(
|
||||||
|
title = "GitHub",
|
||||||
|
text = "View the source code",
|
||||||
|
onClick = {
|
||||||
|
uriHandler.openUri("https://github.com/vfsfitvnm/ViMusic")
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
SettingsEntryGroupText(title = "TROUBLESHOOTING")
|
||||||
|
|
||||||
SettingsEntry(
|
SettingsEntry(
|
||||||
title = "Report an issue",
|
title = "Report an issue",
|
||||||
text = "You will be redirected to GitHub",
|
text = "You will be redirected to GitHub",
|
||||||
|
@ -86,16 +93,6 @@ fun AboutScreen() {
|
||||||
uriHandler.openUri("https://github.com/vfsfitvnm/ViMusic/issues/new?assignees=&labels=enhancement&template=feature_request.yaml")
|
uriHandler.openUri("https://github.com/vfsfitvnm/ViMusic/issues/new?assignees=&labels=enhancement&template=feature_request.yaml")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
SettingsEntryGroupText(title = "SOCIAL")
|
|
||||||
|
|
||||||
SettingsEntry(
|
|
||||||
title = "GitHub",
|
|
||||||
text = "View the source code",
|
|
||||||
onClick = {
|
|
||||||
uriHandler.openUri("https://github.com/vfsfitvnm/ViMusic")
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@ import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
@ -35,9 +34,9 @@ import it.vfsfitvnm.vimusic.query
|
||||||
import it.vfsfitvnm.vimusic.service.PlayerService
|
import it.vfsfitvnm.vimusic.service.PlayerService
|
||||||
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog
|
import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
|
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntry
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntry
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntryGroupText
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntryGroupText
|
||||||
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsGroupDescription
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.SettingsTitle
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsTitle
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.globalRoutes
|
import it.vfsfitvnm.vimusic.ui.screens.globalRoutes
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
|
@ -144,6 +143,8 @@ fun BackupAndRestoreScreen() {
|
||||||
|
|
||||||
SettingsEntryGroupText(title = "BACKUP")
|
SettingsEntryGroupText(title = "BACKUP")
|
||||||
|
|
||||||
|
SettingsGroupDescription(text = "Exports the application database to the external storage.\nThis means playlists, song history, favorites songs will exported.\nThis operation excludes personal preferences (i.e. the theme mode) and the cache.")
|
||||||
|
|
||||||
SettingsEntry(
|
SettingsEntry(
|
||||||
title = "Backup",
|
title = "Backup",
|
||||||
text = "Export the database to the external storage",
|
text = "Export the database to the external storage",
|
||||||
|
@ -156,6 +157,8 @@ fun BackupAndRestoreScreen() {
|
||||||
|
|
||||||
SettingsEntryGroupText(title = "RESTORE")
|
SettingsEntryGroupText(title = "RESTORE")
|
||||||
|
|
||||||
|
SettingsGroupDescription(text = "Replaces the existing application database with the selected one.\nThis means every currently existing data will be wiped: the two databases won't be merged.\nIt is recommended to restore the database immediately after the application is installed on a new device.")
|
||||||
|
|
||||||
SettingsEntry(
|
SettingsEntry(
|
||||||
title = "Restore",
|
title = "Restore",
|
||||||
text = "Import the database from the external storage",
|
text = "Import the database from the external storage",
|
||||||
|
@ -163,21 +166,6 @@ fun BackupAndRestoreScreen() {
|
||||||
isShowingRestoreDialog = true
|
isShowingRestoreDialog = true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
TextCard(
|
|
||||||
icon = R.drawable.alert_circle,
|
|
||||||
) {
|
|
||||||
Title(text = "Backup")
|
|
||||||
Text(text = "The backup consists in exporting the application database to your device storage.\nThis means playlists, song history, favorites songs will exported.\nThis operation excludes personal preferences such as the theme mode and everything you can set in the Settings page.")
|
|
||||||
|
|
||||||
Spacer(
|
|
||||||
modifier = Modifier
|
|
||||||
.height(32.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
Title(text = "Restore")
|
|
||||||
Text(text = "The restore replaces the existing application database with the selected - previously exported - one.\nThis means every currently existing data will be wiped: THE TWO DATABASES WON'T BE MERGED.\nIt is recommended to restore the database immediately after the application is installed on a new device.")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,11 +32,11 @@ import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.enums.CoilDiskCacheMaxSize
|
import it.vfsfitvnm.vimusic.enums.CoilDiskCacheMaxSize
|
||||||
import it.vfsfitvnm.vimusic.enums.ExoPlayerDiskCacheMaxSize
|
import it.vfsfitvnm.vimusic.enums.ExoPlayerDiskCacheMaxSize
|
||||||
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
|
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.DisabledSettingsEntry
|
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.EnumValueSelectorSettingsEntry
|
import it.vfsfitvnm.vimusic.ui.screens.EnumValueSelectorSettingsEntry
|
||||||
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsDescription
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntry
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntry
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntryGroupText
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntryGroupText
|
||||||
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsGroupDescription
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.SettingsTitle
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsTitle
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.globalRoutes
|
import it.vfsfitvnm.vimusic.ui.screens.globalRoutes
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
|
@ -96,6 +96,8 @@ fun CacheSettingsScreen() {
|
||||||
|
|
||||||
SettingsTitle(text = "Cache")
|
SettingsTitle(text = "Cache")
|
||||||
|
|
||||||
|
SettingsDescription(text = "The cache follows the LRU (Least Recently Used) strategy: when it runs out of space, the resources that haven't been accessed for the longest time are cleared to accommodate the new ones.")
|
||||||
|
|
||||||
Coil.imageLoader(context).diskCache?.let { diskCache ->
|
Coil.imageLoader(context).diskCache?.let { diskCache ->
|
||||||
var diskCacheSize by remember(diskCache) {
|
var diskCacheSize by remember(diskCache) {
|
||||||
mutableStateOf(diskCache.size)
|
mutableStateOf(diskCache.size)
|
||||||
|
@ -103,6 +105,8 @@ fun CacheSettingsScreen() {
|
||||||
|
|
||||||
SettingsEntryGroupText(title = "IMAGE CACHE")
|
SettingsEntryGroupText(title = "IMAGE CACHE")
|
||||||
|
|
||||||
|
SettingsGroupDescription(text = "${Formatter.formatShortFileSize(context, diskCacheSize)} used (${diskCacheSize * 100 / coilDiskCacheMaxSize.bytes.coerceAtLeast(1)}%)")
|
||||||
|
|
||||||
EnumValueSelectorSettingsEntry(
|
EnumValueSelectorSettingsEntry(
|
||||||
title = "Max size",
|
title = "Max size",
|
||||||
selectedValue = coilDiskCacheMaxSize,
|
selectedValue = coilDiskCacheMaxSize,
|
||||||
|
@ -111,18 +115,6 @@ fun CacheSettingsScreen() {
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
DisabledSettingsEntry(
|
|
||||||
title = "Space used",
|
|
||||||
text = "${
|
|
||||||
Formatter.formatShortFileSize(
|
|
||||||
context,
|
|
||||||
diskCacheSize
|
|
||||||
)
|
|
||||||
} (${
|
|
||||||
diskCacheSize * 100 / coilDiskCacheMaxSize.bytes.coerceAtLeast(1)
|
|
||||||
}%)",
|
|
||||||
)
|
|
||||||
|
|
||||||
SettingsEntry(
|
SettingsEntry(
|
||||||
title = "Clear space",
|
title = "Clear space",
|
||||||
text = "Wipe every cached image",
|
text = "Wipe every cached image",
|
||||||
|
@ -144,6 +136,17 @@ fun CacheSettingsScreen() {
|
||||||
|
|
||||||
SettingsEntryGroupText(title = "SONG CACHE")
|
SettingsEntryGroupText(title = "SONG CACHE")
|
||||||
|
|
||||||
|
SettingsGroupDescription(
|
||||||
|
text = buildString {
|
||||||
|
append(Formatter.formatShortFileSize(context, diskCacheSize))
|
||||||
|
append(" used")
|
||||||
|
when (val size = exoPlayerDiskCacheMaxSize) {
|
||||||
|
ExoPlayerDiskCacheMaxSize.Unlimited -> {}
|
||||||
|
else -> append(" (${diskCacheSize * 100 / size.bytes}%)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
EnumValueSelectorSettingsEntry(
|
EnumValueSelectorSettingsEntry(
|
||||||
title = "Max size",
|
title = "Max size",
|
||||||
selectedValue = exoPlayerDiskCacheMaxSize,
|
selectedValue = exoPlayerDiskCacheMaxSize,
|
||||||
|
@ -151,23 +154,6 @@ fun CacheSettingsScreen() {
|
||||||
exoPlayerDiskCacheMaxSize = it
|
exoPlayerDiskCacheMaxSize = it
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
DisabledSettingsEntry(
|
|
||||||
title = "Space used",
|
|
||||||
text = buildString {
|
|
||||||
append(Formatter.formatShortFileSize(context, diskCacheSize))
|
|
||||||
|
|
||||||
when (val size = exoPlayerDiskCacheMaxSize) {
|
|
||||||
ExoPlayerDiskCacheMaxSize.Unlimited -> {}
|
|
||||||
else -> append("(${diskCacheSize * 100 / size.bytes}%)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
TextCard(icon = R.drawable.alert_circle) {
|
|
||||||
Title(text = "Cache strategy")
|
|
||||||
Text(text = "The cache follows the LRU (Least Recently Used) strategy: when it runs out of space, the resources that haven't been accessed for the longest time are cleared to accommodate the new resource.")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,6 @@ import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
@ -34,9 +33,10 @@ import androidx.compose.ui.unit.dp
|
||||||
import it.vfsfitvnm.route.RouteHandler
|
import it.vfsfitvnm.route.RouteHandler
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
import it.vfsfitvnm.vimusic.ui.components.TopAppBar
|
||||||
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsDescription
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntry
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntry
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntryGroupText
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsEntryGroupText
|
||||||
|
import it.vfsfitvnm.vimusic.ui.screens.SettingsGroupDescription
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.SwitchSettingEntry
|
import it.vfsfitvnm.vimusic.ui.screens.SwitchSettingEntry
|
||||||
import it.vfsfitvnm.vimusic.ui.screens.globalRoutes
|
import it.vfsfitvnm.vimusic.ui.screens.globalRoutes
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
|
@ -101,6 +101,12 @@ fun OtherSettingsScreen() {
|
||||||
|
|
||||||
SettingsEntryGroupText(title = "SERVICE LIFETIME")
|
SettingsEntryGroupText(title = "SERVICE LIFETIME")
|
||||||
|
|
||||||
|
SettingsGroupDescription(text = "Some device manufacturers may have an aggressive policy against stopped foreground services - the media notification can disappear suddenly when paused.\nThe gentle approach consists in disabling battery optimizations - this is enough for some devices and ROMs.\nHowever, if it's not, you can make the service \"invincible\" - which should keep the service alive.")
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
SettingsDescription(text = "Since Android 12, the invincible service works ONLY if battery optimizations are disabled for this application.")
|
||||||
|
}
|
||||||
|
|
||||||
SettingsEntry(
|
SettingsEntry(
|
||||||
title = "Ignore battery optimizations",
|
title = "Ignore battery optimizations",
|
||||||
isEnabled = !isIgnoringBatteryOptimizations,
|
isEnabled = !isIgnoringBatteryOptimizations,
|
||||||
|
@ -145,21 +151,6 @@ fun OtherSettingsScreen() {
|
||||||
isInvincibilityEnabled = it
|
isInvincibilityEnabled = it
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
TextCard(icon = R.drawable.alert_circle) {
|
|
||||||
Title(text = "Service lifetime")
|
|
||||||
Text(text = "Some device manufacturers may have an aggressive policy against stopped foreground services - the media notification can disappear suddenly when paused.\nThe gentle approach consists in disabling battery optimizations - this is enough for some devices and ROMs.\nHowever, if it's not, you can make the service \"invincible\" - which should keep the service alive.")
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
|
||||||
Spacer(
|
|
||||||
modifier = Modifier
|
|
||||||
.height(32.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
Title(text = "Invincible service")
|
|
||||||
Text(text = "Since Android 12, this option works ONLY if battery optimizations are disabled for this application.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue