From 167be6e34dc30db5e9656b060c4db2307638d466 Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Sat, 14 Jun 2025 17:17:07 +0200 Subject: [PATCH] (feat) transparency schemes --- .../mm20/launcher2/ui/base/ProvideSettings.kt | 12 +- .../launcher2/ui/common/ImportThemeSheet.kt | 2 +- .../launcher2/ui/common/ImportThemeSheetVM.kt | 7 +- .../launcher2/ui/component/LauncherCard.kt | 172 +--------- .../mm20/launcher2/ui/component/SearchBar.kt | 4 +- .../ui/launcher/scaffold/LauncherScaffold.kt | 10 +- .../ui/launcher/search/SearchColumn.kt | 4 +- .../launcher/search/common/grid/GridItem.kt | 3 +- .../search/common/grid/GridResults.kt | 8 +- .../search/common/list/ListResults.kt | 14 +- .../search/favorites/SearchFavorites.kt | 4 +- .../launcher/sheets/ConfigureWidgetSheet.kt | 3 +- .../ui/launcher/widgets/WidgetItem.kt | 4 +- .../ui/launcher/widgets/music/MusicWidget.kt | 4 +- .../launcher/widgets/weather/WeatherWidget.kt | 6 +- .../launcher2/ui/settings/SettingsActivity.kt | 12 + .../appearance/AppearanceSettingsScreen.kt | 11 + .../appearance/AppearanceSettingsScreenVM.kt | 21 +- .../appearance/ExportThemeSettingsScreen.kt | 15 +- .../appearance/ExportThemeSettingsScreenVM.kt | 21 +- .../appearance/ImportThemeSettingsScreen.kt | 48 ++- .../appearance/ImportThemeSettingsScreenVM.kt | 41 ++- .../CalendarProviderSettingsScreen.kt | 15 +- .../ui/settings/cards/CardsSettingsScreen.kt | 10 - .../colorscheme/ColorSchemeSettingsScreen.kt | 8 +- .../colorscheme/ColorSchemesSettingsScreen.kt | 8 +- .../ColorSchemesSettingsScreenVM.kt | 33 +- .../colorscheme/CorePaletteColorPreference.kt | 4 - .../colorscheme/ThemeColorPreference.kt | 17 +- .../settings/plugins/PluginSettingsScreen.kt | 10 - .../shapes/ShapeSchemeSettingsScreen.kt | 6 +- .../shapes/ShapeSchemesSettingsScreen.kt | 6 +- .../shapes/ShapeSchemesSettingsScreenVM.kt | 37 +-- .../settings/transparencies/Checkerboard.kt | 36 +++ .../TransparencySchemeSettingsScreen.kt | 304 ++++++++++++++++++ .../TransparencySchemesSettingsScreen.kt | 218 +++++++++++++ .../TransparencySchemesSettingsScreenVM.kt | 50 +++ .../mm20/launcher2/ui/theme/LauncherTheme.kt | 29 +- .../ui/theme/colorscheme/ColorScheme.kt | 27 +- .../mm20/launcher2/ui/theme/shapes/Shapes.kt | 6 +- .../theme/transparency/TransparencyScheme.kt | 27 +- core/i18n/src/main/res/values/strings.xml | 7 +- .../preferences/LauncherSettingsData.kt | 59 +--- .../launcher2/preferences/ui/UiSettings.kt | 30 +- .../de/mm20/launcher2/database/AppDatabase.kt | 6 +- .../mm20/launcher2/database/daos/ThemeDao.kt | 22 ++ .../database/entities/TransparenciesEntity.kt | 15 + .../database/migrations/Migration_28_29.kt | 22 ++ .../de/mm20/launcher2/themes/DefaultThemes.kt | 241 +------------- .../launcher2/themes/LegacySerialization.kt | 4 + .../de/mm20/launcher2/themes/Serialization.kt | 4 +- .../de/mm20/launcher2/themes/ThemeBundle.kt | 14 +- .../mm20/launcher2/themes/ThemeRepository.kt | 216 +------------ .../launcher2/themes/{ => colors}/Colors.kt | 2 +- .../themes/colors/ColorsRepository.kt | 102 ++++++ .../mm20/launcher2/themes/colors/Defaults.kt | 235 ++++++++++++++ .../launcher2/themes/{ => shapes}/Shapes.kt | 3 +- .../themes/shapes/ShapesRepository.kt | 129 ++++++++ .../themes/transparencies/Transparencies.kt | 35 ++ .../TransparenciesRepository.kt | 87 +++++ gradle/libs.versions.toml | 2 +- 61 files changed, 1608 insertions(+), 904 deletions(-) create mode 100644 app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/Checkerboard.kt create mode 100644 app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/TransparencySchemeSettingsScreen.kt create mode 100644 app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/TransparencySchemesSettingsScreen.kt create mode 100644 app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/TransparencySchemesSettingsScreenVM.kt create mode 100644 data/database/src/main/java/de/mm20/launcher2/database/entities/TransparenciesEntity.kt create mode 100644 data/database/src/main/java/de/mm20/launcher2/database/migrations/Migration_28_29.kt rename data/themes/src/main/java/de/mm20/launcher2/themes/{ => colors}/Colors.kt (99%) create mode 100644 data/themes/src/main/java/de/mm20/launcher2/themes/colors/ColorsRepository.kt create mode 100644 data/themes/src/main/java/de/mm20/launcher2/themes/colors/Defaults.kt rename data/themes/src/main/java/de/mm20/launcher2/themes/{ => shapes}/Shapes.kt (97%) create mode 100644 data/themes/src/main/java/de/mm20/launcher2/themes/shapes/ShapesRepository.kt create mode 100644 data/themes/src/main/java/de/mm20/launcher2/themes/transparencies/Transparencies.kt create mode 100644 data/themes/src/main/java/de/mm20/launcher2/themes/transparencies/TransparenciesRepository.kt diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/base/ProvideSettings.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/base/ProvideSettings.kt index d4b0f9a7..156bc1d3 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/base/ProvideSettings.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/base/ProvideSettings.kt @@ -1,6 +1,10 @@ package de.mm20.launcher2.ui.base -import androidx.compose.runtime.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import de.mm20.launcher2.preferences.IconShape import de.mm20.launcher2.preferences.ui.CardStyle import de.mm20.launcher2.preferences.ui.GridSettings @@ -9,8 +13,6 @@ import de.mm20.launcher2.ui.component.ProvideIconShape import de.mm20.launcher2.ui.locals.LocalCardStyle import de.mm20.launcher2.ui.locals.LocalFavoritesEnabled import de.mm20.launcher2.ui.locals.LocalGridSettings -import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme -import de.mm20.launcher2.ui.theme.transparency.TransparencyScheme import de.mm20.launcher2.widgets.FavoritesWidget import de.mm20.launcher2.widgets.WidgetRepository import kotlinx.coroutines.flow.combine @@ -48,10 +50,6 @@ fun ProvideSettings( LocalCardStyle provides cardStyle, LocalFavoritesEnabled provides favoritesEnabled, LocalGridSettings provides gridSettings, - LocalTransparencyScheme provides TransparencyScheme( - background = cardStyle.opacity * 0.85f, - surface = cardStyle.opacity, - ) ) { ProvideIconShape(iconShape) { content() diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheet.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheet.kt index da651816..12d7a344 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheet.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheet.kt @@ -40,7 +40,7 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.viewmodel.compose.viewModel -import de.mm20.launcher2.themes.Colors +import de.mm20.launcher2.themes.colors.Colors import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.BottomSheetDialog import de.mm20.launcher2.ui.component.LargeMessage diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheetVM.kt index bd842022..9afe57a6 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheetVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheetVM.kt @@ -5,9 +5,8 @@ import android.net.Uri import androidx.compose.runtime.mutableStateOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import de.mm20.launcher2.preferences.ColorsDescriptor import de.mm20.launcher2.preferences.ui.UiSettings -import de.mm20.launcher2.themes.Colors +import de.mm20.launcher2.themes.colors.Colors import de.mm20.launcher2.themes.ThemeRepository import de.mm20.launcher2.themes.fromLegacyJson import kotlinx.coroutines.Dispatchers @@ -58,9 +57,9 @@ class ImportThemeSheetVM : ViewModel(), KoinComponent { } private fun importTheme(colors: Colors, apply: Boolean) { - themeRepository.createColors(colors) + themeRepository.colors.create(colors) if (apply) { - uiSettings.setColors(ColorsDescriptor.Custom(colors.id.toString())) + uiSettings.setColorsId(colors.id) } } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/component/LauncherCard.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/component/LauncherCard.kt index 2391a478..f4344fa6 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/component/LauncherCard.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/component/LauncherCard.kt @@ -1,39 +1,29 @@ package de.mm20.launcher2.ui.component import androidx.compose.foundation.BorderStroke -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.shape.CornerSize -import androidx.compose.material3.LocalAbsoluteTonalElevation -import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.drawBehind -import androidx.compose.ui.draw.drawWithCache -import androidx.compose.ui.draw.shadow -import androidx.compose.ui.geometry.Offset import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.RectangleShape import androidx.compose.ui.graphics.Shape -import androidx.compose.ui.graphics.drawOutline -import androidx.compose.ui.graphics.drawscope.Stroke -import androidx.compose.ui.graphics.drawscope.withTransform -import androidx.compose.ui.unit.Density import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import de.mm20.launcher2.ui.locals.LocalCardStyle -import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme +import de.mm20.launcher2.ui.theme.transparency.transparency @Composable fun LauncherCard( modifier: Modifier = Modifier, elevation: Dp = 2.dp, - backgroundOpacity: Float = LocalTransparencyScheme.current.surface, + backgroundOpacity: Float = MaterialTheme.transparency.surface, shape: Shape = MaterialTheme.shapes.medium, - color: Color = MaterialTheme.colorScheme.surface.copy(alpha = backgroundOpacity.coerceIn(0f, 1f)), + color: Color = MaterialTheme.colorScheme.surface.copy( + alpha = backgroundOpacity.coerceIn( + 0f, + 1f + ) + ), border: BorderStroke? = LocalCardStyle.current.borderWidth.takeIf { it > 0 } ?.let { BorderStroke(it.dp, MaterialTheme.colorScheme.surface) }, content: @Composable () -> Unit = {} @@ -48,150 +38,4 @@ fun LauncherCard( shadowElevation = if (backgroundOpacity == 1f) elevation else 0.dp, tonalElevation = elevation, ) -} - -@Composable -fun PartialLauncherCard( - modifier: Modifier = Modifier, - isTop: Boolean = false, - isBottom: Boolean = false, - elevation: Dp = 2.dp, - backgroundOpacity: Float = LocalTransparencyScheme.current.surface, - content: @Composable () -> Unit -) { - - if (isTop && isBottom) { - LauncherCard(modifier = modifier, content = content) - } else if (!isTop && !isBottom) { - CardMiddlePiece(modifier = modifier, elevation = elevation, content = content) - } else { - CardEndPiece( - modifier = modifier, - isTop = isTop, - isBottom = isBottom, - elevation = elevation, - backgroundOpacity = backgroundOpacity, - content = content - ) - } -} - -@Composable -private fun CardMiddlePiece( - modifier: Modifier, - elevation: Dp, - backgroundOpacity: Float = LocalTransparencyScheme.current.surface, - content: @Composable () -> Unit -) { - val borderWidth = LocalCardStyle.current.borderWidth.dp - val borderColor = MaterialTheme.colorScheme.surface - - val absoluteElevation = LocalAbsoluteTonalElevation.current + elevation - Box( - modifier = modifier - .shadow(if (backgroundOpacity < 1f) 0.dp else elevation, RectangleShape, true) - .background( - if (backgroundOpacity == 1f) { - MaterialTheme.colorScheme.surfaceColorAtElevation(absoluteElevation) - } else { - MaterialTheme.colorScheme.surface.copy( - alpha = backgroundOpacity.coerceIn( - 0f, - 1f - ) - ) - } - ) - .drawBehind { - if (borderWidth == 0.dp) return@drawBehind - val border = borderWidth.toPx() - drawRect( - color = borderColor, - topLeft = Offset.Zero, - size = size.copy(width = border) - ) - drawRect( - color = borderColor, - topLeft = Offset(size.width - border, 0f), - size = size.copy(width = border) - ) - }, - ) { - CompositionLocalProvider( - LocalContentColor provides MaterialTheme.colorScheme.onSurface, - LocalAbsoluteTonalElevation provides absoluteElevation, - ) { - content() - } - } -} - -@Composable -private fun CardEndPiece( - modifier: Modifier = Modifier, - isTop: Boolean, - isBottom: Boolean, - elevation: Dp, - backgroundOpacity: Float, - content: @Composable () -> Unit, -) { - val shape = when { - isTop -> MaterialTheme.shapes.medium.copy( - bottomEnd = CornerSize(0), - bottomStart = CornerSize(0), - ) - isBottom -> MaterialTheme.shapes.medium.copy( - topEnd = CornerSize(0), - topStart = CornerSize(0), - ) - else -> RectangleShape - } - - val borderWidth = LocalCardStyle.current.borderWidth.dp - val borderColor = MaterialTheme.colorScheme.surface - - val absoluteElevation = LocalAbsoluteTonalElevation.current + elevation - Box( - modifier = modifier - .shadow(if (backgroundOpacity < 1f) 0.dp else elevation, shape, true) - .background( - if (backgroundOpacity == 1f) { - MaterialTheme.colorScheme.surfaceColorAtElevation(absoluteElevation) - } else { - MaterialTheme.colorScheme.surface.copy( - alpha = backgroundOpacity.coerceIn( - 0f, - 1f - ) - ) - } - ) - .drawWithCache { - val border = borderWidth.toPx() - val outline = shape.createOutline( - size.copy(height = size.height + border), - layoutDirection, - Density(density, fontScale) - ) - onDrawBehind { - if (borderWidth == 0.dp) return@onDrawBehind - withTransform({ - translate(0f, if (isBottom) -border else 0f) - }) { - drawOutline( - outline, - borderColor, - style = Stroke(width = border * 2) - ) - } - } - }, - ) { - CompositionLocalProvider( - LocalContentColor provides MaterialTheme.colorScheme.onSurface, - LocalAbsoluteTonalElevation provides absoluteElevation, - ) { - content() - } - } } \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/component/SearchBar.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/component/SearchBar.kt index 2a96beb1..2c1befed 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/component/SearchBar.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/component/SearchBar.kt @@ -45,7 +45,7 @@ import androidx.compose.ui.unit.dp import de.mm20.launcher2.preferences.SearchBarStyle import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.layout.BottomReversed -import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme +import de.mm20.launcher2.ui.theme.transparency.transparency @Composable fun SearchBar( @@ -102,7 +102,7 @@ fun SearchBar( } }) { when { - it == SearchBarLevel.Active -> LocalTransparencyScheme.current.surface + it == SearchBarLevel.Active -> MaterialTheme.transparency.surface style != SearchBarStyle.Transparent -> 1f it == SearchBarLevel.Resting -> 0f else -> 1f diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/scaffold/LauncherScaffold.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/scaffold/LauncherScaffold.kt index a734496d..aaa318c0 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/scaffold/LauncherScaffold.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/scaffold/LauncherScaffold.kt @@ -100,7 +100,7 @@ import de.mm20.launcher2.ui.launcher.helper.WallpaperBlur import de.mm20.launcher2.ui.launcher.search.SearchVM import de.mm20.launcher2.ui.launcher.search.filters.KeyboardFilterBar import de.mm20.launcher2.ui.launcher.searchbar.LauncherSearchBar -import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme +import de.mm20.launcher2.ui.theme.transparency.transparency import dev.chrisbanes.haze.hazeEffect import dev.chrisbanes.haze.hazeSource import dev.chrisbanes.haze.rememberHazeState @@ -1416,7 +1416,7 @@ internal fun LauncherScaffold( .homePageAnimation( state, if (config.homeComponent.drawBackground) { - config.backgroundColor.copy(alpha = LocalTransparencyScheme.current.background) + config.backgroundColor.copy(alpha = MaterialTheme.transparency.background) } else { Color.Transparent } @@ -1516,7 +1516,7 @@ internal fun LauncherScaffold( blurRadius = 4.dp } .background( - MaterialTheme.colorScheme.surfaceContainer.copy(alpha = LocalTransparencyScheme.current.background) + MaterialTheme.colorScheme.surfaceContainer.copy(alpha = MaterialTheme.transparency.background) ) .statusBarsPadding() ) @@ -1536,7 +1536,7 @@ internal fun LauncherScaffold( blurRadius = 4.dp } .background( - MaterialTheme.colorScheme.surfaceContainer.copy(alpha = LocalTransparencyScheme.current.background) + MaterialTheme.colorScheme.surfaceContainer.copy(alpha = MaterialTheme.transparency.background) ) .navigationBarsPadding() ) @@ -1583,7 +1583,7 @@ private fun SecondaryPage( .fillMaxSize() .secondaryPageAnimation( state, - config.backgroundColor.copy(alpha = LocalTransparencyScheme.current.background), + config.backgroundColor.copy(alpha = MaterialTheme.transparency.background), ) val composable = composables[component] diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt index 2530b607..662931ff 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/SearchColumn.kt @@ -53,7 +53,7 @@ import de.mm20.launcher2.ui.launcher.sheets.HiddenItemsSheet import de.mm20.launcher2.ui.launcher.sheets.LocalBottomSheetManager import de.mm20.launcher2.ui.locals.LocalCardStyle import de.mm20.launcher2.ui.locals.LocalGridSettings -import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme +import de.mm20.launcher2.ui.theme.transparency.transparency @Composable fun SearchColumn( @@ -370,7 +370,7 @@ fun LazyListScope.SingleResult( vertical = 4.dp, ), color = if (highlight) MaterialTheme.colorScheme.secondaryContainer - else MaterialTheme.colorScheme.surface.copy(LocalTransparencyScheme.current.surface) + else MaterialTheme.colorScheme.surface.copy(MaterialTheme.transparency.surface) ) { content() } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/grid/GridItem.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/grid/GridItem.kt index 60e7754b..9dd5a659 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/grid/GridItem.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/grid/GridItem.kt @@ -78,6 +78,7 @@ import de.mm20.launcher2.ui.launcher.transitions.HandleEnterHomeTransition import de.mm20.launcher2.ui.locals.LocalGridSettings import de.mm20.launcher2.ui.locals.LocalWindowSize import de.mm20.launcher2.ui.overlays.Overlay +import de.mm20.launcher2.ui.theme.transparency.transparency import kotlin.math.pow @@ -266,7 +267,7 @@ fun ItemPopup(origin: IntRect, searchable: Searchable, onDismissRequest: () -> U ) { LauncherCard( elevation = 8.dp * animationProgress.value, - backgroundOpacity = 1f, + backgroundOpacity = MaterialTheme.transparency.elevatedSurface, modifier = Modifier .placeOverlay( origin.translate( diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/grid/GridResults.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/grid/GridResults.kt index dfd44d30..7a7ede57 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/grid/GridResults.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/grid/GridResults.kt @@ -13,7 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.unit.dp import de.mm20.launcher2.search.SavableSearchable import de.mm20.launcher2.ui.ktx.withCorners -import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme +import de.mm20.launcher2.ui.theme.transparency.transparency import kotlin.math.ceil fun LazyListScope.GridResults( @@ -39,7 +39,7 @@ fun LazyListScope.GridResults( bottom = if (!reverse && isBottom) 8.dp else 0.dp, ) .background( - MaterialTheme.colorScheme.surface.copy(alpha = LocalTransparencyScheme.current.surface), + MaterialTheme.colorScheme.surface.copy(alpha = MaterialTheme.transparency.surface), MaterialTheme.shapes.medium.withCorners( topStart = isTop, topEnd = isTop, @@ -72,7 +72,7 @@ fun LazyListScope.GridResults( bottom = if (!reverse && isLast) 8.dp else 0.dp, ) .background( - MaterialTheme.colorScheme.surface.copy(alpha = LocalTransparencyScheme.current.surface), + MaterialTheme.colorScheme.surface.copy(alpha = MaterialTheme.transparency.surface), MaterialTheme.shapes.medium.withCorners( topStart = isFirst && !reverse || isLast && reverse, topEnd = isFirst && !reverse || isLast && reverse, @@ -120,7 +120,7 @@ fun LazyListScope.GridResults( bottom = if (!reverse) 8.dp else 0.dp, ) .background( - MaterialTheme.colorScheme.surface.copy(alpha = LocalTransparencyScheme.current.surface), + MaterialTheme.colorScheme.surface.copy(alpha = MaterialTheme.transparency.surface), MaterialTheme.shapes.medium.withCorners( topStart = isTop, topEnd = isTop, diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/list/ListResults.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/list/ListResults.kt index 290184b1..b1e7751c 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/list/ListResults.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/common/list/ListResults.kt @@ -1,6 +1,5 @@ package de.mm20.launcher2.ui.launcher.search.common.list -import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.core.animateDp import androidx.compose.animation.core.animateFloat import androidx.compose.animation.core.updateTransition @@ -12,7 +11,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyItemScope import androidx.compose.foundation.lazy.LazyListScope -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable @@ -22,10 +20,9 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.shadow import androidx.compose.ui.unit.dp import de.mm20.launcher2.search.SavableSearchable -import de.mm20.launcher2.ui.ktx.animateCorners import de.mm20.launcher2.ui.ktx.animateShapeAsState import de.mm20.launcher2.ui.layout.BottomReversed -import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme +import de.mm20.launcher2.ui.theme.transparency.transparency fun LazyListScope.ListResults( key: String, @@ -101,12 +98,13 @@ fun LazyItemScope.ListItemSurface( content: @Composable ColumnScope.() -> Unit, ) { val transition = updateTransition(isExpanded) - val elevation by transition.animateDp { - if (it) 2.dp else 0.dp - } val backgroundAlpha by transition.animateFloat { - if (it) 1f else LocalTransparencyScheme.current.surface + if (it) MaterialTheme.transparency.elevatedSurface else MaterialTheme.transparency.surface } + val elevation by transition.animateDp { + if (it && backgroundAlpha == 1f) 2.dp else 0.dp + } + val padding by transition.animateDp { if (it) 8.dp else 1.dp diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavorites.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavorites.kt index e4de3036..095e6b02 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavorites.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/favorites/SearchFavorites.kt @@ -22,7 +22,7 @@ import de.mm20.launcher2.ui.common.FavoritesTagSelector import de.mm20.launcher2.ui.component.Banner import de.mm20.launcher2.ui.launcher.search.common.grid.SearchResultGrid import de.mm20.launcher2.ui.layout.BottomReversed -import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme +import de.mm20.launcher2.ui.theme.transparency.transparency fun LazyListScope.SearchFavorites( favorites: List, @@ -47,7 +47,7 @@ fun LazyListScope.SearchFavorites( ) .background( MaterialTheme.colorScheme.surface.copy( - LocalTransparencyScheme.current.surface + MaterialTheme.transparency.surface ), MaterialTheme.shapes.medium ) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt index 7148613f..b4a2d19e 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/sheets/ConfigureWidgetSheet.kt @@ -72,7 +72,7 @@ import de.mm20.launcher2.permissions.PermissionsManager import de.mm20.launcher2.plugin.PluginRepository import de.mm20.launcher2.plugin.PluginType import de.mm20.launcher2.search.calendar.CalendarListType -import de.mm20.launcher2.themes.atTone +import de.mm20.launcher2.themes.colors.atTone import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.base.LocalAppWidgetHost import de.mm20.launcher2.ui.component.BottomSheetDialog @@ -95,7 +95,6 @@ import de.mm20.launcher2.widgets.NotesWidget import de.mm20.launcher2.widgets.WeatherWidget import de.mm20.launcher2.widgets.Widget import kotlinx.coroutines.flow.map -import org.koin.androidx.compose.get import org.koin.compose.koinInject import java.time.ZonedDateTime import java.time.format.DateTimeFormatter diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetItem.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetItem.kt index 4bcf1037..4cf2a0ee 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetItem.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/WidgetItem.kt @@ -41,7 +41,7 @@ import de.mm20.launcher2.ui.launcher.widgets.favorites.FavoritesWidget import de.mm20.launcher2.ui.launcher.widgets.music.MusicWidget import de.mm20.launcher2.ui.launcher.widgets.notes.NotesWidget import de.mm20.launcher2.ui.launcher.widgets.weather.WeatherWidget -import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme +import de.mm20.launcher2.ui.theme.transparency.transparency import de.mm20.launcher2.widgets.AppWidget import de.mm20.launcher2.widgets.CalendarWidget import de.mm20.launcher2.widgets.FavoritesWidget @@ -73,7 +73,7 @@ fun WidgetItem( } else null val backgroundOpacity by animateFloatAsState( - if (widget is AppWidget && !widget.config.background && !editMode) 0f else LocalTransparencyScheme.current.surface, + if (widget is AppWidget && !widget.config.background && !editMode) 0f else MaterialTheme.transparency.surface, label = "widgetCardBackgroundOpacity", ) diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/music/MusicWidget.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/music/MusicWidget.kt index f29067c3..85a4c208 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/music/MusicWidget.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/music/MusicWidget.kt @@ -87,7 +87,7 @@ import de.mm20.launcher2.ui.ktx.conditional import de.mm20.launcher2.ui.launcher.transitions.EnterHomeTransitionParams import de.mm20.launcher2.ui.launcher.transitions.HandleEnterHomeTransition import de.mm20.launcher2.ui.locals.LocalWindowSize -import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme +import de.mm20.launcher2.ui.theme.transparency.transparency import de.mm20.launcher2.widgets.MusicWidget import kotlin.math.min @@ -352,7 +352,7 @@ fun MusicWidget(widget: MusicWidget) { ) { FilledTonalIconButton( colors = IconButtonDefaults.filledTonalIconButtonColors( - containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = LocalTransparencyScheme.current.surface), + containerColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = MaterialTheme.transparency.surface), ), onClick = { viewModel.togglePause() }, ) { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/weather/WeatherWidget.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/weather/WeatherWidget.kt index ab588516..e47b0ab7 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/weather/WeatherWidget.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/widgets/weather/WeatherWidget.kt @@ -86,7 +86,7 @@ import de.mm20.launcher2.ui.component.MissingPermissionBanner import de.mm20.launcher2.ui.component.Tooltip import de.mm20.launcher2.ui.component.weather.AnimatedWeatherIcon import de.mm20.launcher2.ui.component.weather.WeatherIcon -import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme +import de.mm20.launcher2.ui.theme.transparency.transparency import de.mm20.launcher2.weather.DailyForecast import de.mm20.launcher2.weather.Forecast import de.mm20.launcher2.widgets.WeatherWidget @@ -187,7 +187,7 @@ fun WeatherWidget(widget: WeatherWidget) { val currentDayForecasts by viewModel.currentDayForecasts Surface( - color = MaterialTheme.colorScheme.surfaceContainer.copy(alpha = LocalTransparencyScheme.current.surface), + color = MaterialTheme.colorScheme.surfaceContainer.copy(alpha = MaterialTheme.transparency.surface), modifier = Modifier.fillMaxWidth() ) { Column( @@ -299,7 +299,7 @@ fun CurrentWeather(forecast: Forecast, imperialUnits: Boolean) { topEnd = CornerSize(0), bottomEnd = CornerSize(0) ), - color = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = LocalTransparencyScheme.current.surface), + color = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = MaterialTheme.transparency.surface), ) { Text( text = "${forecast.provider} (${ diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt index c3f606ef..7ecd8347 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt @@ -80,6 +80,10 @@ import de.mm20.launcher2.ui.settings.shapes.ShapeSchemeSettingsScreen import de.mm20.launcher2.ui.settings.shapes.ShapeSchemesSettingsScreen import de.mm20.launcher2.ui.settings.tags.TagsSettingsScreen import de.mm20.launcher2.ui.settings.tasks.TasksIntegrationSettingsScreen +import de.mm20.launcher2.ui.settings.transparencies.TransparencySchemeSettingsRoute +import de.mm20.launcher2.ui.settings.transparencies.TransparencySchemeSettingsScreen +import de.mm20.launcher2.ui.settings.transparencies.TransparencySchemesSettingsRoute +import de.mm20.launcher2.ui.settings.transparencies.TransparencySchemesSettingsScreen import de.mm20.launcher2.ui.settings.unitconverter.UnitConverterHelpSettingsScreen import de.mm20.launcher2.ui.settings.unitconverter.UnitConverterSettingsScreen import de.mm20.launcher2.ui.settings.weather.WeatherIntegrationSettingsScreen @@ -206,6 +210,14 @@ class SettingsActivity : BaseActivity() { } ?: return@composable ShapeSchemeSettingsScreen(id) } + composable { + TransparencySchemesSettingsScreen() + } + composable { + val route: TransparencySchemeSettingsRoute = it.toRoute() + ?: return@composable + TransparencySchemeSettingsScreen(UUID.fromString(route.id)) + } composable("settings/appearance/cards") { CardsSettingsScreen() } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt index 50d9df93..af7a892e 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt @@ -6,6 +6,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.ArrowCircleDown import androidx.compose.material.icons.rounded.ArrowCircleUp import androidx.compose.material.icons.rounded.CropSquare +import androidx.compose.material.icons.rounded.Opacity import androidx.compose.material.icons.rounded.Palette import androidx.compose.material.icons.rounded.TextFields import androidx.compose.material3.Text @@ -27,6 +28,7 @@ import de.mm20.launcher2.ui.component.preferences.PreferenceCategory import de.mm20.launcher2.ui.component.preferences.PreferenceScreen import de.mm20.launcher2.ui.component.preferences.value import de.mm20.launcher2.ui.locals.LocalNavController +import de.mm20.launcher2.ui.settings.transparencies.TransparencySchemesSettingsRoute import de.mm20.launcher2.ui.theme.getTypography @Composable @@ -36,6 +38,7 @@ fun AppearanceSettingsScreen() { val navController = LocalNavController.current val colorThemeName by viewModel.colorThemeName.collectAsStateWithLifecycle(null) val shapeThemeName by viewModel.shapeThemeName.collectAsStateWithLifecycle(null) + val transparencyThemeName by viewModel.transparencyThemeName.collectAsStateWithLifecycle(null) val compatModeColors by viewModel.compatModeColors.collectAsState() val importLauncher = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { @@ -101,6 +104,14 @@ fun AppearanceSettingsScreen() { }, icon = Icons.Rounded.CropSquare, ) + Preference( + title = stringResource(id = R.string.preference_screen_transparencies), + summary = transparencyThemeName, + onClick = { + navController?.navigate(TransparencySchemesSettingsRoute) + }, + icon = Icons.Rounded.Opacity, + ) Preference( title = stringResource(R.string.preference_cards), diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt index 62dd764b..e0df7c2d 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt @@ -25,15 +25,22 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent { uiSettings.setColorScheme(colorScheme) } - val colorThemeName = uiSettings.colors.flatMapLatest { - themeRepository.getColorsOrDefault(it) - }.map { - it.name - } + val colorThemeName = uiSettings.colorsId.flatMapLatest { + themeRepository.colors.getOrDefault(it) + }.map { + it.name + } .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) - val shapeThemeName = uiSettings.shapes.flatMapLatest { - themeRepository.getShapesOrDefault(it) + val shapeThemeName = uiSettings.shapesId.flatMapLatest { + themeRepository.shapes.getOrDefault(it) + }.map { + it.name + } + .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), null) + + val transparencyThemeName = uiSettings.transparenciesId.flatMapLatest { + themeRepository.transparencies.getOrDefault(it) }.map { it.name } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ExportThemeSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ExportThemeSettingsScreen.kt index ad0378e0..41f4da50 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ExportThemeSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ExportThemeSettingsScreen.kt @@ -12,6 +12,7 @@ import androidx.compose.foundation.layout.size import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.CropSquare import androidx.compose.material.icons.rounded.KeyboardArrowDown +import androidx.compose.material.icons.rounded.Opacity import androidx.compose.material.icons.rounded.Palette import androidx.compose.material.icons.rounded.Save import androidx.compose.material.icons.rounded.Share @@ -50,10 +51,11 @@ fun ExportThemeSettingsScreen() { val colorSchemes by viewModel.colorSchemes.collectAsState(emptyList()) val shapeThemes by viewModel.shapeSchemes.collectAsState(emptyList()) + val transparencySchemes by viewModel.transparencySchemes.collectAsState(emptyList()) val isValidSelection by remember { derivedStateOf { - viewModel.colorScheme != null || viewModel.shapeScheme != null + viewModel.colorScheme != null || viewModel.shapeScheme != null || viewModel.transparencyScheme != null } } @@ -111,6 +113,17 @@ fun ExportThemeSettingsScreen() { viewModel.setShapeScheme(newValue) } ) + ListPreference( + stringResource(R.string.preference_screen_transparencies), + icon = Icons.Rounded.Opacity, + value = viewModel.transparencyScheme, + items = listOf(stringResource(R.string.no_selection) to null) + transparencySchemes.map { + it.name to it + }, + onValueChanged = { newValue -> + viewModel.setTransparencyScheme(newValue) + } + ) } } item { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ExportThemeSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ExportThemeSettingsScreenVM.kt index 60047ea4..ec09e069 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ExportThemeSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ExportThemeSettingsScreenVM.kt @@ -3,7 +3,6 @@ package de.mm20.launcher2.ui.settings.appearance import android.content.Context import android.content.Intent import android.net.Uri -import android.util.Log import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -11,11 +10,11 @@ import androidx.core.content.FileProvider import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.mm20.launcher2.ktx.tryStartActivity -import de.mm20.launcher2.themes.Colors -import de.mm20.launcher2.themes.Shapes +import de.mm20.launcher2.themes.colors.Colors +import de.mm20.launcher2.themes.shapes.Shapes import de.mm20.launcher2.themes.ThemeBundle import de.mm20.launcher2.themes.ThemeRepository -import de.mm20.launcher2.themes.toLegacyJson +import de.mm20.launcher2.themes.transparencies.Transparencies import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch @@ -28,8 +27,9 @@ class ExportThemeSettingsScreenVM: ViewModel(), KoinComponent { private val themeRepository: ThemeRepository by inject() - val colorSchemes = themeRepository.getAllColors().map { it.filter { !it.builtIn } } - val shapeSchemes = themeRepository.getAllShapes().map { it.filter { !it.builtIn } } + val colorSchemes = themeRepository.colors.getAll().map { it.filter { !it.builtIn } } + val shapeSchemes = themeRepository.shapes.getAll().map { it.filter { !it.builtIn } } + val transparencySchemes = themeRepository.transparencies.getAll().map { it.filter { !it.builtIn } } var themeName by mutableStateOf("") var themeAuthor by mutableStateOf("") @@ -55,12 +55,21 @@ class ExportThemeSettingsScreenVM: ViewModel(), KoinComponent { shapeScheme = scheme } + var transparencyScheme by mutableStateOf(null) + @JvmName("_setTransparencyScheme") + private set + fun setTransparencyScheme(scheme: Transparencies?) { + if (themeName.isBlank() && scheme != null) themeName = scheme.name + transparencyScheme = scheme + } + private fun getThemeBundle(): ThemeBundle { return ThemeBundle( name = themeName, author = themeAuthor.takeIf { it.isNotBlank() }, colors = colorScheme, shapes = shapeScheme, + transparencies = transparencyScheme, ) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ImportThemeSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ImportThemeSettingsScreen.kt index 550581b2..f2108b9e 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ImportThemeSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ImportThemeSettingsScreen.kt @@ -19,6 +19,7 @@ import androidx.compose.material.icons.rounded.Edit import androidx.compose.material.icons.rounded.ErrorOutline import androidx.compose.material.icons.rounded.LightMode import androidx.compose.material.icons.rounded.MoreVert +import androidx.compose.material.icons.rounded.Opacity import androidx.compose.material.icons.rounded.Palette import androidx.compose.material.icons.rounded.Star import androidx.compose.material3.Button @@ -35,6 +36,7 @@ import androidx.compose.material3.SegmentedButtonDefaults import androidx.compose.material3.SingleChoiceSegmentedButtonRow import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -62,9 +64,13 @@ import de.mm20.launcher2.ui.component.preferences.PreferenceCategory import de.mm20.launcher2.ui.component.preferences.PreferenceScreen import de.mm20.launcher2.ui.component.preferences.SwitchPreference import de.mm20.launcher2.ui.locals.LocalDarkTheme +import de.mm20.launcher2.ui.settings.transparencies.checkerboard import de.mm20.launcher2.ui.theme.colorscheme.darkColorSchemeOf import de.mm20.launcher2.ui.theme.colorscheme.lightColorSchemeOf import de.mm20.launcher2.ui.theme.shapes.shapesOf +import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme +import de.mm20.launcher2.ui.theme.transparency.transparency +import de.mm20.launcher2.ui.theme.transparency.transparencySchemeOf import kotlinx.coroutines.launch import kotlinx.serialization.Serializable @@ -118,10 +124,15 @@ fun ImportThemeSettingsScreen( } ?: MaterialTheme.colorScheme, shapes = themeBundle.shapes?.let { shapesOf(it) } ?: MaterialTheme.shapes, ) { - ThemePreview( - darkMode = darkModePreview, - onDarkModeChanged = { darkModePreview = it } - ) + val transparencies = themeBundle.transparencies?.let { transparencySchemeOf(it) } ?: MaterialTheme.transparency + CompositionLocalProvider( + LocalTransparencyScheme provides transparencies + ) { + ThemePreview( + darkMode = darkModePreview, + onDarkModeChanged = { darkModePreview = it } + ) + } } } item { @@ -156,6 +167,21 @@ fun ImportThemeSettingsScreen( } else null, ) } + if (themeBundle.transparencies != null) { + Preference( + icon = Icons.Rounded.Opacity, + title = stringResource(R.string.preference_screen_transparencies), + summary = themeBundle.transparencies?.name, + controls = if (viewModel.transparenciesExists) { + { + Icon( + Icons.Rounded.ChangeCircle, + stringResource(R.string.import_theme_exists) + ) + } + } else null, + ) + } if (viewModel.colorsExists || viewModel.shapesExists) { Banner( modifier = Modifier @@ -211,8 +237,13 @@ private fun ThemePreview( modifier = Modifier .fillMaxWidth() .clip(MaterialTheme.shapes.medium) + .checkerboard( + MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.onPrimaryContainer, + 12.dp, + ) .background( - MaterialTheme.colorScheme.surfaceContainer, + MaterialTheme.colorScheme.surfaceContainer.copy(alpha = MaterialTheme.transparency.background), MaterialTheme.shapes.medium ) .innerShadow( @@ -225,7 +256,7 @@ private fun ThemePreview( modifier = Modifier .fillMaxWidth() .padding(top = 12.dp, start = 12.dp, end = 12.dp), - level = SearchBarLevel.Active, + level = SearchBarLevel.Raised, value = "", onValueChange = {}, readOnly = true, @@ -239,7 +270,10 @@ private fun ThemePreview( modifier = Modifier .fillMaxWidth() .padding(top = 12.dp, start = 12.dp, end = 12.dp) - .background(MaterialTheme.colorScheme.surface, MaterialTheme.shapes.medium) + .background( + MaterialTheme.colorScheme.surface.copy(alpha = MaterialTheme.transparency.surface), + MaterialTheme.shapes.medium + ) .padding(12.dp) ) { Row( diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ImportThemeSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ImportThemeSettingsScreenVM.kt index e39af082..2c33cfbc 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ImportThemeSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/ImportThemeSettingsScreenVM.kt @@ -8,8 +8,6 @@ import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.mm20.launcher2.crashreporter.CrashReporter -import de.mm20.launcher2.preferences.ColorsDescriptor -import de.mm20.launcher2.preferences.ShapesDescriptor import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.themes.ThemeBundle import de.mm20.launcher2.themes.ThemeRepository @@ -19,9 +17,8 @@ import kotlinx.coroutines.flow.first import kotlinx.coroutines.launch import org.koin.core.component.KoinComponent import org.koin.core.component.inject -import kotlin.getValue -class ImportThemeSettingsScreenVM: ViewModel(), KoinComponent { +class ImportThemeSettingsScreenVM : ViewModel(), KoinComponent { private val themeRepository by inject() private val uiSettings by inject() @@ -35,6 +32,9 @@ class ImportThemeSettingsScreenVM: ViewModel(), KoinComponent { var shapesExists by mutableStateOf(false) private set + var transparenciesExists by mutableStateOf(false) + private set + var loading by mutableStateOf(false) private set @@ -54,11 +54,16 @@ class ImportThemeSettingsScreenVM: ViewModel(), KoinComponent { val text = it.readText() val theme = ThemeBundle.fromJson(text) if (theme != null) { - val colors = theme.colors?.id?.let { themeRepository.getColors(it) }?.first() - val shapes = theme.shapes?.id?.let { themeRepository.getShapes(it) }?.first() + val colors = + theme.colors?.id?.let { themeRepository.colors.get(it) }?.first() + val shapes = + theme.shapes?.id?.let { themeRepository.shapes.get(it) }?.first() + val transparencies = + theme.transparencies?.id?.let { themeRepository.transparencies.get(it) }?.first() colorsExists = colors != null shapesExists = shapes != null + transparenciesExists = transparencies != null themeBundle = theme loading = false } else { @@ -77,30 +82,42 @@ class ImportThemeSettingsScreenVM: ViewModel(), KoinComponent { val colors = themeBundle.colors val shapes = themeBundle.shapes + val transparencies = themeBundle.transparencies val colorsExist = this.colorsExists val shapesExist = this.shapesExists + val transparenciesExist = this.transparenciesExists loading = true return viewModelScope.launch { if (colors != null) { if (colorsExist) { - themeRepository.updateColors(colors) + themeRepository.colors.update(colors) } else { - themeRepository.createColors(colors) + themeRepository.colors.create(colors) } if (applyTheme) { - uiSettings.setColors(ColorsDescriptor.Custom(colors.id.toString())) + uiSettings.setColorsId(colors.id) } } if (shapes != null) { if (shapesExist) { - themeRepository.updateShapes(shapes) + themeRepository.shapes.update(shapes) } else { - themeRepository.createShapes(shapes) + themeRepository.shapes.create(shapes) } if (applyTheme) { - uiSettings.setShapes(ShapesDescriptor.Custom(shapes.id.toString())) + uiSettings.setShapesId(shapes.id) + } + } + if (transparencies != null) { + if (transparenciesExist) { + themeRepository.transparencies.update(transparencies) + } else { + themeRepository.transparencies.create(transparencies) + } + if (applyTheme) { + uiSettings.setTransparenciesId(transparencies.id) } } loading = false diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/calendarsearch/CalendarProviderSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/calendarsearch/CalendarProviderSettingsScreen.kt index 4f2b5ade..5154cfc5 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/calendarsearch/CalendarProviderSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/calendarsearch/CalendarProviderSettingsScreen.kt @@ -1,32 +1,19 @@ package de.mm20.launcher2.ui.settings.calendarsearch -import android.app.PendingIntent -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.items -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.ErrorOutline import androidx.compose.material3.CheckboxDefaults import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue -import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import de.mm20.launcher2.calendar.providers.CalendarList -import de.mm20.launcher2.crashreporter.CrashReporter -import de.mm20.launcher2.ktx.sendWithBackgroundPermission import de.mm20.launcher2.plugin.PluginState -import de.mm20.launcher2.themes.atTone +import de.mm20.launcher2.themes.colors.atTone import de.mm20.launcher2.ui.R -import de.mm20.launcher2.ui.component.Banner import de.mm20.launcher2.ui.component.preferences.CheckboxPreference import de.mm20.launcher2.ui.component.preferences.PreferenceCategory import de.mm20.launcher2.ui.component.preferences.PreferenceScreen diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreen.kt index 30b2db06..4695b271 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreen.kt @@ -47,16 +47,6 @@ fun CardsSettingsScreen() { } item { PreferenceCategory { - SliderPreference( - title = stringResource(R.string.preference_cards_opacity), - icon = Icons.Rounded.Opacity, - value = cardStyle.opacity, - min = 0f, - max = 1f, - onValueChanged = { - viewModel.setOpacity(it) - } - ) SliderPreference( title = stringResource(R.string.preference_cards_stroke_width), icon = Icons.Rounded.LineWeight, diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemeSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemeSettingsScreen.kt index 642e6bec..9c56c375 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemeSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemeSettingsScreen.kt @@ -28,7 +28,6 @@ import androidx.compose.material3.FilledTonalIconButton import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChipDefaults import androidx.compose.material3.FloatingActionButton -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -51,7 +50,6 @@ 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.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.core.content.ContextCompat import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -60,9 +58,9 @@ import de.mm20.launcher2.badges.Badge import de.mm20.launcher2.icons.ColorLayer import de.mm20.launcher2.icons.StaticLauncherIcon import de.mm20.launcher2.icons.TintedIconLayer -import de.mm20.launcher2.themes.DefaultDarkColorScheme -import de.mm20.launcher2.themes.DefaultLightColorScheme -import de.mm20.launcher2.themes.merge +import de.mm20.launcher2.themes.colors.DefaultDarkColorScheme +import de.mm20.launcher2.themes.colors.DefaultLightColorScheme +import de.mm20.launcher2.themes.colors.merge import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.Banner import de.mm20.launcher2.ui.component.ShapedLauncherIcon diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreen.kt index 743365a4..3a55ef8a 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreen.kt @@ -1,8 +1,5 @@ package de.mm20.launcher2.ui.settings.colorscheme -import android.net.Uri -import androidx.activity.compose.rememberLauncherForActivityResult -import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.background import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box @@ -17,7 +14,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.rounded.Add import androidx.compose.material.icons.rounded.ContentCopy import androidx.compose.material.icons.rounded.Delete -import androidx.compose.material.icons.rounded.Download import androidx.compose.material.icons.rounded.Edit import androidx.compose.material.icons.rounded.MoreVert import androidx.compose.material.icons.rounded.RadioButtonChecked @@ -26,7 +22,6 @@ import androidx.compose.material.icons.rounded.Share import androidx.compose.material3.AlertDialog import androidx.compose.material3.DropdownMenu import androidx.compose.material3.DropdownMenuItem -import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme @@ -45,9 +40,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel -import de.mm20.launcher2.themes.Colors +import de.mm20.launcher2.themes.colors.Colors import de.mm20.launcher2.ui.R -import de.mm20.launcher2.ui.common.ImportThemeSheet import de.mm20.launcher2.ui.component.preferences.Preference import de.mm20.launcher2.ui.component.preferences.PreferenceCategory import de.mm20.launcher2.ui.component.preferences.PreferenceScreen diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreenVM.kt index 0a59622b..66300cfe 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreenVM.kt @@ -6,11 +6,10 @@ import androidx.core.content.FileProvider import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import de.mm20.launcher2.ktx.tryStartActivity -import de.mm20.launcher2.preferences.ColorsDescriptor import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.themes.BlackAndWhiteThemeId import de.mm20.launcher2.themes.DefaultThemeId -import de.mm20.launcher2.themes.Colors +import de.mm20.launcher2.themes.colors.Colors import de.mm20.launcher2.themes.HighContrastThemeId import de.mm20.launcher2.themes.ThemeRepository import de.mm20.launcher2.themes.toLegacyJson @@ -30,39 +29,27 @@ class ColorSchemesSettingsScreenVM : ViewModel(), KoinComponent { private val themeRepository: ThemeRepository by inject() private val uiSettings: UiSettings by inject() - val selectedColors = uiSettings.colors.map { - when(it) { - ColorsDescriptor.Default -> DefaultThemeId - ColorsDescriptor.HighContrast -> HighContrastThemeId - ColorsDescriptor.BlackAndWhite -> BlackAndWhiteThemeId - is ColorsDescriptor.Custom -> UUID.fromString(it.id) - } - } - val colors: Flow> = themeRepository.getAllColors() + val selectedColors = uiSettings.colorsId + val colors: Flow> = themeRepository.colors.getAll() fun getTheme(id: UUID): Flow { - return themeRepository.getColors(id) + return themeRepository.colors.get(id) } fun updateTheme(colors: Colors) { - themeRepository.updateColors(colors) + themeRepository.colors.update(colors) } fun selectTheme(colors: Colors) { - uiSettings.setColors(when(colors.id) { - DefaultThemeId -> ColorsDescriptor.Default - HighContrastThemeId -> ColorsDescriptor.HighContrast - BlackAndWhiteThemeId -> ColorsDescriptor.BlackAndWhite - else -> ColorsDescriptor.Custom(colors.id.toString()) - }) + uiSettings.setColorsId(colors.id) } fun duplicate(colors: Colors) { - themeRepository.createColors(colors.copy(id = UUID.randomUUID())) + themeRepository.colors.create(colors.copy(id = UUID.randomUUID())) } fun delete(colors: Colors) { - themeRepository.deleteColors(colors) + themeRepository.colors.delete(colors) } fun exportTheme(context: Context, colors: Colors) { @@ -85,10 +72,10 @@ class ColorSchemesSettingsScreenVM : ViewModel(), KoinComponent { } fun createNew(context: Context) { - themeRepository.createColors( + themeRepository.colors.create( Colors( id = UUID.randomUUID(), - name = context.getString(R.string.new_color_scheme_name) + name = context.getString(R.string.new_theme_name) ) ) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/CorePaletteColorPreference.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/CorePaletteColorPreference.kt index 33fa1e05..0d4bdac7 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/CorePaletteColorPreference.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/CorePaletteColorPreference.kt @@ -5,7 +5,6 @@ import androidx.compose.animation.expandVertically import androidx.compose.animation.shrinkVertically import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth @@ -33,14 +32,11 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.res.stringResource -import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import de.mm20.launcher2.themes.get import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.BottomSheetDialog -import de.mm20.launcher2.ui.component.Tooltip import de.mm20.launcher2.ui.component.colorpicker.HctColorPicker import de.mm20.launcher2.ui.component.colorpicker.rememberHctColorPickerState import de.mm20.launcher2.ui.component.preferences.SwitchPreference diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeColorPreference.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeColorPreference.kt index 4bdd2f1e..62f23dbe 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeColorPreference.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeColorPreference.kt @@ -4,7 +4,6 @@ import androidx.compose.animation.AnimatedContent import androidx.compose.foundation.Canvas import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row @@ -12,7 +11,6 @@ import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.requiredWidth import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState @@ -51,21 +49,20 @@ import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp -import de.mm20.launcher2.themes.ColorRef -import de.mm20.launcher2.themes.CorePaletteColor -import de.mm20.launcher2.themes.FullCorePalette -import de.mm20.launcher2.themes.StaticColor -import de.mm20.launcher2.themes.atTone -import de.mm20.launcher2.themes.get +import de.mm20.launcher2.themes.colors.ColorRef +import de.mm20.launcher2.themes.colors.CorePaletteColor +import de.mm20.launcher2.themes.colors.FullCorePalette +import de.mm20.launcher2.themes.colors.StaticColor +import de.mm20.launcher2.themes.colors.atTone +import de.mm20.launcher2.themes.colors.get import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.BottomSheetDialog -import de.mm20.launcher2.ui.component.Tooltip import de.mm20.launcher2.ui.component.colorpicker.HctColorPicker import de.mm20.launcher2.ui.component.colorpicker.rememberHctColorPickerState import de.mm20.launcher2.ui.ktx.hct import hct.Hct import kotlin.math.roundToInt -import de.mm20.launcher2.themes.Color as ThemeColor +import de.mm20.launcher2.themes.colors.Color as ThemeColor @Composable fun ThemeColorPreference( diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreen.kt index d0124caa..481444ea 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/plugins/PluginSettingsScreen.kt @@ -14,8 +14,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.items import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons @@ -26,11 +24,9 @@ import androidx.compose.material.icons.rounded.Error import androidx.compose.material.icons.rounded.Info import androidx.compose.material.icons.rounded.Settings import androidx.compose.material.icons.rounded.Verified -import androidx.compose.material3.CheckboxDefaults import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.ModalBottomSheet import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text @@ -40,12 +36,9 @@ 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.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.pluralStringResource import androidx.compose.ui.res.stringResource @@ -57,15 +50,12 @@ import coil.compose.AsyncImage import de.mm20.launcher2.crashreporter.CrashReporter import de.mm20.launcher2.ktx.sendWithBackgroundPermission import de.mm20.launcher2.plugin.PluginState -import de.mm20.launcher2.themes.atTone import de.mm20.launcher2.ui.R -import de.mm20.launcher2.ui.component.preferences.CheckboxPreference import de.mm20.launcher2.ui.component.preferences.GuardedPreference import de.mm20.launcher2.ui.component.preferences.Preference import de.mm20.launcher2.ui.component.preferences.PreferenceCategory import de.mm20.launcher2.ui.component.preferences.PreferenceWithSwitch import de.mm20.launcher2.ui.component.preferences.SwitchPreference -import de.mm20.launcher2.ui.locals.LocalDarkTheme import de.mm20.launcher2.ui.locals.LocalNavController @Composable diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemeSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemeSettingsScreen.kt index ef24516b..ed7a6535 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemeSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemeSettingsScreen.kt @@ -66,7 +66,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import de.mm20.launcher2.icons.CutCorner import de.mm20.launcher2.icons.RoundedCornerAlt -import de.mm20.launcher2.themes.CornerStyle +import de.mm20.launcher2.themes.shapes.CornerStyle import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.BottomSheetDialog import de.mm20.launcher2.ui.component.preferences.PreferenceCategory @@ -76,7 +76,7 @@ import de.mm20.launcher2.ui.theme.shapes.shapesOf import java.util.UUID import kotlin.math.max import kotlin.math.min -import de.mm20.launcher2.themes.Shape as ThemeShape +import de.mm20.launcher2.themes.shapes.Shape as ThemeShape @Composable fun ShapeSchemeSettingsScreen(themeId: UUID) { @@ -284,7 +284,7 @@ fun ShapeSchemeSettingsScreen(themeId: UUID) { } } item { - PreferenceCategory(title = "Large") { + PreferenceCategory { ShapePreview( previewShapes = previewShapes, ) { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreen.kt index 97ca9b0c..13eb13c4 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreen.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreen.kt @@ -35,8 +35,8 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel -import de.mm20.launcher2.themes.CornerStyle -import de.mm20.launcher2.themes.Shapes +import de.mm20.launcher2.themes.shapes.CornerStyle +import de.mm20.launcher2.themes.shapes.Shapes import de.mm20.launcher2.ui.R import de.mm20.launcher2.ui.component.preferences.Preference import de.mm20.launcher2.ui.component.preferences.PreferenceCategory @@ -49,7 +49,7 @@ fun ShapeSchemesSettingsScreen() { val navController = LocalNavController.current val context = LocalContext.current - val selectedTheme by viewModel.selectedShapes.collectAsStateWithLifecycle(null) + val selectedTheme by viewModel.selectedShapesId.collectAsStateWithLifecycle(null) val themes by viewModel.shapes.collectAsStateWithLifecycle(emptyList()) var deleteShapes by remember { mutableStateOf(null) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreenVM.kt index 1f5f8bc2..27e39ce0 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreenVM.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreenVM.kt @@ -2,17 +2,11 @@ package de.mm20.launcher2.ui.settings.shapes import android.content.Context import androidx.lifecycle.ViewModel -import de.mm20.launcher2.preferences.ShapesDescriptor import de.mm20.launcher2.preferences.ui.UiSettings -import de.mm20.launcher2.themes.CutShapesId -import de.mm20.launcher2.themes.DefaultThemeId -import de.mm20.launcher2.themes.ExtraRoundShapesId -import de.mm20.launcher2.themes.RectShapesId -import de.mm20.launcher2.themes.Shapes +import de.mm20.launcher2.themes.shapes.Shapes import de.mm20.launcher2.themes.ThemeRepository import de.mm20.launcher2.ui.R import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.map import org.koin.core.component.KoinComponent import org.koin.core.component.inject import java.util.UUID @@ -23,45 +17,34 @@ class ShapeSchemesSettingsScreenVM : ViewModel(), KoinComponent { private val themeRepository: ThemeRepository by inject() private val uiSettings: UiSettings by inject() - val selectedShapes = uiSettings.shapes.map { - when(it) { - ShapesDescriptor.Default -> DefaultThemeId - ShapesDescriptor.Cut -> CutShapesId - ShapesDescriptor.ExtraRound -> ExtraRoundShapesId - ShapesDescriptor.Rect -> RectShapesId - is ShapesDescriptor.Custom -> UUID.fromString(it.id) - } - } - val shapes: Flow> = themeRepository.getAllShapes() + val selectedShapesId = uiSettings.shapesId + val shapes: Flow> = themeRepository.shapes.getAll() fun getShapes(id: UUID): Flow { - return themeRepository.getShapes(id) + return themeRepository.shapes.get(id) } fun updateShapes(shapes: Shapes) { - themeRepository.updateShapes(shapes) + themeRepository.shapes.update(shapes) } fun selectShapes(shapes: Shapes) { - uiSettings.setShapes(when(shapes.id) { - DefaultThemeId -> ShapesDescriptor.Default - else -> ShapesDescriptor.Custom(shapes.id.toString()) - }) + uiSettings.setShapesId(shapes.id) } fun duplicate(shapes: Shapes) { - themeRepository.createShapes(shapes.copy(id = UUID.randomUUID())) + themeRepository.shapes.create(shapes.copy(id = UUID.randomUUID())) } fun delete(shapes: Shapes) { - themeRepository.deleteShapes(shapes) + themeRepository.shapes.delete(shapes) } fun createNew(context: Context) { - themeRepository.createShapes( + themeRepository.shapes.create( Shapes( id = UUID.randomUUID(), - name = context.getString(R.string.new_shapes_name) + name = context.getString(R.string.new_theme_name) ) ) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/Checkerboard.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/Checkerboard.kt new file mode 100644 index 00000000..83b1d748 --- /dev/null +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/Checkerboard.kt @@ -0,0 +1,36 @@ +package de.mm20.launcher2.ui.settings.transparencies + +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawBehind +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import de.mm20.launcher2.ktx.ceilToInt + +fun Modifier.checkerboard( + color1: Color = Color.LightGray, + color2: Color = Color.DarkGray, + tileSize: Dp = 16.dp +): Modifier { + return drawBehind { + val tileSizePx = with(density) { tileSize.toPx() } + val hTiles = (size.width / tileSizePx).ceilToInt() + val vTiles = (size.height / tileSizePx).ceilToInt() + + for (i in 0 until hTiles) { + for (j in 0 until vTiles) { + val color = if ((i + j) % 2 == 0) color1 else color2 + drawRect( + color = color, + topLeft = Offset( + x = i * tileSizePx, + y = j * tileSizePx + ), + size = Size(tileSizePx + 1, tileSizePx + 1) + ) + } + } + } +} \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/TransparencySchemeSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/TransparencySchemeSettingsScreen.kt new file mode 100644 index 00000000..e77f7e6b --- /dev/null +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/TransparencySchemeSettingsScreen.kt @@ -0,0 +1,304 @@ +package de.mm20.launcher2.ui.settings.transparencies + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +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.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.requiredWidth +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.CornerSize +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.RestartAlt +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.Button +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalAbsoluteTonalElevation +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Slider +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontFamily +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import de.mm20.launcher2.ktx.isAtLeastApiLevel +import de.mm20.launcher2.themes.transparencies.Transparencies +import de.mm20.launcher2.ui.R +import de.mm20.launcher2.ui.component.LocalIconShape +import de.mm20.launcher2.ui.component.preferences.PreferenceCategory +import de.mm20.launcher2.ui.component.preferences.PreferenceScreen +import de.mm20.launcher2.ui.component.surfaceColorAtElevation +import de.mm20.launcher2.ui.locals.LocalGridSettings +import de.mm20.launcher2.ui.theme.transparency.transparencySchemeOf +import de.mm20.launcher2.ui.theme.wallpaperColorsAsState +import kotlinx.serialization.Serializable +import java.text.DecimalFormat +import java.util.UUID +import kotlin.math.floor +import kotlin.math.log +import kotlin.math.round + +@Serializable +data class TransparencySchemeSettingsRoute( + val id: String +) + +@Composable +fun TransparencySchemeSettingsScreen(themeId: UUID) { + val viewModel: TransparencySchemesSettingsScreenVM = viewModel() + + val context = LocalContext.current + + val wallpaperColors by wallpaperColorsAsState() + + val theme by remember( + viewModel, + themeId + ) { viewModel.getTransparencies(themeId) }.collectAsStateWithLifecycle(null) + + + var editName by remember { mutableStateOf(false) } + + if (editName) { + var name by remember(theme) { mutableStateOf(theme?.name ?: "") } + AlertDialog( + onDismissRequest = { editName = false }, + text = { + OutlinedTextField( + value = name, + onValueChange = { name = it }, + singleLine = true + ) + }, + confirmButton = { + Button( + onClick = { + viewModel.updateTransparencies(theme!!.copy(name = name)) + editName = false + } + ) { + Text(stringResource(R.string.save)) + } + } + ) + } + + PreferenceScreen( + title = { + Text( + theme?.name ?: "", + modifier = Modifier.clickable { + editName = true + }, + ) + }, + helpUrl = "https://kvaesitso.mm20.de/docs/user-guide/customization/color-schemes", + ) { + if (theme == null) return@PreferenceScreen + + item { + PreferenceCategory { + TransparenciesPreview(theme!!) + TransparencyPreference( + title = "Background", + value = theme!!.background, + defaultValue = 0.85f, + onValueChange = { viewModel.updateTransparencies(theme!!.copy(background = it)) } + ) + TransparencyPreference( + title = "Surface", + value = theme!!.surface, + defaultValue = 1f, + onValueChange = { viewModel.updateTransparencies(theme!!.copy(surface = it)) } + ) + TransparencyPreference( + title = "Elevated Surface", + value = theme!!.elevatedSurface, + defaultValue = 1f, + onValueChange = { viewModel.updateTransparencies(theme!!.copy(elevatedSurface = it)) } + ) + } + } + } +} + +@Composable +private fun TransparenciesPreview( + theme: Transparencies, +) { + val transparencyScheme = transparencySchemeOf(theme) + + Box { + Box( + modifier = Modifier + .fillMaxWidth() + .height(200.dp) + .clip(MaterialTheme.shapes.extraSmall) + .checkerboard( + MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.onPrimaryContainer, + 12.dp, + ) + ) { + Box( + modifier = Modifier + .align(Alignment.BottomCenter) + .fillMaxWidth() + .height(160.dp) + .background( + MaterialTheme.colorScheme.surfaceContainer.copy(transparencyScheme.background), + MaterialTheme.shapes.large.copy( + bottomStart = CornerSize(0f), + bottomEnd = CornerSize(0f), + ) + ), + ) { + val xs = MaterialTheme.shapes.extraSmall + Row( + modifier = Modifier + .fillMaxSize() + .padding(top = 48.dp, start = 12.dp, end = 12.dp, bottom = 2.dp) + .background( + MaterialTheme.colorScheme.surfaceContainer.copy(transparencyScheme.surface), + MaterialTheme.shapes.medium.copy( + bottomStart = xs.bottomStart, + bottomEnd = xs.bottomStart, + ), + ) + .padding(12.dp), + horizontalArrangement = Arrangement.spacedBy(4.dp) + ) { + repeat(LocalGridSettings.current.columnCount) { + Column( + modifier = Modifier + .weight(1f) + .padding(vertical = 12.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + Box( + modifier = Modifier + .size(LocalGridSettings.current.iconSize.dp) + .background( + MaterialTheme.colorScheme.primaryContainer, + LocalIconShape.current, + ) + ) + } + } + } + } + } + val elevatedSurface = MaterialTheme.colorScheme + .surfaceColorAtElevation(8.dp + LocalAbsoluteTonalElevation.current) + .copy(alpha = transparencyScheme.elevatedSurface) + Box( + modifier = Modifier + .padding(end = 24.dp) + .shadow( + if (transparencyScheme.elevatedSurface < 1f) 0.dp else 8.dp, + MaterialTheme.shapes.medium, + clip = true + ) + .background(elevatedSurface) + .align(Alignment.CenterEnd) + .size(144.dp), + ) + } +} + +@Composable +private fun TransparencyPreference( + title: String, + value: Float?, + defaultValue: Float, + onValueChange: (Float?) -> Unit, +) { + Row( + modifier = Modifier + .fillMaxWidth() + .clip(MaterialTheme.shapes.extraSmall) + .background(MaterialTheme.colorScheme.surface) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + Box( + modifier = Modifier + .padding(end = 20.dp) + .size(48.dp) + .clip(MaterialTheme.shapes.small) + .checkerboard( + MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.onPrimaryContainer, + 12.dp, + ) + .padding(8.dp) + .background( + MaterialTheme.colorScheme.surface.copy(alpha = value ?: defaultValue), + MaterialTheme.shapes.extraSmall + ), + contentAlignment = Alignment.Center + ) { + Box( + modifier = Modifier + .size(48.dp) + ) + } + + Column( + modifier = Modifier.weight(1f) + ) { + Text( + title, + style = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace), + textAlign = TextAlign.Center, + overflow = TextOverflow.Ellipsis, + maxLines = 1, + ) + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + Slider( + modifier = Modifier.weight(1f), + value = value ?: defaultValue, + onValueChange = { + val roundedValue = round(it * 100) / 100 + onValueChange(roundedValue) + }, + valueRange = 0f..1f, + ) + val format = remember { DecimalFormat().apply { + maximumFractionDigits = 2 + minimumFractionDigits = 0 + } } + Text( + text = format.format(value ?: defaultValue), + style = MaterialTheme.typography.labelSmall, + textAlign = TextAlign.Center, + modifier = Modifier.padding(start = 8.dp).widthIn(min = 48.dp), + ) + } + } + } +} \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/TransparencySchemesSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/TransparencySchemesSettingsScreen.kt new file mode 100644 index 00000000..41865e06 --- /dev/null +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/TransparencySchemesSettingsScreen.kt @@ -0,0 +1,218 @@ +package de.mm20.launcher2.ui.settings.transparencies + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Add +import androidx.compose.material.icons.rounded.ContentCopy +import androidx.compose.material.icons.rounded.Delete +import androidx.compose.material.icons.rounded.Edit +import androidx.compose.material.icons.rounded.MoreVert +import androidx.compose.material.icons.rounded.RadioButtonChecked +import androidx.compose.material.icons.rounded.RadioButtonUnchecked +import androidx.compose.material3.AlertDialog +import androidx.compose.material3.DropdownMenu +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.material3.TextButton +import androidx.compose.material3.surfaceColorAtElevation +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import androidx.lifecycle.viewmodel.compose.viewModel +import de.mm20.launcher2.themes.transparencies.Transparencies +import de.mm20.launcher2.ui.R +import de.mm20.launcher2.ui.component.preferences.Preference +import de.mm20.launcher2.ui.component.preferences.PreferenceCategory +import de.mm20.launcher2.ui.component.preferences.PreferenceScreen +import de.mm20.launcher2.ui.locals.LocalNavController +import de.mm20.launcher2.ui.theme.WallpaperColors +import de.mm20.launcher2.ui.theme.transparency.transparencySchemeOf +import de.mm20.launcher2.ui.theme.wallpaperColorsAsState +import kotlinx.serialization.Serializable + +@Serializable +data object TransparencySchemesSettingsRoute + +@Composable +fun TransparencySchemesSettingsScreen() { + val viewModel: TransparencySchemesSettingsScreenVM = viewModel() + val navController = LocalNavController.current + val context = LocalContext.current + + val selectedTheme by viewModel.selectedTransparencies.collectAsStateWithLifecycle(null) + val themes by viewModel.transparencies.collectAsStateWithLifecycle(emptyList()) + + var deleteTransparencies by remember { mutableStateOf(null) } + + val wallpaperColors = wallpaperColorsAsState().value + + PreferenceScreen( + title = stringResource(R.string.preference_screen_transparencies), + topBarActions = { + IconButton(onClick = { viewModel.createNew(context) }) { + Icon(Icons.Rounded.Add, null) + } + }, + ) { + item { + PreferenceCategory { + for (theme in themes) { + var showMenu by remember { mutableStateOf(false) } + Preference( + icon = if (theme.id == selectedTheme) Icons.Rounded.RadioButtonChecked else Icons.Rounded.RadioButtonUnchecked, + title = theme.name, + controls = { + Row( + verticalAlignment = Alignment.CenterVertically, + ) { + TransparenciesPreview(wallpaperColors, theme) + IconButton( + modifier = Modifier.padding(start = 12.dp), + onClick = { showMenu = true }) { + Icon(Icons.Rounded.MoreVert, null) + } + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) { + if (!theme.builtIn) { + DropdownMenuItem( + leadingIcon = { + Icon(Icons.Rounded.Edit, null) + }, + text = { Text(stringResource(R.string.edit)) }, + onClick = { + navController?.navigate( + TransparencySchemeSettingsRoute(theme.id.toString()) + ) + showMenu = false + } + ) + } + DropdownMenuItem( + leadingIcon = { + Icon(Icons.Rounded.ContentCopy, null) + }, + text = { Text(stringResource(R.string.duplicate)) }, + onClick = { + viewModel.duplicate(theme) + showMenu = false + } + ) + if (!theme.builtIn) { + DropdownMenuItem( + leadingIcon = { + Icon(Icons.Rounded.Delete, null) + }, + text = { Text(stringResource(R.string.menu_delete)) }, + onClick = { + deleteTransparencies = theme + showMenu = false + } + ) + } + } + } + }, + onClick = { + viewModel.selectTransparencies(theme) + } + ) + } + } + } + } + if (deleteTransparencies != null) { + AlertDialog( + onDismissRequest = { deleteTransparencies = null }, + text = { + Text( + stringResource( + R.string.confirmation_delete_transparencies_scheme, + deleteTransparencies!!.name + ) + ) + }, + confirmButton = { + TextButton( + onClick = { + viewModel.delete(deleteTransparencies!!) + deleteTransparencies = null + } + ) { + Text(stringResource(android.R.string.ok)) + } + }, + dismissButton = { + TextButton( + onClick = { deleteTransparencies = null } + ) { + Text(stringResource(android.R.string.cancel)) + } + } + ) + } +} + +@Composable +private fun TransparenciesPreview(wallpaperColors: WallpaperColors, theme: Transparencies) { + val transparencies = transparencySchemeOf(theme) + + Box( + modifier = Modifier + .clip(MaterialTheme.shapes.extraSmall) + .checkerboard( + MaterialTheme.colorScheme.primary, + MaterialTheme.colorScheme.onPrimaryContainer, + 12.dp, + ) + .height(40.dp) + .width(56.dp), + contentAlignment = Alignment.Center + ) { + Box( + modifier = Modifier + .background(MaterialTheme.colorScheme.surfaceContainer.copy(alpha = transparencies.background), MaterialTheme.shapes.extraSmall) + .height(40.dp) + .width(56.dp) + ) + Box( + modifier = Modifier + .background(MaterialTheme.colorScheme.surface.copy(alpha = transparencies.surface), MaterialTheme.shapes.extraSmall) + .height(24.dp) + .width(48.dp) + ) + Box( + modifier = Modifier + .height(32.dp) + .width(36.dp) + .shadow( + if (transparencies.elevatedSurface < 1f) 0.dp else 8.dp, + shape = MaterialTheme.shapes.extraSmall, + clip = true, + ) + .background(MaterialTheme.colorScheme.surfaceColorAtElevation(8.dp)) + ) + } +} \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/TransparencySchemesSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/TransparencySchemesSettingsScreenVM.kt new file mode 100644 index 00000000..34459d2c --- /dev/null +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/transparencies/TransparencySchemesSettingsScreenVM.kt @@ -0,0 +1,50 @@ +package de.mm20.launcher2.ui.settings.transparencies + +import android.content.Context +import androidx.lifecycle.ViewModel +import de.mm20.launcher2.preferences.ui.UiSettings +import de.mm20.launcher2.themes.ThemeRepository +import de.mm20.launcher2.themes.transparencies.Transparencies +import de.mm20.launcher2.ui.R +import kotlinx.coroutines.flow.Flow +import org.koin.core.component.KoinComponent +import org.koin.core.component.inject +import java.util.UUID + +class TransparencySchemesSettingsScreenVM : ViewModel(), KoinComponent { + + private val themeRepository: ThemeRepository by inject() + private val uiSettings: UiSettings by inject() + + val selectedTransparencies = uiSettings.transparenciesId + val transparencies: Flow> = themeRepository.transparencies.getAll() + + fun getTransparencies(id: UUID): Flow { + return themeRepository.transparencies.get(id) + } + + fun updateTransparencies(transparencies: Transparencies) { + themeRepository.transparencies.update(transparencies) + } + + fun selectTransparencies(transparencies: Transparencies) { + uiSettings.setTransparenciesId(transparencies.id) + } + + fun duplicate(transparencies: Transparencies) { + themeRepository.transparencies.create(transparencies.copy(id = UUID.randomUUID())) + } + + fun delete(transparencies: Transparencies) { + themeRepository.transparencies.delete(transparencies) + } + + fun createNew(context: Context) { + themeRepository.transparencies.create( + Transparencies( + id = UUID.randomUUID(), + name = context.getString(R.string.new_theme_name) + ) + ) + } +} \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/LauncherTheme.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/LauncherTheme.kt index 9cee27e7..b991666e 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/LauncherTheme.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/LauncherTheme.kt @@ -2,25 +2,20 @@ package de.mm20.launcher2.ui.theme import android.content.Context import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.foundation.shape.CornerSize -import androidx.compose.foundation.shape.CutCornerShape -import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.* import androidx.compose.runtime.* import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.unit.coerceAtMost -import androidx.compose.ui.unit.dp import de.mm20.launcher2.preferences.Font -import de.mm20.launcher2.preferences.SurfaceShape import de.mm20.launcher2.preferences.ui.UiSettings import de.mm20.launcher2.themes.ThemeRepository import de.mm20.launcher2.ui.locals.LocalDarkTheme import de.mm20.launcher2.ui.theme.colorscheme.* import de.mm20.launcher2.ui.theme.shapes.shapesOf +import de.mm20.launcher2.ui.theme.transparency.LocalTransparencyScheme +import de.mm20.launcher2.ui.theme.transparency.transparencySchemeOf import de.mm20.launcher2.ui.theme.typography.DefaultTypography import de.mm20.launcher2.ui.theme.typography.getDeviceDefaultTypography import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.map import org.koin.compose.koinInject import de.mm20.launcher2.preferences.ColorScheme as ColorSchemePref @@ -35,14 +30,20 @@ fun LauncherTheme( val themeRepository: ThemeRepository = koinInject() val themeColors by remember { - uiSettings.colors.flatMapLatest { - themeRepository.getColorsOrDefault(it) + uiSettings.colorsId.flatMapLatest { + themeRepository.colors.getOrDefault(it) } }.collectAsState(null) val themeShapes by remember { - uiSettings.shapes.flatMapLatest { - themeRepository.getShapesOrDefault(it) + uiSettings.shapesId.flatMapLatest { + themeRepository.shapes.getOrDefault(it) + } + }.collectAsState(null) + + val themeTransparencies by remember { + uiSettings.transparenciesId.flatMapLatest { + themeRepository.transparencies.getOrDefault(it) } }.collectAsState(null) @@ -52,7 +53,7 @@ fun LauncherTheme( val darkTheme = colorSchemePref == ColorSchemePref.Dark || colorSchemePref == ColorSchemePref.System && isSystemInDarkTheme() - if (themeColors == null || themeShapes == null) { + if (themeColors == null || themeShapes == null || themeTransparencies == null) { return } @@ -64,6 +65,7 @@ fun LauncherTheme( val shapes = shapesOf(themeShapes!!) + val transparencyScheme = transparencySchemeOf(themeTransparencies!!) val font by remember { uiSettings.font }.collectAsState( @@ -75,7 +77,8 @@ fun LauncherTheme( } CompositionLocalProvider( - LocalDarkTheme provides darkTheme + LocalDarkTheme provides darkTheme, + LocalTransparencyScheme provides transparencyScheme, ) { MaterialExpressiveTheme( colorScheme = colorScheme, diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/ColorScheme.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/ColorScheme.kt index bd0b3c25..e163338b 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/ColorScheme.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/ColorScheme.kt @@ -1,5 +1,6 @@ package de.mm20.launcher2.ui.theme.colorscheme +import android.R import android.os.Build import androidx.compose.material3.ColorScheme import androidx.compose.runtime.Composable @@ -11,14 +12,14 @@ import androidx.compose.ui.graphics.toArgb import androidx.compose.ui.platform.LocalContext import androidx.core.content.ContextCompat import de.mm20.launcher2.preferences.ui.UiSettings -import de.mm20.launcher2.themes.CorePalette -import de.mm20.launcher2.themes.DefaultDarkColorScheme -import de.mm20.launcher2.themes.DefaultLightColorScheme -import de.mm20.launcher2.themes.FullColorScheme -import de.mm20.launcher2.themes.PartialCorePalette -import de.mm20.launcher2.themes.Colors as ThemeColors -import de.mm20.launcher2.themes.get -import de.mm20.launcher2.themes.merge +import de.mm20.launcher2.themes.colors.CorePalette +import de.mm20.launcher2.themes.colors.DefaultDarkColorScheme +import de.mm20.launcher2.themes.colors.DefaultLightColorScheme +import de.mm20.launcher2.themes.colors.FullColorScheme +import de.mm20.launcher2.themes.colors.PartialCorePalette +import de.mm20.launcher2.themes.colors.Colors as ThemeColors +import de.mm20.launcher2.themes.colors.get +import de.mm20.launcher2.themes.colors.merge import de.mm20.launcher2.ui.locals.LocalWallpaperColors import org.koin.compose.koinInject @@ -88,11 +89,11 @@ fun systemCorePalette(): CorePalette { if (Build.VERSION.SDK_INT >= 31 && !compatModeColors) { val context = LocalContext.current return CorePalette( - primary = ContextCompat.getColor(context, android.R.color.system_accent1_500), - secondary = ContextCompat.getColor(context, android.R.color.system_accent2_500), - tertiary = ContextCompat.getColor(context, android.R.color.system_accent3_500), - neutral = ContextCompat.getColor(context, android.R.color.system_neutral1_500), - neutralVariant = ContextCompat.getColor(context, android.R.color.system_neutral2_500), + primary = ContextCompat.getColor(context, R.color.system_accent1_500), + secondary = ContextCompat.getColor(context, R.color.system_accent2_500), + tertiary = ContextCompat.getColor(context, R.color.system_accent3_500), + neutral = ContextCompat.getColor(context, R.color.system_neutral1_500), + neutralVariant = ContextCompat.getColor(context, R.color.system_neutral2_500), error = 0xFFB3261E.toInt(), ) } diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/shapes/Shapes.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/shapes/Shapes.kt index 63d3ad0c..c5ac2507 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/shapes/Shapes.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/shapes/Shapes.kt @@ -8,9 +8,9 @@ import androidx.compose.material3.Shapes import androidx.compose.runtime.Composable import androidx.compose.runtime.remember import androidx.compose.ui.unit.dp -import de.mm20.launcher2.themes.CornerStyle -import de.mm20.launcher2.themes.Shape as ThemeShape -import de.mm20.launcher2.themes.Shapes as ThemeShapes +import de.mm20.launcher2.themes.shapes.CornerStyle +import de.mm20.launcher2.themes.shapes.Shape as ThemeShape +import de.mm20.launcher2.themes.shapes.Shapes as ThemeShapes @Composable fun shapesOf(shapes: ThemeShapes): Shapes { diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/transparency/TransparencyScheme.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/transparency/TransparencyScheme.kt index 77ed6480..10075f29 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/transparency/TransparencyScheme.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/transparency/TransparencyScheme.kt @@ -1,10 +1,31 @@ package de.mm20.launcher2.ui.theme.transparency +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable import androidx.compose.runtime.compositionLocalOf +import androidx.compose.runtime.remember +import de.mm20.launcher2.themes.transparencies.Transparencies + + +@Composable +fun transparencySchemeOf(transparencies: Transparencies): TransparencyScheme { + return remember(transparencies) { + TransparencyScheme( + background = transparencies.background ?: 0.85f, + surface = transparencies.surface ?: 1f, + elevatedSurface = transparencies.elevatedSurface ?: 1f, + ) + } +} data class TransparencyScheme( - val background: Float, - val surface: Float, + val background: Float = 0.85f, + val surface: Float = 1f, + val elevatedSurface: Float = 1f, ) -val LocalTransparencyScheme = compositionLocalOf { TransparencyScheme(0.85f, 1f) } \ No newline at end of file +val LocalTransparencyScheme = compositionLocalOf { TransparencyScheme(background = 0.85f, surface = 1f, elevatedSurface = 1f) } + +val MaterialTheme.transparency + @Composable + get() = LocalTransparencyScheme.current \ No newline at end of file diff --git a/core/i18n/src/main/res/values/strings.xml b/core/i18n/src/main/res/values/strings.xml index 78d56013..20017ca3 100644 --- a/core/i18n/src/main/res/values/strings.xml +++ b/core/i18n/src/main/res/values/strings.xml @@ -439,6 +439,9 @@ Extra round Rectangular Base shape + Transparency + Default + Semi-transparent Font System default About @@ -821,8 +824,8 @@ The note could not be written to the linked file. Possibly, it has been moved or deleted. A copy has been saved to the launcher\'s internal storage. Do you really want to delete the color scheme %1$s\? Do you really want to delete the shape scheme %1$s? - New color scheme - New shapes + Do you really want to delete the transparency scheme %1$s? + (untitled) Use system default From primary color Palette diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt index 6cb71319..83253847 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt @@ -2,18 +2,22 @@ package de.mm20.launcher2.preferences import android.content.Context import de.mm20.launcher2.search.SearchFilters +import de.mm20.launcher2.serialization.UUIDSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonNames +import java.util.UUID @Serializable data class LauncherSettingsData internal constructor( val schemaVersion: Int = 5, val uiColorScheme: ColorScheme = ColorScheme.System, - @JsonNames("uiTheme") - val uiColors: ColorsDescriptor = ColorsDescriptor.Default, - val uiShapes: ShapesDescriptor = ShapesDescriptor.Default, + @Serializable(with = UUIDSerializer::class) + val uiColorsId: UUID = UUID(0L, 0L), + @Serializable(with = UUIDSerializer::class) + val uiShapesId: UUID = UUID(0L, 0L), + @Serializable(with = UUIDSerializer::class) + val uiTransparenciesId: UUID = UUID(0L, 0L), val uiCompatModeColors: Boolean = false, val uiFont: Font = Font.Outfit, @@ -205,53 +209,6 @@ enum class Font { System, } - -@Serializable -sealed interface ColorsDescriptor { - @Serializable - @SerialName("default") - data object Default : ColorsDescriptor - - @Serializable - @SerialName("high_contrast") - data object HighContrast : ColorsDescriptor - - @Serializable - @SerialName("bw") - data object BlackAndWhite : ColorsDescriptor - - @Serializable - @SerialName("custom") - data class Custom( - val id: String, - ) : ColorsDescriptor -} - -@Serializable -sealed interface ShapesDescriptor { - @Serializable - @SerialName("default") - data object Default : ShapesDescriptor - - @Serializable - @SerialName("cut") - data object Cut : ShapesDescriptor - - @Serializable - @SerialName("extra_round") - data object ExtraRound : ShapesDescriptor - - @Serializable - @SerialName("rect") - data object Rect : ShapesDescriptor - - @Serializable - @SerialName("custom") - data class Custom( - val id: String, - ) : ShapesDescriptor -} - internal enum class ClockWidgetStyleEnum { Digital1, Digital2, diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiSettings.kt index 8e1d0e4c..9abf8d16 100644 --- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiSettings.kt +++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiSettings.kt @@ -8,10 +8,9 @@ import de.mm20.launcher2.preferences.ScreenOrientation import de.mm20.launcher2.preferences.SearchBarColors import de.mm20.launcher2.preferences.SearchBarStyle import de.mm20.launcher2.preferences.SystemBarColors -import de.mm20.launcher2.preferences.ColorsDescriptor -import de.mm20.launcher2.preferences.ShapesDescriptor import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.map +import java.util.UUID data class CardStyle( val opacity: Float = 1f, @@ -286,25 +285,36 @@ class UiSettings internal constructor( } - val colors + val colorsId get() = launcherDataStore.data.map { - it.uiColors + it.uiColorsId }.distinctUntilChanged() - fun setColors(colors: ColorsDescriptor) { + fun setColorsId(colorsId: UUID) { launcherDataStore.update { - it.copy(uiColors = colors) + it.copy(uiColorsId = colorsId) } } - val shapes + val shapesId get() = launcherDataStore.data.map { - it.uiShapes + it.uiShapesId }.distinctUntilChanged() - fun setShapes(shapes: ShapesDescriptor) { + fun setShapesId(shapesId: UUID) { launcherDataStore.update { - it.copy(uiShapes = shapes) + it.copy(uiShapesId = shapesId) + } + } + + val transparenciesId + get() = launcherDataStore.data.map { + it.uiTransparenciesId + }.distinctUntilChanged() + + fun setTransparenciesId(transparenciesId: UUID) { + launcherDataStore.update { + it.copy(uiTransparenciesId = transparenciesId) } } diff --git a/data/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt b/data/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt index a0ceb897..d49eff12 100644 --- a/data/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt +++ b/data/database/src/main/java/de/mm20/launcher2/database/AppDatabase.kt @@ -20,6 +20,7 @@ import de.mm20.launcher2.database.entities.SavedSearchableEntity import de.mm20.launcher2.database.entities.SearchActionEntity import de.mm20.launcher2.database.entities.ColorsEntity import de.mm20.launcher2.database.entities.ShapesEntity +import de.mm20.launcher2.database.entities.TransparenciesEntity import de.mm20.launcher2.database.entities.WidgetEntity import de.mm20.launcher2.database.migrations.Migration_10_11 import de.mm20.launcher2.database.migrations.Migration_11_12 @@ -39,6 +40,7 @@ import de.mm20.launcher2.database.migrations.Migration_24_25 import de.mm20.launcher2.database.migrations.Migration_25_26 import de.mm20.launcher2.database.migrations.Migration_26_27 import de.mm20.launcher2.database.migrations.Migration_27_28 +import de.mm20.launcher2.database.migrations.Migration_28_29 import de.mm20.launcher2.database.migrations.Migration_6_7 import de.mm20.launcher2.database.migrations.Migration_7_8 import de.mm20.launcher2.database.migrations.Migration_8_9 @@ -59,7 +61,8 @@ import java.util.UUID ColorsEntity::class, PluginEntity::class, ShapesEntity::class, - ], version = 28, exportSchema = true + TransparenciesEntity::class, + ], version = 29, exportSchema = true ) @TypeConverters(ComponentNameConverter::class) abstract class AppDatabase : RoomDatabase() { @@ -160,6 +163,7 @@ abstract class AppDatabase : RoomDatabase() { Migration_25_26(), Migration_26_27(), Migration_27_28(), + Migration_28_29(), ).build() if (_instance == null) _instance = instance return instance diff --git a/data/database/src/main/java/de/mm20/launcher2/database/daos/ThemeDao.kt b/data/database/src/main/java/de/mm20/launcher2/database/daos/ThemeDao.kt index fc6956f8..08b2f098 100644 --- a/data/database/src/main/java/de/mm20/launcher2/database/daos/ThemeDao.kt +++ b/data/database/src/main/java/de/mm20/launcher2/database/daos/ThemeDao.kt @@ -6,6 +6,7 @@ import androidx.room.Query import androidx.room.Update import de.mm20.launcher2.database.entities.ColorsEntity import de.mm20.launcher2.database.entities.ShapesEntity +import de.mm20.launcher2.database.entities.TransparenciesEntity import kotlinx.coroutines.flow.Flow import java.util.UUID @@ -17,39 +18,60 @@ interface ThemeDao { @Query("SELECT * FROM Shapes") fun getAllShapes(): Flow> + @Query("SELECT * FROM Transparencies") + fun getAllTransparencies(): Flow> + @Query("SELECT * FROM Theme WHERE id = :id LIMIT 1") fun getColors(id: UUID): Flow @Query("SELECT * FROM Shapes WHERE id = :id LIMIT 1") fun getShapes(id: UUID): Flow + @Query("SELECT * FROM Transparencies WHERE id = :id LIMIT 1") + fun getTransparencies(id: UUID): Flow + @Insert suspend fun insertColors(colors: ColorsEntity) @Insert suspend fun insertShapes(shapes: ShapesEntity) + @Insert + suspend fun insertTransparencies(transparencies: TransparenciesEntity) + @Update suspend fun updateColors(colors: ColorsEntity) @Update suspend fun updateShapes(shapes: ShapesEntity) + @Update + suspend fun updateTransparencies(transparencies: TransparenciesEntity) + @Query("DELETE FROM Theme WHERE id = :id") suspend fun deleteColors(id: UUID) @Query("DELETE FROM Shapes WHERE id = :id") suspend fun deleteShapes(id: UUID) + @Query("DELETE FROM Transparencies WHERE id = :id") + suspend fun deleteTransparencies(id: UUID) + @Query("DELETE FROM Theme") suspend fun deleteAllColors() @Query("DELETE FROM Shapes") suspend fun deleteAllShapes() + @Query("DELETE FROM Transparencies") + suspend fun deleteAllTransparencies() + @Insert fun insertAllColors(colors: List) @Insert fun insertAllShapes(shapes: List) + + @Insert + fun insertAllTransparencies(transparencies: List) } \ No newline at end of file diff --git a/data/database/src/main/java/de/mm20/launcher2/database/entities/TransparenciesEntity.kt b/data/database/src/main/java/de/mm20/launcher2/database/entities/TransparenciesEntity.kt new file mode 100644 index 00000000..a04bfee7 --- /dev/null +++ b/data/database/src/main/java/de/mm20/launcher2/database/entities/TransparenciesEntity.kt @@ -0,0 +1,15 @@ +package de.mm20.launcher2.database.entities + +import androidx.room.Entity +import androidx.room.PrimaryKey +import java.util.UUID + +@Entity(tableName = "Transparencies") +data class TransparenciesEntity( + @PrimaryKey val id: UUID, + val name: String, + + val background: Float?, + val surface: Float?, + val elevatedSurface: Float?, +) \ No newline at end of file diff --git a/data/database/src/main/java/de/mm20/launcher2/database/migrations/Migration_28_29.kt b/data/database/src/main/java/de/mm20/launcher2/database/migrations/Migration_28_29.kt new file mode 100644 index 00000000..0117a429 --- /dev/null +++ b/data/database/src/main/java/de/mm20/launcher2/database/migrations/Migration_28_29.kt @@ -0,0 +1,22 @@ +package de.mm20.launcher2.database.migrations + +import androidx.room.migration.Migration +import androidx.sqlite.SQLiteConnection +import androidx.sqlite.execSQL + +class Migration_28_29: Migration(28, 29) { + + override fun migrate(connection: SQLiteConnection) { + connection.execSQL( + """ + CREATE TABLE IF NOT EXISTS `Transparencies` ( + `id` BLOB NOT NULL PRIMARY KEY, + `name` TEXT NOT NULL, + `background` REAL, + `surface` REAL, + `elevatedSurface` REAL + ) + """.trimIndent() + ) + } +} \ No newline at end of file diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/DefaultThemes.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/DefaultThemes.kt index ee2d9f11..0629e4ae 100644 --- a/data/themes/src/main/java/de/mm20/launcher2/themes/DefaultThemes.kt +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/DefaultThemes.kt @@ -1,245 +1,20 @@ package de.mm20.launcher2.themes +import de.mm20.launcher2.themes.colors.Color +import de.mm20.launcher2.themes.colors.ColorRef +import de.mm20.launcher2.themes.colors.ColorScheme +import de.mm20.launcher2.themes.colors.CorePaletteColor +import de.mm20.launcher2.themes.colors.StaticColor import java.util.UUID val DefaultThemeId = UUID(0L, 0L) + val HighContrastThemeId = UUID(0L, 2L) val BlackAndWhiteThemeId = UUID(0L, 1L) + val ExtraRoundShapesId = UUID(0L, 1L) val CutShapesId = UUID(0L, 2L) val RectShapesId = UUID(0L, 3L) -val DefaultLightColorScheme = ColorScheme( - primary = ColorRef(CorePaletteColor.Primary, 40), - onPrimary = ColorRef(CorePaletteColor.Primary, 100), - primaryContainer = ColorRef(CorePaletteColor.Primary, 90), - onPrimaryContainer = ColorRef(CorePaletteColor.Primary, 30), - secondary = ColorRef(CorePaletteColor.Secondary, 40), - onSecondary = ColorRef(CorePaletteColor.Secondary, 100), - secondaryContainer = ColorRef(CorePaletteColor.Secondary, 90), - onSecondaryContainer = ColorRef(CorePaletteColor.Secondary, 30), - tertiary = ColorRef(CorePaletteColor.Tertiary, 40), - onTertiary = ColorRef(CorePaletteColor.Tertiary, 100), - tertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 90), - onTertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 30), - error = ColorRef(CorePaletteColor.Error, 40), - onError = ColorRef(CorePaletteColor.Error, 100), - errorContainer = ColorRef(CorePaletteColor.Error, 90), - onErrorContainer = ColorRef(CorePaletteColor.Error, 30), - surfaceDim = ColorRef(CorePaletteColor.Neutral, 87), - surface = ColorRef(CorePaletteColor.Neutral, 98), - surfaceBright = ColorRef(CorePaletteColor.Neutral, 98), - surfaceContainerLowest = ColorRef(CorePaletteColor.Neutral, 100), - surfaceContainerLow = ColorRef(CorePaletteColor.Neutral, 96), - surfaceContainer = ColorRef(CorePaletteColor.Neutral, 94), - surfaceContainerHigh = ColorRef(CorePaletteColor.Neutral, 92), - surfaceContainerHighest = ColorRef(CorePaletteColor.Neutral, 90), - onSurface = ColorRef(CorePaletteColor.Neutral, 10), - onSurfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 30), - outline = ColorRef(CorePaletteColor.NeutralVariant, 50), - outlineVariant = ColorRef(CorePaletteColor.NeutralVariant, 80), - inverseSurface = ColorRef(CorePaletteColor.Neutral, 20), - inverseOnSurface = ColorRef(CorePaletteColor.Neutral, 95), - inversePrimary = ColorRef(CorePaletteColor.Primary, 80), - surfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 90), - surfaceTint = ColorRef(CorePaletteColor.Primary, 40), - background = ColorRef(CorePaletteColor.Neutral, 98), - onBackground = ColorRef(CorePaletteColor.Neutral, 10), - scrim = ColorRef(CorePaletteColor.Neutral, 0), -) - -val DefaultDarkColorScheme = ColorScheme( - primary = ColorRef(CorePaletteColor.Primary, 80), - onPrimary = ColorRef(CorePaletteColor.Primary, 20), - primaryContainer = ColorRef(CorePaletteColor.Primary, 30), - onPrimaryContainer = ColorRef(CorePaletteColor.Primary, 90), - secondary = ColorRef(CorePaletteColor.Secondary, 80), - onSecondary = ColorRef(CorePaletteColor.Secondary, 20), - secondaryContainer = ColorRef(CorePaletteColor.Secondary, 30), - onSecondaryContainer = ColorRef(CorePaletteColor.Secondary, 90), - tertiary = ColorRef(CorePaletteColor.Tertiary, 80), - onTertiary = ColorRef(CorePaletteColor.Tertiary, 20), - tertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 30), - onTertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 90), - error = ColorRef(CorePaletteColor.Error, 80), - onError = ColorRef(CorePaletteColor.Error, 20), - errorContainer = ColorRef(CorePaletteColor.Error, 30), - onErrorContainer = ColorRef(CorePaletteColor.Error, 90), - surfaceDim = ColorRef(CorePaletteColor.Neutral, 6), - surface = ColorRef(CorePaletteColor.Neutral, 6), - surfaceBright = ColorRef(CorePaletteColor.Neutral, 24), - surfaceContainerLowest = ColorRef(CorePaletteColor.Neutral, 4), - surfaceContainerLow = ColorRef(CorePaletteColor.Neutral, 10), - surfaceContainer = ColorRef(CorePaletteColor.Neutral, 12), - surfaceContainerHigh = ColorRef(CorePaletteColor.Neutral, 17), - surfaceContainerHighest = ColorRef(CorePaletteColor.Neutral, 22), - onSurface = ColorRef(CorePaletteColor.Neutral, 90), - onSurfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 80), - outline = ColorRef(CorePaletteColor.NeutralVariant, 60), - outlineVariant = ColorRef(CorePaletteColor.NeutralVariant, 30), - inverseSurface = ColorRef(CorePaletteColor.Neutral, 90), - inverseOnSurface = ColorRef(CorePaletteColor.Neutral, 20), - inversePrimary = ColorRef(CorePaletteColor.Primary, 40), - surfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 30), - surfaceTint = ColorRef(CorePaletteColor.Primary, 80), - background = ColorRef(CorePaletteColor.Neutral, 6), - onBackground = ColorRef(CorePaletteColor.Neutral, 90), - scrim = ColorRef(CorePaletteColor.Neutral, 0), -) - -val HighContrastLightColorScheme = ColorScheme( - primary = ColorRef(CorePaletteColor.Primary, 20), - onPrimary = ColorRef(CorePaletteColor.Primary, 100), - primaryContainer = ColorRef(CorePaletteColor.Primary, 30), - onPrimaryContainer = ColorRef(CorePaletteColor.Primary, 100), - secondary = ColorRef(CorePaletteColor.Secondary, 20), - onSecondary = ColorRef(CorePaletteColor.Secondary, 100), - secondaryContainer = ColorRef(CorePaletteColor.Secondary, 30), - onSecondaryContainer = ColorRef(CorePaletteColor.Secondary, 100), - tertiary = ColorRef(CorePaletteColor.Tertiary, 20), - onTertiary = ColorRef(CorePaletteColor.Tertiary, 100), - tertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 30), - onTertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 100), - error = ColorRef(CorePaletteColor.Error, 20), - onError = ColorRef(CorePaletteColor.Error, 100), - errorContainer = ColorRef(CorePaletteColor.Error, 30), - onErrorContainer = ColorRef(CorePaletteColor.Error, 100), - surfaceDim = ColorRef(CorePaletteColor.Neutral, 87), - surface = ColorRef(CorePaletteColor.Neutral, 98), - surfaceBright = ColorRef(CorePaletteColor.Neutral, 98), - surfaceContainerLowest = ColorRef(CorePaletteColor.Neutral, 100), - surfaceContainerLow = ColorRef(CorePaletteColor.Neutral, 96), - surfaceContainer = ColorRef(CorePaletteColor.Neutral, 94), - surfaceContainerHigh = ColorRef(CorePaletteColor.Neutral, 92), - surfaceContainerHighest = ColorRef(CorePaletteColor.Neutral, 90), - onSurface = ColorRef(CorePaletteColor.Neutral, 0), - onSurfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 0), - outline = ColorRef(CorePaletteColor.NeutralVariant, 20), - outlineVariant = ColorRef(CorePaletteColor.NeutralVariant, 30), - inverseSurface = ColorRef(CorePaletteColor.Neutral, 20), - inverseOnSurface = ColorRef(CorePaletteColor.Neutral, 100), - inversePrimary = ColorRef(CorePaletteColor.Primary, 80), - surfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 90), - surfaceTint = ColorRef(CorePaletteColor.Primary, 20), - background = ColorRef(CorePaletteColor.Neutral, 98), - onBackground = ColorRef(CorePaletteColor.Neutral, 0), - scrim = ColorRef(CorePaletteColor.Neutral, 0), -) - -val HighContrastDarkColorScheme = ColorScheme( - primary = ColorRef(CorePaletteColor.Primary, 95), - onPrimary = ColorRef(CorePaletteColor.Primary, 0), - primaryContainer = ColorRef(CorePaletteColor.Primary, 80), - onPrimaryContainer = ColorRef(CorePaletteColor.Primary, 0), - secondary = ColorRef(CorePaletteColor.Secondary, 95), - onSecondary = ColorRef(CorePaletteColor.Secondary, 0), - secondaryContainer = ColorRef(CorePaletteColor.Secondary, 80), - onSecondaryContainer = ColorRef(CorePaletteColor.Secondary, 0), - tertiary = ColorRef(CorePaletteColor.Tertiary, 95), - onTertiary = ColorRef(CorePaletteColor.Tertiary, 0), - tertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 80), - onTertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 0), - error = ColorRef(CorePaletteColor.Error, 95), - onError = ColorRef(CorePaletteColor.Error, 0), - errorContainer = ColorRef(CorePaletteColor.Error, 80), - onErrorContainer = ColorRef(CorePaletteColor.Error, 0), - surfaceDim = ColorRef(CorePaletteColor.Neutral, 6), - surface = ColorRef(CorePaletteColor.Neutral, 6), - surfaceBright = ColorRef(CorePaletteColor.Neutral, 24), - surfaceContainerLowest = ColorRef(CorePaletteColor.Neutral, 4), - surfaceContainerLow = ColorRef(CorePaletteColor.Neutral, 10), - surfaceContainer = ColorRef(CorePaletteColor.Neutral, 12), - surfaceContainerHigh = ColorRef(CorePaletteColor.Neutral, 17), - surfaceContainerHighest = ColorRef(CorePaletteColor.Neutral, 22), - onSurface = ColorRef(CorePaletteColor.Neutral, 100), - onSurfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 100), - outline = ColorRef(CorePaletteColor.NeutralVariant, 95), - outlineVariant = ColorRef(CorePaletteColor.NeutralVariant, 80), - inverseSurface = ColorRef(CorePaletteColor.Neutral, 90), - inverseOnSurface = ColorRef(CorePaletteColor.Neutral, 0), - inversePrimary = ColorRef(CorePaletteColor.Primary, 20), - surfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 30), - surfaceTint = ColorRef(CorePaletteColor.Primary, 95), - background = ColorRef(CorePaletteColor.Neutral, 6), - onBackground = ColorRef(CorePaletteColor.Neutral, 100), - scrim = ColorRef(CorePaletteColor.Neutral, 0), -) - -val BlackAndWhiteLightColorScheme = ColorScheme( - primary = StaticColor(0xFF000000.toInt()), - onPrimary = StaticColor(0xFFFFFFFF.toInt()), - primaryContainer = StaticColor(0xFFFFFFFF.toInt()), - onPrimaryContainer = StaticColor(0xFF000000.toInt()), - inversePrimary = StaticColor(0xFFFFFFFF.toInt()), - secondary = StaticColor(0xFF000000.toInt()), - onSecondary = StaticColor(0xFFFFFFFF.toInt()), - secondaryContainer = StaticColor(0xFFFFFFFF.toInt()), - onSecondaryContainer = StaticColor(0xFF000000.toInt()), - tertiary = StaticColor(0xFF000000.toInt()), - onTertiary = StaticColor(0xFFFFFFFF.toInt()), - tertiaryContainer = StaticColor(0xFFFFFFFF.toInt()), - onTertiaryContainer = StaticColor(0xFF000000.toInt()), - background = StaticColor(0xFFFFFFFF.toInt()), - onBackground = StaticColor(0xFF000000.toInt()), - surface = StaticColor(0xFFFFFFFF.toInt()), - onSurface = StaticColor(0xFF000000.toInt()), - surfaceVariant = StaticColor(0xFFFFFFFF.toInt()), - onSurfaceVariant = StaticColor(0xFF000000.toInt()), - inverseSurface = StaticColor(0xFF000000.toInt()), - inverseOnSurface = StaticColor(0xFFFFFFFF.toInt()), - error = null, - onError = null, - errorContainer = null, - onErrorContainer = null, - outline = StaticColor(0xFF000000.toInt()), - surfaceTint = StaticColor(0xFFFFFFFF.toInt()), - outlineVariant = StaticColor(0xFF000000.toInt()), - scrim = StaticColor(0xFF000000.toInt()), - surfaceDim = StaticColor(0xFFFFFFFF.toInt()), - surfaceBright = StaticColor(0xFFFFFFFF.toInt()), - surfaceContainer = StaticColor(0xFFFFFFFF.toInt()), - surfaceContainerHigh = StaticColor(0xFFFFFFFF.toInt()), - surfaceContainerHighest = StaticColor(0xFFFFFFFF.toInt()), - surfaceContainerLow = StaticColor(0xFFFFFFFF.toInt()), - surfaceContainerLowest = StaticColor(0xFFFFFFFF.toInt()), -) - -val BlackAndWhiteDarkColorScheme = ColorScheme( - primary = StaticColor(0xFFFFFFFF.toInt()), - onPrimary = StaticColor(0xFF000000.toInt()), - primaryContainer = StaticColor(0xFF000000.toInt()), - onPrimaryContainer = StaticColor(0xFFFFFFFF.toInt()), - inversePrimary = StaticColor(0xFF000000.toInt()), - secondary = StaticColor(0xFFFFFFFF.toInt()), - onSecondary = StaticColor(0xFF000000.toInt()), - secondaryContainer = StaticColor(0xFF000000.toInt()), - onSecondaryContainer = StaticColor(0xFFFFFFFF.toInt()), - tertiary = StaticColor(0xFFFFFFFF.toInt()), - onTertiary = StaticColor(0xFF000000.toInt()), - tertiaryContainer = StaticColor(0xFF000000.toInt()), - onTertiaryContainer = StaticColor(0xFFFFFFFF.toInt()), - background = StaticColor(0xFF000000.toInt()), - onBackground = StaticColor(0xFFFFFFFF.toInt()), - surface = StaticColor(0xFF000000.toInt()), - onSurface = StaticColor(0xFFFFFFFF.toInt()), - surfaceVariant = StaticColor(0xFF000000.toInt()), - onSurfaceVariant = StaticColor(0xFFFFFFFF.toInt()), - inverseSurface = StaticColor(0xFFFFFFFF.toInt()), - inverseOnSurface = StaticColor(0xFF000000.toInt()), - error = null, - onError = null, - errorContainer = null, - onErrorContainer = null, - outline = StaticColor(0xFFFFFFFF.toInt()), - surfaceTint = StaticColor(0xFFFFFFFF.toInt()), - outlineVariant = StaticColor(0xFFFFFFFF.toInt()), - scrim = StaticColor(0xFFFFFFFF.toInt()), - surfaceDim = StaticColor(0xFF000000.toInt()), - surfaceBright = StaticColor(0xFF000000.toInt()), - surfaceContainer = StaticColor(0xFF000000.toInt()), - surfaceContainerHigh = StaticColor(0xFF000000.toInt()), - surfaceContainerHighest = StaticColor(0xFF000000.toInt()), - surfaceContainerLow = StaticColor(0xFF000000.toInt()), - surfaceContainerLowest = StaticColor(0xFF000000.toInt()), -) \ No newline at end of file +val SemiTransparentId = UUID(0L, 1L) \ No newline at end of file diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/LegacySerialization.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/LegacySerialization.kt index b5f30138..841937fd 100644 --- a/data/themes/src/main/java/de/mm20/launcher2/themes/LegacySerialization.kt +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/LegacySerialization.kt @@ -1,5 +1,9 @@ package de.mm20.launcher2.themes +import de.mm20.launcher2.themes.colors.Color +import de.mm20.launcher2.themes.colors.ColorRef +import de.mm20.launcher2.themes.colors.Colors +import de.mm20.launcher2.themes.colors.StaticColor import kotlinx.serialization.KSerializer import kotlinx.serialization.SerializationException import kotlinx.serialization.descriptors.PrimitiveKind diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/Serialization.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/Serialization.kt index 322984c4..1e470bac 100644 --- a/data/themes/src/main/java/de/mm20/launcher2/themes/Serialization.kt +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/Serialization.kt @@ -1,5 +1,8 @@ package de.mm20.launcher2.themes +import de.mm20.launcher2.themes.colors.Color +import de.mm20.launcher2.themes.colors.StaticColor +import de.mm20.launcher2.themes.shapes.Shape import kotlinx.serialization.KSerializer import kotlinx.serialization.descriptors.PrimitiveKind import kotlinx.serialization.descriptors.PrimitiveSerialDescriptor @@ -8,7 +11,6 @@ import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder import kotlinx.serialization.json.Json import kotlinx.serialization.modules.SerializersModule -import kotlinx.serialization.modules.polymorphic internal val module = SerializersModule { contextual(Color::class, ColorSerializer) diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeBundle.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeBundle.kt index b7f2eab4..89555d55 100644 --- a/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeBundle.kt +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeBundle.kt @@ -2,12 +2,17 @@ package de.mm20.launcher2.themes import android.util.Log import de.mm20.launcher2.crashreporter.CrashReporter -import de.mm20.launcher2.serialization.Json -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.withContext +import de.mm20.launcher2.themes.colors.Color +import de.mm20.launcher2.themes.colors.ColorScheme +import de.mm20.launcher2.themes.colors.Colors +import de.mm20.launcher2.themes.colors.CorePalette +import de.mm20.launcher2.themes.colors.DefaultDarkColorScheme +import de.mm20.launcher2.themes.colors.DefaultLightColorScheme +import de.mm20.launcher2.themes.colors.EmptyCorePalette +import de.mm20.launcher2.themes.shapes.Shapes +import de.mm20.launcher2.themes.transparencies.Transparencies import kotlinx.serialization.Serializable import kotlinx.serialization.SerializationException -import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.contentOrNull @@ -22,6 +27,7 @@ data class ThemeBundle( val author: String? = null, val colors: Colors? = null, val shapes: Shapes? = null, + val transparencies: Transparencies? = null, /** * The file version, always 2 for the new theme format. */ diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeRepository.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeRepository.kt index 793d1a7a..da9f6f05 100644 --- a/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeRepository.kt +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/ThemeRepository.kt @@ -4,225 +4,24 @@ import android.content.Context import de.mm20.launcher2.backup.Backupable import de.mm20.launcher2.crashreporter.CrashReporter import de.mm20.launcher2.database.AppDatabase -import de.mm20.launcher2.preferences.ColorsDescriptor -import de.mm20.launcher2.preferences.ShapesDescriptor -import kotlinx.coroutines.CoroutineScope +import de.mm20.launcher2.themes.colors.Colors +import de.mm20.launcher2.themes.colors.ColorsRepository +import de.mm20.launcher2.themes.shapes.ShapesRepository +import de.mm20.launcher2.themes.transparencies.TransparenciesRepository import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.flowOn -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.serialization.SerializationException import java.io.File -import java.util.UUID class ThemeRepository( private val context: Context, private val database: AppDatabase, ) : Backupable { - private val scope = CoroutineScope(Dispatchers.IO + Job()) + val colors = ColorsRepository(context, database) + val shapes = ShapesRepository(context, database) + val transparencies = TransparenciesRepository(context, database) - fun getAllColors(): Flow> { - return database.themeDao().getAllColors().map { - getBuiltInColors() + it.map { Colors(it) } - } - } - - fun getColors(id: UUID): Flow { - if (id == DefaultThemeId) return flowOf(getDefaultColors()) - if (id == HighContrastThemeId) return flowOf(getHighContrastColors()) - if (id == BlackAndWhiteThemeId) return flowOf(getBlackAndWhiteColors()) - return database.themeDao().getColors(id).map { it?.let { Colors(it) } }.flowOn(Dispatchers.Default) - } - - fun createColors(colors: Colors) { - scope.launch { - database.themeDao().insertColors(colors.toEntity()) - } - } - - fun updateColors(colors: Colors) { - scope.launch { - database.themeDao().updateColors(colors.toEntity()) - } - } - - fun getColorsOrDefault(theme: ColorsDescriptor?): Flow { - return when(theme) { - is ColorsDescriptor.HighContrast -> flowOf(getHighContrastColors()) - is ColorsDescriptor.BlackAndWhite -> flowOf(getBlackAndWhiteColors()) - is ColorsDescriptor.Custom -> { - val id = UUID.fromString(theme.id) - getColors(id).map { it ?: getDefaultColors() } - } - else -> flowOf(getDefaultColors()) - } - } - - private fun getBuiltInColors(): List { - return listOf( - getDefaultColors(), - getHighContrastColors(), - getBlackAndWhiteColors(), - ) - } - - private fun getDefaultColors(): Colors { - return Colors( - id = DefaultThemeId, - builtIn = true, - name = context.getString(R.string.preference_colors_default), - corePalette = EmptyCorePalette, - lightColorScheme = DefaultLightColorScheme, - darkColorScheme = DefaultDarkColorScheme, - ) - } - - private fun getHighContrastColors(): Colors { - return Colors( - id = HighContrastThemeId, - builtIn = true, - name = context.getString(R.string.preference_colors_high_contrast), - corePalette = EmptyCorePalette, - lightColorScheme = HighContrastLightColorScheme, - darkColorScheme = HighContrastDarkColorScheme, - ) - } - - private fun getBlackAndWhiteColors(): Colors { - return Colors( - id = BlackAndWhiteThemeId, - builtIn = true, - name = context.getString(R.string.preference_colors_bw), - corePalette = EmptyCorePalette, - lightColorScheme = BlackAndWhiteLightColorScheme, - darkColorScheme = BlackAndWhiteDarkColorScheme, - ) - } - - fun deleteColors(colors: Colors) { - scope.launch { - database.themeDao().deleteColors(colors.id) - } - } - - fun getAllShapes(): Flow> { - return database.themeDao().getAllShapes().map { - getBuiltInShapes() + it.map { Shapes(it) } - } - } - - fun getShapes(id: UUID): Flow { - if (id == DefaultThemeId) return flowOf(getDefaultShapes()) - if (id == ExtraRoundShapesId) return flowOf(getExtraRoundShapes()) - if (id == RectShapesId) return flowOf(getRectShapes()) - if (id == CutShapesId) return flowOf(getCutShapes()) - return database.themeDao().getShapes(id).map { it?.let { Shapes(it) } }.flowOn(Dispatchers.Default) - } - - fun createShapes(shapes: Shapes) { - scope.launch { - database.themeDao().insertShapes(shapes.toEntity()) - } - } - - fun updateShapes(shapes: Shapes) { - scope.launch { - database.themeDao().updateShapes(shapes.toEntity()) - } - } - - fun getShapesOrDefault(theme: ShapesDescriptor?): Flow { - return when(theme) { - is ShapesDescriptor.Custom -> { - val id = UUID.fromString(theme.id) - getShapes(id).map { it ?: getDefaultShapes() } - } - is ShapesDescriptor.ExtraRound -> flowOf(getExtraRoundShapes()) - is ShapesDescriptor.Rect -> flowOf(getRectShapes()) - is ShapesDescriptor.Cut -> flowOf(getCutShapes()) - else -> flowOf(getDefaultShapes()) - } - } - - private fun getBuiltInShapes(): List { - return listOf( - getDefaultShapes(), - getExtraRoundShapes(), - getRectShapes(), - getCutShapes(), - ) - } - - private fun getDefaultShapes(): Shapes { - return Shapes( - id = DefaultThemeId, - builtIn = true, - name = context.getString(R.string.preference_shapes_default), - baseShape = Shape( - corners = CornerStyle.Rounded, - radii = intArrayOf(12, 12, 12, 12), - ) - ) - } - - private fun getCutShapes(): Shapes { - return Shapes( - id = CutShapesId, - builtIn = true, - name = context.getString(R.string.preference_cards_shape_cut), - baseShape = Shape( - corners = CornerStyle.Cut, - radii = intArrayOf(12, 12, 12, 12), - ) - ) - } - - private fun getExtraRoundShapes(): Shapes { - return Shapes( - id = ExtraRoundShapesId, - builtIn = true, - name = context.getString(R.string.preference_shapes_extra_round), - baseShape = Shape( - corners = CornerStyle.Rounded, - radii = intArrayOf(24, 24, 24, 24), - ), - extraSmall = Shape( - radii = intArrayOf(4, 4, 4, 4), - ), - extraLarge = Shape( - radii = intArrayOf(36, 36, 36, 36), - ), - extraLargeIncreased = Shape( - radii = intArrayOf(40, 40, 40, 40), - ), - extraExtraLarge = Shape( - radii = intArrayOf(56, 56, 56, 56), - ) - ) - } - - private fun getRectShapes(): Shapes { - return Shapes( - id = RectShapesId, - builtIn = true, - name = context.getString(R.string.preference_shapes_rect), - baseShape = Shape( - corners = CornerStyle.Rounded, - radii = intArrayOf(0, 0, 0, 0), - ) - ) - } - - fun deleteShapes(shapes: Shapes) { - scope.launch { - database.themeDao().deleteShapes(shapes.id) - } - } override suspend fun backup(toDir: File) = withContext(Dispatchers.IO) { val dao = database.themeDao() @@ -257,5 +56,4 @@ class ThemeRepository( dao.insertAllColors(colors.map { it.toEntity() }) } } - } \ No newline at end of file diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/Colors.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/colors/Colors.kt similarity index 99% rename from data/themes/src/main/java/de/mm20/launcher2/themes/Colors.kt rename to data/themes/src/main/java/de/mm20/launcher2/themes/colors/Colors.kt index 1dbf4804..d861c46f 100644 --- a/data/themes/src/main/java/de/mm20/launcher2/themes/Colors.kt +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/colors/Colors.kt @@ -1,4 +1,4 @@ -package de.mm20.launcher2.themes +package de.mm20.launcher2.themes.colors import de.mm20.launcher2.database.entities.ColorsEntity import de.mm20.launcher2.serialization.ColorIntAsHexSerializer diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/colors/ColorsRepository.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/colors/ColorsRepository.kt new file mode 100644 index 00000000..54a7448b --- /dev/null +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/colors/ColorsRepository.kt @@ -0,0 +1,102 @@ +package de.mm20.launcher2.themes.colors + +import android.content.Context +import de.mm20.launcher2.database.AppDatabase +import de.mm20.launcher2.themes.BlackAndWhiteThemeId +import de.mm20.launcher2.themes.DefaultThemeId +import de.mm20.launcher2.themes.HighContrastThemeId +import de.mm20.launcher2.themes.R +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import java.util.UUID + +class ColorsRepository( + private val context: Context, + private val database: AppDatabase, +) { + private val scope = CoroutineScope(Dispatchers.IO + Job()) + + fun getAll(): Flow> { + return database.themeDao().getAllColors().map { + getBuiltIn() + it.map { Colors(it) } + } + } + + fun get(id: UUID): Flow { + if (id == DefaultThemeId) return flowOf(default) + if (id == HighContrastThemeId) return flowOf(highContrast) + if (id == BlackAndWhiteThemeId) return flowOf(blackAndWhite) + return database.themeDao().getColors(id).map { it?.let { Colors(it) } } + .flowOn(Dispatchers.Default) + } + + fun create(colors: Colors) { + scope.launch { + database.themeDao().insertColors(colors.toEntity()) + } + } + + fun update(colors: Colors) { + scope.launch { + database.themeDao().updateColors(colors.toEntity()) + } + } + + + fun delete(colors: Colors) { + scope.launch { + database.themeDao().deleteColors(colors.id) + } + } + + fun getOrDefault(id: UUID?): Flow { + if (id == null) return flowOf(default) + return get(id).map { it ?: default } + } + + private fun getBuiltIn(): List { + return listOf( + default, + highContrast, + blackAndWhite, + ) + } + + private val default: Colors + get() = Colors( + id = DefaultThemeId, + builtIn = true, + name = context.getString(R.string.preference_colors_default), + corePalette = EmptyCorePalette, + lightColorScheme = DefaultLightColorScheme, + darkColorScheme = DefaultDarkColorScheme, + ) + + + private val highContrast: Colors + get() = Colors( + id = HighContrastThemeId, + builtIn = true, + name = context.getString(R.string.preference_colors_high_contrast), + corePalette = EmptyCorePalette, + lightColorScheme = HighContrastLightColorScheme, + darkColorScheme = HighContrastDarkColorScheme, + ) + + + private val blackAndWhite: Colors + get() = Colors( + id = BlackAndWhiteThemeId, + builtIn = true, + name = context.getString(R.string.preference_colors_bw), + corePalette = EmptyCorePalette, + lightColorScheme = BlackAndWhiteLightColorScheme, + darkColorScheme = BlackAndWhiteDarkColorScheme, + ) +} \ No newline at end of file diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/colors/Defaults.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/colors/Defaults.kt new file mode 100644 index 00000000..fb7f62c7 --- /dev/null +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/colors/Defaults.kt @@ -0,0 +1,235 @@ +package de.mm20.launcher2.themes.colors + +val DefaultLightColorScheme = ColorScheme( + primary = ColorRef(CorePaletteColor.Primary, 40), + onPrimary = ColorRef(CorePaletteColor.Primary, 100), + primaryContainer = ColorRef(CorePaletteColor.Primary, 90), + onPrimaryContainer = ColorRef(CorePaletteColor.Primary, 30), + secondary = ColorRef(CorePaletteColor.Secondary, 40), + onSecondary = ColorRef(CorePaletteColor.Secondary, 100), + secondaryContainer = ColorRef(CorePaletteColor.Secondary, 90), + onSecondaryContainer = ColorRef(CorePaletteColor.Secondary, 30), + tertiary = ColorRef(CorePaletteColor.Tertiary, 40), + onTertiary = ColorRef(CorePaletteColor.Tertiary, 100), + tertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 90), + onTertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 30), + error = ColorRef(CorePaletteColor.Error, 40), + onError = ColorRef(CorePaletteColor.Error, 100), + errorContainer = ColorRef(CorePaletteColor.Error, 90), + onErrorContainer = ColorRef(CorePaletteColor.Error, 30), + surfaceDim = ColorRef(CorePaletteColor.Neutral, 87), + surface = ColorRef(CorePaletteColor.Neutral, 98), + surfaceBright = ColorRef(CorePaletteColor.Neutral, 98), + surfaceContainerLowest = ColorRef(CorePaletteColor.Neutral, 100), + surfaceContainerLow = ColorRef(CorePaletteColor.Neutral, 96), + surfaceContainer = ColorRef(CorePaletteColor.Neutral, 94), + surfaceContainerHigh = ColorRef(CorePaletteColor.Neutral, 92), + surfaceContainerHighest = ColorRef(CorePaletteColor.Neutral, 90), + onSurface = ColorRef(CorePaletteColor.Neutral, 10), + onSurfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 30), + outline = ColorRef(CorePaletteColor.NeutralVariant, 50), + outlineVariant = ColorRef(CorePaletteColor.NeutralVariant, 80), + inverseSurface = ColorRef(CorePaletteColor.Neutral, 20), + inverseOnSurface = ColorRef(CorePaletteColor.Neutral, 95), + inversePrimary = ColorRef(CorePaletteColor.Primary, 80), + surfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 90), + surfaceTint = ColorRef(CorePaletteColor.Primary, 40), + background = ColorRef(CorePaletteColor.Neutral, 98), + onBackground = ColorRef(CorePaletteColor.Neutral, 10), + scrim = ColorRef(CorePaletteColor.Neutral, 0), +) + +val DefaultDarkColorScheme = ColorScheme( + primary = ColorRef(CorePaletteColor.Primary, 80), + onPrimary = ColorRef(CorePaletteColor.Primary, 20), + primaryContainer = ColorRef(CorePaletteColor.Primary, 30), + onPrimaryContainer = ColorRef(CorePaletteColor.Primary, 90), + secondary = ColorRef(CorePaletteColor.Secondary, 80), + onSecondary = ColorRef(CorePaletteColor.Secondary, 20), + secondaryContainer = ColorRef(CorePaletteColor.Secondary, 30), + onSecondaryContainer = ColorRef(CorePaletteColor.Secondary, 90), + tertiary = ColorRef(CorePaletteColor.Tertiary, 80), + onTertiary = ColorRef(CorePaletteColor.Tertiary, 20), + tertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 30), + onTertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 90), + error = ColorRef(CorePaletteColor.Error, 80), + onError = ColorRef(CorePaletteColor.Error, 20), + errorContainer = ColorRef(CorePaletteColor.Error, 30), + onErrorContainer = ColorRef(CorePaletteColor.Error, 90), + surfaceDim = ColorRef(CorePaletteColor.Neutral, 6), + surface = ColorRef(CorePaletteColor.Neutral, 6), + surfaceBright = ColorRef(CorePaletteColor.Neutral, 24), + surfaceContainerLowest = ColorRef(CorePaletteColor.Neutral, 4), + surfaceContainerLow = ColorRef(CorePaletteColor.Neutral, 10), + surfaceContainer = ColorRef(CorePaletteColor.Neutral, 12), + surfaceContainerHigh = ColorRef(CorePaletteColor.Neutral, 17), + surfaceContainerHighest = ColorRef(CorePaletteColor.Neutral, 22), + onSurface = ColorRef(CorePaletteColor.Neutral, 90), + onSurfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 80), + outline = ColorRef(CorePaletteColor.NeutralVariant, 60), + outlineVariant = ColorRef(CorePaletteColor.NeutralVariant, 30), + inverseSurface = ColorRef(CorePaletteColor.Neutral, 90), + inverseOnSurface = ColorRef(CorePaletteColor.Neutral, 20), + inversePrimary = ColorRef(CorePaletteColor.Primary, 40), + surfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 30), + surfaceTint = ColorRef(CorePaletteColor.Primary, 80), + background = ColorRef(CorePaletteColor.Neutral, 6), + onBackground = ColorRef(CorePaletteColor.Neutral, 90), + scrim = ColorRef(CorePaletteColor.Neutral, 0), +) + +val HighContrastLightColorScheme = ColorScheme( + primary = ColorRef(CorePaletteColor.Primary, 20), + onPrimary = ColorRef(CorePaletteColor.Primary, 100), + primaryContainer = ColorRef(CorePaletteColor.Primary, 30), + onPrimaryContainer = ColorRef(CorePaletteColor.Primary, 100), + secondary = ColorRef(CorePaletteColor.Secondary, 20), + onSecondary = ColorRef(CorePaletteColor.Secondary, 100), + secondaryContainer = ColorRef(CorePaletteColor.Secondary, 30), + onSecondaryContainer = ColorRef(CorePaletteColor.Secondary, 100), + tertiary = ColorRef(CorePaletteColor.Tertiary, 20), + onTertiary = ColorRef(CorePaletteColor.Tertiary, 100), + tertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 30), + onTertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 100), + error = ColorRef(CorePaletteColor.Error, 20), + onError = ColorRef(CorePaletteColor.Error, 100), + errorContainer = ColorRef(CorePaletteColor.Error, 30), + onErrorContainer = ColorRef(CorePaletteColor.Error, 100), + surfaceDim = ColorRef(CorePaletteColor.Neutral, 87), + surface = ColorRef(CorePaletteColor.Neutral, 98), + surfaceBright = ColorRef(CorePaletteColor.Neutral, 98), + surfaceContainerLowest = ColorRef(CorePaletteColor.Neutral, 100), + surfaceContainerLow = ColorRef(CorePaletteColor.Neutral, 96), + surfaceContainer = ColorRef(CorePaletteColor.Neutral, 94), + surfaceContainerHigh = ColorRef(CorePaletteColor.Neutral, 92), + surfaceContainerHighest = ColorRef(CorePaletteColor.Neutral, 90), + onSurface = ColorRef(CorePaletteColor.Neutral, 0), + onSurfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 0), + outline = ColorRef(CorePaletteColor.NeutralVariant, 20), + outlineVariant = ColorRef(CorePaletteColor.NeutralVariant, 30), + inverseSurface = ColorRef(CorePaletteColor.Neutral, 20), + inverseOnSurface = ColorRef(CorePaletteColor.Neutral, 100), + inversePrimary = ColorRef(CorePaletteColor.Primary, 80), + surfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 90), + surfaceTint = ColorRef(CorePaletteColor.Primary, 20), + background = ColorRef(CorePaletteColor.Neutral, 98), + onBackground = ColorRef(CorePaletteColor.Neutral, 0), + scrim = ColorRef(CorePaletteColor.Neutral, 0), +) + +val HighContrastDarkColorScheme = ColorScheme( + primary = ColorRef(CorePaletteColor.Primary, 95), + onPrimary = ColorRef(CorePaletteColor.Primary, 0), + primaryContainer = ColorRef(CorePaletteColor.Primary, 80), + onPrimaryContainer = ColorRef(CorePaletteColor.Primary, 0), + secondary = ColorRef(CorePaletteColor.Secondary, 95), + onSecondary = ColorRef(CorePaletteColor.Secondary, 0), + secondaryContainer = ColorRef(CorePaletteColor.Secondary, 80), + onSecondaryContainer = ColorRef(CorePaletteColor.Secondary, 0), + tertiary = ColorRef(CorePaletteColor.Tertiary, 95), + onTertiary = ColorRef(CorePaletteColor.Tertiary, 0), + tertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 80), + onTertiaryContainer = ColorRef(CorePaletteColor.Tertiary, 0), + error = ColorRef(CorePaletteColor.Error, 95), + onError = ColorRef(CorePaletteColor.Error, 0), + errorContainer = ColorRef(CorePaletteColor.Error, 80), + onErrorContainer = ColorRef(CorePaletteColor.Error, 0), + surfaceDim = ColorRef(CorePaletteColor.Neutral, 6), + surface = ColorRef(CorePaletteColor.Neutral, 6), + surfaceBright = ColorRef(CorePaletteColor.Neutral, 24), + surfaceContainerLowest = ColorRef(CorePaletteColor.Neutral, 4), + surfaceContainerLow = ColorRef(CorePaletteColor.Neutral, 10), + surfaceContainer = ColorRef(CorePaletteColor.Neutral, 12), + surfaceContainerHigh = ColorRef(CorePaletteColor.Neutral, 17), + surfaceContainerHighest = ColorRef(CorePaletteColor.Neutral, 22), + onSurface = ColorRef(CorePaletteColor.Neutral, 100), + onSurfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 100), + outline = ColorRef(CorePaletteColor.NeutralVariant, 95), + outlineVariant = ColorRef(CorePaletteColor.NeutralVariant, 80), + inverseSurface = ColorRef(CorePaletteColor.Neutral, 90), + inverseOnSurface = ColorRef(CorePaletteColor.Neutral, 0), + inversePrimary = ColorRef(CorePaletteColor.Primary, 20), + surfaceVariant = ColorRef(CorePaletteColor.NeutralVariant, 30), + surfaceTint = ColorRef(CorePaletteColor.Primary, 95), + background = ColorRef(CorePaletteColor.Neutral, 6), + onBackground = ColorRef(CorePaletteColor.Neutral, 100), + scrim = ColorRef(CorePaletteColor.Neutral, 0), +) + +val BlackAndWhiteLightColorScheme = ColorScheme( + primary = StaticColor(0xFF000000.toInt()), + onPrimary = StaticColor(0xFFFFFFFF.toInt()), + primaryContainer = StaticColor(0xFFFFFFFF.toInt()), + onPrimaryContainer = StaticColor(0xFF000000.toInt()), + inversePrimary = StaticColor(0xFFFFFFFF.toInt()), + secondary = StaticColor(0xFF000000.toInt()), + onSecondary = StaticColor(0xFFFFFFFF.toInt()), + secondaryContainer = StaticColor(0xFFFFFFFF.toInt()), + onSecondaryContainer = StaticColor(0xFF000000.toInt()), + tertiary = StaticColor(0xFF000000.toInt()), + onTertiary = StaticColor(0xFFFFFFFF.toInt()), + tertiaryContainer = StaticColor(0xFFFFFFFF.toInt()), + onTertiaryContainer = StaticColor(0xFF000000.toInt()), + background = StaticColor(0xFFFFFFFF.toInt()), + onBackground = StaticColor(0xFF000000.toInt()), + surface = StaticColor(0xFFFFFFFF.toInt()), + onSurface = StaticColor(0xFF000000.toInt()), + surfaceVariant = StaticColor(0xFFFFFFFF.toInt()), + onSurfaceVariant = StaticColor(0xFF000000.toInt()), + inverseSurface = StaticColor(0xFF000000.toInt()), + inverseOnSurface = StaticColor(0xFFFFFFFF.toInt()), + error = null, + onError = null, + errorContainer = null, + onErrorContainer = null, + outline = StaticColor(0xFF000000.toInt()), + surfaceTint = StaticColor(0xFFFFFFFF.toInt()), + outlineVariant = StaticColor(0xFF000000.toInt()), + scrim = StaticColor(0xFF000000.toInt()), + surfaceDim = StaticColor(0xFFFFFFFF.toInt()), + surfaceBright = StaticColor(0xFFFFFFFF.toInt()), + surfaceContainer = StaticColor(0xFFFFFFFF.toInt()), + surfaceContainerHigh = StaticColor(0xFFFFFFFF.toInt()), + surfaceContainerHighest = StaticColor(0xFFFFFFFF.toInt()), + surfaceContainerLow = StaticColor(0xFFFFFFFF.toInt()), + surfaceContainerLowest = StaticColor(0xFFFFFFFF.toInt()), +) + +val BlackAndWhiteDarkColorScheme = ColorScheme( + primary = StaticColor(0xFFFFFFFF.toInt()), + onPrimary = StaticColor(0xFF000000.toInt()), + primaryContainer = StaticColor(0xFF000000.toInt()), + onPrimaryContainer = StaticColor(0xFFFFFFFF.toInt()), + inversePrimary = StaticColor(0xFF000000.toInt()), + secondary = StaticColor(0xFFFFFFFF.toInt()), + onSecondary = StaticColor(0xFF000000.toInt()), + secondaryContainer = StaticColor(0xFF000000.toInt()), + onSecondaryContainer = StaticColor(0xFFFFFFFF.toInt()), + tertiary = StaticColor(0xFFFFFFFF.toInt()), + onTertiary = StaticColor(0xFF000000.toInt()), + tertiaryContainer = StaticColor(0xFF000000.toInt()), + onTertiaryContainer = StaticColor(0xFFFFFFFF.toInt()), + background = StaticColor(0xFF000000.toInt()), + onBackground = StaticColor(0xFFFFFFFF.toInt()), + surface = StaticColor(0xFF000000.toInt()), + onSurface = StaticColor(0xFFFFFFFF.toInt()), + surfaceVariant = StaticColor(0xFF000000.toInt()), + onSurfaceVariant = StaticColor(0xFFFFFFFF.toInt()), + inverseSurface = StaticColor(0xFFFFFFFF.toInt()), + inverseOnSurface = StaticColor(0xFF000000.toInt()), + error = null, + onError = null, + errorContainer = null, + onErrorContainer = null, + outline = StaticColor(0xFFFFFFFF.toInt()), + surfaceTint = StaticColor(0xFFFFFFFF.toInt()), + outlineVariant = StaticColor(0xFFFFFFFF.toInt()), + scrim = StaticColor(0xFFFFFFFF.toInt()), + surfaceDim = StaticColor(0xFF000000.toInt()), + surfaceBright = StaticColor(0xFF000000.toInt()), + surfaceContainer = StaticColor(0xFF000000.toInt()), + surfaceContainerHigh = StaticColor(0xFF000000.toInt()), + surfaceContainerHighest = StaticColor(0xFF000000.toInt()), + surfaceContainerLow = StaticColor(0xFF000000.toInt()), + surfaceContainerLowest = StaticColor(0xFF000000.toInt()), +) \ No newline at end of file diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/Shapes.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/shapes/Shapes.kt similarity index 97% rename from data/themes/src/main/java/de/mm20/launcher2/themes/Shapes.kt rename to data/themes/src/main/java/de/mm20/launcher2/themes/shapes/Shapes.kt index 74c5d284..acf5f899 100644 --- a/data/themes/src/main/java/de/mm20/launcher2/themes/Shapes.kt +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/shapes/Shapes.kt @@ -1,7 +1,8 @@ -package de.mm20.launcher2.themes +package de.mm20.launcher2.themes.shapes import de.mm20.launcher2.database.entities.ShapesEntity import de.mm20.launcher2.serialization.UUIDSerializer +import de.mm20.launcher2.themes.ShapeSerializer import kotlinx.serialization.Serializable import java.util.UUID diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/shapes/ShapesRepository.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/shapes/ShapesRepository.kt new file mode 100644 index 00000000..5b94fbe2 --- /dev/null +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/shapes/ShapesRepository.kt @@ -0,0 +1,129 @@ +package de.mm20.launcher2.themes.shapes + +import android.content.Context +import de.mm20.launcher2.database.AppDatabase +import de.mm20.launcher2.themes.CutShapesId +import de.mm20.launcher2.themes.DefaultThemeId +import de.mm20.launcher2.themes.ExtraRoundShapesId +import de.mm20.launcher2.themes.R +import de.mm20.launcher2.themes.RectShapesId +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import java.util.UUID + +class ShapesRepository( + private val context: Context, + private val database: AppDatabase, +) { + private val scope = CoroutineScope(Dispatchers.IO + Job()) + + fun getAll(): Flow> { + return database.themeDao().getAllShapes().map { + getBuiltIn() + it.map { Shapes(it) } + } + } + + fun get(id: UUID): Flow { + if (id == DefaultThemeId) return flowOf(default) + if (id == ExtraRoundShapesId) return flowOf(extraRound) + if (id == RectShapesId) return flowOf(rect) + if (id == CutShapesId) return flowOf(cut) + return database.themeDao().getShapes(id).map { it?.let { Shapes(it) } } + .flowOn(Dispatchers.Default) + } + + fun create(shapes: Shapes) { + scope.launch { + database.themeDao().insertShapes(shapes.toEntity()) + } + } + + fun update(shapes: Shapes) { + scope.launch { + database.themeDao().updateShapes(shapes.toEntity()) + } + } + + fun delete(shapes: Shapes) { + scope.launch { + database.themeDao().deleteShapes(shapes.id) + } + } + + fun getOrDefault(id: UUID?): Flow { + if (id == null) return flowOf(default) + return get(id).map { it ?: default } + } + + private fun getBuiltIn(): List { + return listOf( + default, + extraRound, + rect, + cut, + ) + } + + private val default: Shapes + get() = Shapes( + id = DefaultThemeId, + builtIn = true, + name = context.getString(R.string.preference_shapes_default), + baseShape = Shape( + corners = CornerStyle.Rounded, + radii = intArrayOf(12, 12, 12, 12), + ) + ) + + private val cut: Shapes + get() = Shapes( + id = CutShapesId, + builtIn = true, + name = context.getString(R.string.preference_cards_shape_cut), + baseShape = Shape( + corners = CornerStyle.Cut, + radii = intArrayOf(12, 12, 12, 12), + ) + ) + + private val extraRound: Shapes + get() = Shapes( + id = ExtraRoundShapesId, + builtIn = true, + name = context.getString(R.string.preference_shapes_extra_round), + baseShape = Shape( + corners = CornerStyle.Rounded, + radii = intArrayOf(24, 24, 24, 24), + ), + extraSmall = Shape( + radii = intArrayOf(4, 4, 4, 4), + ), + extraLarge = Shape( + radii = intArrayOf(36, 36, 36, 36), + ), + extraLargeIncreased = Shape( + radii = intArrayOf(40, 40, 40, 40), + ), + extraExtraLarge = Shape( + radii = intArrayOf(56, 56, 56, 56), + ) + ) + + + private val rect: Shapes + get() = Shapes( + id = RectShapesId, + builtIn = true, + name = context.getString(R.string.preference_shapes_rect), + baseShape = Shape( + corners = CornerStyle.Rounded, + radii = intArrayOf(0, 0, 0, 0), + ) + ) +} diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/transparencies/Transparencies.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/transparencies/Transparencies.kt new file mode 100644 index 00000000..ea228578 --- /dev/null +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/transparencies/Transparencies.kt @@ -0,0 +1,35 @@ +package de.mm20.launcher2.themes.transparencies + +import de.mm20.launcher2.database.entities.TransparenciesEntity +import de.mm20.launcher2.serialization.UUIDSerializer +import kotlinx.serialization.Serializable +import java.util.UUID + +@Serializable +data class Transparencies( + @Serializable(with = UUIDSerializer::class) val id: UUID = UUID.randomUUID(), + val builtIn: Boolean = false, + val name: String, + val background: Float? = null, + val surface: Float? = null, + val elevatedSurface: Float? = null, +) { + constructor(entity: TransparenciesEntity) : this( + id = entity.id, + builtIn = false, + name = entity.name, + background = entity.background, + surface = entity.surface, + elevatedSurface = entity.elevatedSurface, + ) + + internal fun toEntity(): TransparenciesEntity { + return TransparenciesEntity( + id = id, + name = name, + background = background, + surface = surface, + elevatedSurface = elevatedSurface, + ) + } +} \ No newline at end of file diff --git a/data/themes/src/main/java/de/mm20/launcher2/themes/transparencies/TransparenciesRepository.kt b/data/themes/src/main/java/de/mm20/launcher2/themes/transparencies/TransparenciesRepository.kt new file mode 100644 index 00000000..f0d9922c --- /dev/null +++ b/data/themes/src/main/java/de/mm20/launcher2/themes/transparencies/TransparenciesRepository.kt @@ -0,0 +1,87 @@ +package de.mm20.launcher2.themes.transparencies + +import android.content.Context +import de.mm20.launcher2.database.AppDatabase +import de.mm20.launcher2.themes.DefaultThemeId +import de.mm20.launcher2.themes.R +import de.mm20.launcher2.themes.SemiTransparentId +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.launch +import java.util.UUID + +class TransparenciesRepository( + private val context: Context, + private val database: AppDatabase, +) { + private val scope = CoroutineScope(Dispatchers.IO + Job()) + + fun getAll(): Flow> { + return database.themeDao().getAllTransparencies().map { + getBuiltIn() + it.map { Transparencies(it) } + } + } + + fun get(id: UUID): Flow { + if (id == DefaultThemeId) return flowOf(default) + if (id == SemiTransparentId) return flowOf(semiTransparent) + return database.themeDao().getTransparencies(id).map { it?.let { Transparencies(it) } } + } + + fun create(transparencies: Transparencies) { + scope.launch { + database.themeDao().insertTransparencies(transparencies.toEntity()) + } + } + + fun update(transparencies: Transparencies) { + scope.launch { + database.themeDao().updateTransparencies(transparencies.toEntity()) + } + } + + + fun delete(transparencies: Transparencies) { + scope.launch { + database.themeDao().deleteTransparencies(transparencies.id) + } + } + + fun getOrDefault(id: UUID?): Flow { + if (id == null) return flowOf(default) + return get(id).map { it ?: default } + } + + private fun getBuiltIn(): List { + return listOf( + default, + semiTransparent, + ) + } + + private val default: Transparencies + get() = Transparencies( + id = DefaultThemeId, + builtIn = true, + name = context.getString(R.string.preference_transparencies_default), + background = 0.85f, + surface = 1f, + elevatedSurface = 1f, + ) + + + private val semiTransparent: Transparencies + get() = Transparencies( + id = SemiTransparentId, + builtIn = true, + name = context.getString(R.string.preference_transparencies_semi_transparent), + background = 0.40f, + surface = 0.65f, + elevatedSurface = 0.85f, + ) + +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 48e5a912..4aa86a6b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -37,7 +37,7 @@ androidx-constraint-layout = "1.1.1" androidx-emojipicker = "1.5.0" accompanist = "0.36.0" -haze = "1.6.3" +haze = "1.6.4" coil = "2.7.0" koin = "4.0.4" retrofit = "2.11.0"