From 13357d10d791e93f5af03116ed0b22b5ee9827fd Mon Sep 17 00:00:00 2001 From: vfsfitvnm Date: Fri, 10 Jun 2022 21:14:34 +0200 Subject: [PATCH] Add OtherScreen in settings --- .../it/vfsfitvnm/vimusic/MainApplication.kt | 3 +- .../vimusic/ui/screens/SettingsScreen.kt | 96 +++++++++- .../ui/screens/settings/OtherScreen.kt | 170 ++++++++++++++++++ .../vimusic/ui/screens/settings/routes.kt | 7 + .../vimusic/ui/styling/ColorPalette.kt | 3 + .../it/vfsfitvnm/vimusic/utils/Preferences.kt | 1 + app/src/main/res/drawable/shapes.xml | 12 ++ 7 files changed, 284 insertions(+), 8 deletions(-) create mode 100644 app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherScreen.kt create mode 100644 app/src/main/res/drawable/shapes.xml diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainApplication.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainApplication.kt index 4e9050f..f83a998 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainApplication.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/MainApplication.kt @@ -5,6 +5,7 @@ import android.content.Context import coil.ImageLoader import coil.ImageLoaderFactory import coil.disk.DiskCache +import it.vfsfitvnm.vimusic.utils.preferences class MainApplication : Application(), ImageLoaderFactory { @@ -14,7 +15,7 @@ class MainApplication : Application(), ImageLoaderFactory { } override fun newImageLoader(): ImageLoader { - return defaultCoilImageLoader(1024 * 1024 * 1024) + return defaultCoilImageLoader(preferences.coilDiskCacheMaxSizeBytes) } } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SettingsScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SettingsScreen.kt index b4ae127..b58829d 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SettingsScreen.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/SettingsScreen.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.dp import it.vfsfitvnm.route.* import it.vfsfitvnm.vimusic.R @@ -22,6 +23,7 @@ import it.vfsfitvnm.vimusic.ui.components.themed.EnumValueSelectorDialog import it.vfsfitvnm.vimusic.ui.screens.settings.* import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalTypography +import it.vfsfitvnm.vimusic.utils.disabled import it.vfsfitvnm.vimusic.utils.medium import it.vfsfitvnm.vimusic.utils.secondary import it.vfsfitvnm.vimusic.utils.semiBold @@ -33,6 +35,7 @@ fun SettingsScreen() { val artistRoute = rememberArtistRoute() val appearanceRoute = rememberAppearanceRoute() val backupAndRestoreRoute = rememberBackupAndRestoreRoute() + val otherRoute = rememberOtherRoute() val aboutRoute = rememberAboutRoute() val scrollState = rememberScrollState() @@ -41,9 +44,9 @@ fun SettingsScreen() { listenToGlobalEmitter = true, transitionSpec = { when (targetState.route) { - appearanceRoute, backupAndRestoreRoute, aboutRoute -> leftSlide + appearanceRoute, backupAndRestoreRoute, otherRoute, aboutRoute -> leftSlide else -> when (initialState.route) { - appearanceRoute, backupAndRestoreRoute, aboutRoute -> rightSlide + appearanceRoute, backupAndRestoreRoute, otherRoute, aboutRoute -> rightSlide else -> fastFade } } @@ -69,6 +72,10 @@ fun SettingsScreen() { BackupAndRestoreScreen() } + otherRoute { + OtherScreen() + } + aboutRoute { AboutScreen() } @@ -178,6 +185,14 @@ fun SettingsScreen() { route = backupAndRestoreRoute ) + Entry( + color = colorPalette.magenta, + icon = R.drawable.shapes, + title = "Other", + description = "Advanced options", + route = otherRoute + ) + Entry( color = colorPalette.green, icon = R.drawable.information, @@ -216,25 +231,92 @@ inline fun >EnumValueSelectorEntry( ) } - Column( + SettingsEntry( + title = title, + text = valueText(selectedValue), + modifier = modifier, + onClick = { + isShowingDialog = true + } + ) +} + +@Composable +@NonRestartableComposable +fun SettingsEntry( + title: String, + text: String, + modifier: Modifier = Modifier, + onClick: () -> Unit +) { + BaseSettingsEntry( + title = title, + text = text, modifier = modifier .clickable( indication = rememberRipple(bounded = true), interactionSource = remember { MutableInteractionSource() }, - onClick = { isShowingDialog = true } + onClick = onClick ) + ) +} + +@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 = LocalTypography.current + + Column( + modifier = modifier .padding(start = 24.dp) .padding(horizontal = 32.dp, vertical = 16.dp) .fillMaxWidth() ) { BasicText( text = title, - style = typography.xs.semiBold, + style = typography.xs.semiBold.run { titleTextStyle() }, ) BasicText( - text = valueText(selectedValue), - style = typography.xs.semiBold.secondary + text = text, + style = typography.xs.semiBold.secondary.run { textStyle() } ) } +} + +@Composable +fun SettingsEntryGroupText( + title: String, + modifier: Modifier = Modifier, +) { + val typography = LocalTypography.current + + BasicText( + text = title.uppercase(), + style = typography.xs.semiBold, + modifier = modifier + .padding(start = 24.dp, top = 24.dp) + .padding(horizontal = 32.dp) + ) } \ No newline at end of file diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherScreen.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherScreen.kt new file mode 100644 index 0000000..5c50488 --- /dev/null +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/OtherScreen.kt @@ -0,0 +1,170 @@ +package it.vfsfitvnm.vimusic.ui.screens.settings + +import android.text.format.Formatter +import androidx.compose.animation.ExperimentalAnimationApi +import androidx.compose.foundation.* +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.text.BasicText +import androidx.compose.runtime.* +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.unit.dp +import coil.Coil +import coil.annotation.ExperimentalCoilApi +import it.vfsfitvnm.route.RouteHandler +import it.vfsfitvnm.vimusic.R +import it.vfsfitvnm.vimusic.ui.components.SeekBar +import it.vfsfitvnm.vimusic.ui.components.TopAppBar +import it.vfsfitvnm.vimusic.ui.screens.* +import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette +import it.vfsfitvnm.vimusic.ui.styling.LocalTypography +import it.vfsfitvnm.vimusic.utils.LocalPreferences +import it.vfsfitvnm.vimusic.utils.secondary +import it.vfsfitvnm.vimusic.utils.semiBold +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +@OptIn(ExperimentalCoilApi::class) +@ExperimentalAnimationApi +@Composable +fun OtherScreen() { + val albumRoute = rememberPlaylistOrAlbumRoute() + val artistRoute = rememberArtistRoute() + + val scrollState = rememberScrollState() + + RouteHandler(listenToGlobalEmitter = true) { + albumRoute { browseId -> + PlaylistOrAlbumScreen( + browseId = browseId ?: error("browseId cannot be null") + ) + } + + artistRoute { browseId -> + ArtistScreen( + browseId = browseId ?: error("browseId cannot be null") + ) + } + + host { + val context = LocalContext.current + val colorPalette = LocalColorPalette.current + val typography = LocalTypography.current + val preferences = LocalPreferences.current + + var coilDiskCache by remember { + mutableStateOf(Coil.imageLoader(context).diskCache) + } + + val coroutineScope = rememberCoroutineScope() + + Column( + modifier = Modifier + .background(colorPalette.background) + .fillMaxSize() + .verticalScroll(scrollState) + .padding(bottom = 72.dp) + ) { + TopAppBar( + modifier = Modifier + .height(52.dp) + ) { + Image( + painter = painterResource(R.drawable.chevron_back), + contentDescription = null, + colorFilter = ColorFilter.tint(colorPalette.text), + modifier = Modifier + .clickable(onClick = pop) + .padding(horizontal = 16.dp, vertical = 8.dp) + .size(24.dp) + ) + + BasicText( + text = "Other", + style = typography.m.semiBold + ) + + Spacer( + modifier = Modifier + .padding(horizontal = 16.dp, vertical = 8.dp) + .size(24.dp) + ) + } + + + coilDiskCache?.let { diskCache -> + var diskCacheSize by remember(diskCache) { + mutableStateOf(diskCache.size) + } + + var scrubbingDiskCacheMaxSize by remember { + mutableStateOf(null) + } + + SettingsEntryGroupText( + title = "IMAGE CACHE", + ) + + Column( + modifier = Modifier + .padding(start = 24.dp) + .padding(horizontal = 32.dp, vertical = 16.dp) + .fillMaxWidth() + ) { + BasicText( + text = "Max size", + style = typography.xs.semiBold, + ) + + BasicText( + text = Formatter.formatShortFileSize(context, scrubbingDiskCacheMaxSize ?: preferences.coilDiskCacheMaxSizeBytes), + style = typography.xs.semiBold.secondary + ) + + SeekBar( + value = (scrubbingDiskCacheMaxSize ?: preferences.coilDiskCacheMaxSizeBytes).coerceIn(250L * 1024 * 1024, 2048L * 1024 * 1024), + minimumValue = 250L * 1024 * 1024, + maximumValue = 2048L * 1024 * 1024, + onDragStart = { + scrubbingDiskCacheMaxSize = it + }, + onDrag = { delta -> + scrubbingDiskCacheMaxSize = scrubbingDiskCacheMaxSize?.plus(delta)?.coerceIn(250L * 1024 * 1024, 2048L * 1024 * 1024) + println("new = $scrubbingDiskCacheMaxSize") + }, + onDragEnd = { + preferences.coilDiskCacheMaxSizeBytes = scrubbingDiskCacheMaxSize ?: preferences.coilDiskCacheMaxSizeBytes + scrubbingDiskCacheMaxSize = null + }, + color = colorPalette.text, + backgroundColor = colorPalette.textDisabled, + shape = RoundedCornerShape(8.dp), + modifier = Modifier + .padding(top = 8.dp) + .fillMaxWidth() + ) + } + + DisabledSettingsEntry( + title = "Space used", + text = "${Formatter.formatShortFileSize(context, diskCacheSize)} (${diskCacheSize * 100 / preferences.coilDiskCacheMaxSizeBytes.coerceAtLeast(1)}%)", + ) + + SettingsEntry( + title = "Clear space", + text = "Wipe every cached image", + onClick = { + coroutineScope.launch(Dispatchers.IO) { + diskCache.clear() + diskCacheSize = diskCache.size + } + } + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/routes.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/routes.kt index 9fcda90..b2a1caa 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/routes.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/settings/routes.kt @@ -18,6 +18,13 @@ fun rememberBackupAndRestoreRoute(): Route0 { } } +@Composable +fun rememberOtherRoute(): Route0 { + return remember { + Route0("OtherRoute") + } +} + @Composable fun rememberAboutRoute(): Route0 { return remember { diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/styling/ColorPalette.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/styling/ColorPalette.kt index dc119f4..b46ca3d 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/styling/ColorPalette.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/styling/ColorPalette.kt @@ -18,6 +18,7 @@ data class ColorPalette( val red: Color, val green: Color, val orange: Color, + val magenta: Color, val primaryContainer: Color, val onPrimaryContainer: Color, @@ -38,6 +39,7 @@ val DarkColorPalette = ColorPalette( red = Color(0xffbf4040), green = Color(0xff82b154), orange = Color(0xffe9a033), + magenta = Color(0xffbb4da4), primaryContainer = Color(0xff4046bf), onPrimaryContainer = Color.White, @@ -64,6 +66,7 @@ val LightColorPalette = ColorPalette( red = Color(0xffbf4040), green = Color(0xff7fbf40), orange = Color(0xffe8730e), + magenta = Color(0xffbb4da4), primaryContainer = Color(0xff4046bf), onPrimaryContainer = Color.White, diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt index c74d8d8..ff1b7a8 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/utils/Preferences.kt @@ -22,6 +22,7 @@ class Preferences(holder: SharedPreferences) : SharedPreferences by holder { var repeatMode by preference("repeatMode", Player.REPEAT_MODE_OFF) var homePageSongCollection by preference("homePageSongCollection", SongCollection.MostPlayed) var thumbnailRoundness by preference("thumbnailRoundness", ThumbnailRoundness.Light) + var coilDiskCacheMaxSizeBytes by preference("coilDiskCacheMaxSizeBytes", 512L * 1024 * 1024) } val Context.preferences: Preferences diff --git a/app/src/main/res/drawable/shapes.xml b/app/src/main/res/drawable/shapes.xml new file mode 100644 index 0000000..3693f7e --- /dev/null +++ b/app/src/main/res/drawable/shapes.xml @@ -0,0 +1,12 @@ + + + +