(feat) transparency schemes
This commit is contained in:
parent
68874f1d87
commit
167be6e34d
@ -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()
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
|
||||
|
||||
@ -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]
|
||||
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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 <T : SavableSearchable> LazyListScope.GridResults(
|
||||
@ -39,7 +39,7 @@ fun <T : SavableSearchable> 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 <T : SavableSearchable> 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 <T : SavableSearchable> 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,
|
||||
|
||||
@ -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 <T : SavableSearchable> 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
|
||||
|
||||
@ -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<SavableSearchable>,
|
||||
@ -47,7 +47,7 @@ fun LazyListScope.SearchFavorites(
|
||||
)
|
||||
.background(
|
||||
MaterialTheme.colorScheme.surface.copy(
|
||||
LocalTransparencyScheme.current.surface
|
||||
MaterialTheme.transparency.surface
|
||||
),
|
||||
MaterialTheme.shapes.medium
|
||||
)
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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",
|
||||
)
|
||||
|
||||
|
||||
@ -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() },
|
||||
) {
|
||||
|
||||
@ -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} (${
|
||||
|
||||
@ -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<TransparencySchemesSettingsRoute> {
|
||||
TransparencySchemesSettingsScreen()
|
||||
}
|
||||
composable<TransparencySchemeSettingsRoute> {
|
||||
val route: TransparencySchemeSettingsRoute = it.toRoute()
|
||||
?: return@composable
|
||||
TransparencySchemeSettingsScreen(UUID.fromString(route.id))
|
||||
}
|
||||
composable("settings/appearance/cards") {
|
||||
CardsSettingsScreen()
|
||||
}
|
||||
|
||||
@ -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),
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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<Transparencies?>(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,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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<ThemeRepository>()
|
||||
private val uiSettings by inject<UiSettings>()
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<List<Colors>> = themeRepository.getAllColors()
|
||||
val selectedColors = uiSettings.colorsId
|
||||
val colors: Flow<List<Colors>> = themeRepository.colors.getAll()
|
||||
|
||||
fun getTheme(id: UUID): Flow<Colors?> {
|
||||
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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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(
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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,
|
||||
) {
|
||||
|
||||
@ -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<Shapes?>(null) }
|
||||
|
||||
@ -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<List<Shapes>> = themeRepository.getAllShapes()
|
||||
val selectedShapesId = uiSettings.shapesId
|
||||
val shapes: Flow<List<Shapes>> = themeRepository.shapes.getAll()
|
||||
|
||||
fun getShapes(id: UUID): Flow<Shapes?> {
|
||||
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)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<Transparencies?>(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))
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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<List<Transparencies>> = themeRepository.transparencies.getAll()
|
||||
|
||||
fun getTransparencies(id: UUID): Flow<Transparencies?> {
|
||||
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)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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,
|
||||
|
||||
@ -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<Int> {
|
||||
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(),
|
||||
)
|
||||
}
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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) }
|
||||
val LocalTransparencyScheme = compositionLocalOf { TransparencyScheme(background = 0.85f, surface = 1f, elevatedSurface = 1f) }
|
||||
|
||||
val MaterialTheme.transparency
|
||||
@Composable
|
||||
get() = LocalTransparencyScheme.current
|
||||
@ -439,6 +439,9 @@
|
||||
<string name="preference_shapes_extra_round">Extra round</string>
|
||||
<string name="preference_shapes_rect">Rectangular</string>
|
||||
<string name="preference_shapes_base">Base shape</string>
|
||||
<string name="preference_screen_transparencies">Transparency</string>
|
||||
<string name="preference_transparencies_default">Default</string>
|
||||
<string name="preference_transparencies_semi_transparent">Semi-transparent</string>
|
||||
<string name="preference_font">Font</string>
|
||||
<string name="preference_font_system">System default</string>
|
||||
<string name="preference_screen_about">About</string>
|
||||
@ -821,8 +824,8 @@
|
||||
<string name="note_widget_file_write_error_description">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.</string>
|
||||
<string name="confirmation_delete_color_scheme">Do you really want to delete the color scheme %1$s\?</string>
|
||||
<string name="confirmation_delete_shapes_scheme">Do you really want to delete the shape scheme %1$s?</string>
|
||||
<string name="new_color_scheme_name">New color scheme</string>
|
||||
<string name="new_shapes_name">New shapes</string>
|
||||
<string name="confirmation_delete_transparencies_scheme">Do you really want to delete the transparency scheme %1$s?</string>
|
||||
<string name="new_theme_name">(untitled)</string>
|
||||
<string name="theme_color_scheme_system_default">Use system default</string>
|
||||
<string name="theme_color_scheme_autogenerate">From primary color</string>
|
||||
<string name="theme_color_scheme_palette_color">Palette</string>
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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<List<ShapesEntity>>
|
||||
|
||||
@Query("SELECT * FROM Transparencies")
|
||||
fun getAllTransparencies(): Flow<List<TransparenciesEntity>>
|
||||
|
||||
@Query("SELECT * FROM Theme WHERE id = :id LIMIT 1")
|
||||
fun getColors(id: UUID): Flow<ColorsEntity?>
|
||||
|
||||
@Query("SELECT * FROM Shapes WHERE id = :id LIMIT 1")
|
||||
fun getShapes(id: UUID): Flow<ShapesEntity?>
|
||||
|
||||
@Query("SELECT * FROM Transparencies WHERE id = :id LIMIT 1")
|
||||
fun getTransparencies(id: UUID): Flow<TransparenciesEntity?>
|
||||
|
||||
@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<ColorsEntity>)
|
||||
|
||||
@Insert
|
||||
fun insertAllShapes(shapes: List<ShapesEntity>)
|
||||
|
||||
@Insert
|
||||
fun insertAllTransparencies(transparencies: List<TransparenciesEntity>)
|
||||
}
|
||||
@ -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?,
|
||||
)
|
||||
@ -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()
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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<Color>(
|
||||
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<Color>(
|
||||
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<Color>(
|
||||
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<Color>(
|
||||
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<Color?>(
|
||||
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<Color?>(
|
||||
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()),
|
||||
)
|
||||
val SemiTransparentId = UUID(0L, 1L)
|
||||
@ -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
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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.
|
||||
*/
|
||||
|
||||
@ -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<List<Colors>> {
|
||||
return database.themeDao().getAllColors().map {
|
||||
getBuiltInColors() + it.map { Colors(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun getColors(id: UUID): Flow<Colors?> {
|
||||
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<Colors> {
|
||||
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<Colors> {
|
||||
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<List<Shapes>> {
|
||||
return database.themeDao().getAllShapes().map {
|
||||
getBuiltInShapes() + it.map { Shapes(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun getShapes(id: UUID): Flow<Shapes?> {
|
||||
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<Shapes> {
|
||||
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<Shapes> {
|
||||
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() })
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -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
|
||||
@ -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<List<Colors>> {
|
||||
return database.themeDao().getAllColors().map {
|
||||
getBuiltIn() + it.map { Colors(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun get(id: UUID): Flow<Colors?> {
|
||||
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<Colors> {
|
||||
if (id == null) return flowOf(default)
|
||||
return get(id).map { it ?: default }
|
||||
}
|
||||
|
||||
private fun getBuiltIn(): List<Colors> {
|
||||
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,
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,235 @@
|
||||
package de.mm20.launcher2.themes.colors
|
||||
|
||||
val DefaultLightColorScheme = ColorScheme<Color>(
|
||||
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<Color>(
|
||||
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<Color>(
|
||||
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<Color>(
|
||||
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<Color?>(
|
||||
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<Color?>(
|
||||
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()),
|
||||
)
|
||||
@ -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
|
||||
|
||||
@ -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<List<Shapes>> {
|
||||
return database.themeDao().getAllShapes().map {
|
||||
getBuiltIn() + it.map { Shapes(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun get(id: UUID): Flow<Shapes?> {
|
||||
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<Shapes> {
|
||||
if (id == null) return flowOf(default)
|
||||
return get(id).map { it ?: default }
|
||||
}
|
||||
|
||||
private fun getBuiltIn(): List<Shapes> {
|
||||
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),
|
||||
)
|
||||
)
|
||||
}
|
||||
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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<List<Transparencies>> {
|
||||
return database.themeDao().getAllTransparencies().map {
|
||||
getBuiltIn() + it.map { Transparencies(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun get(id: UUID): Flow<Transparencies?> {
|
||||
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<Transparencies> {
|
||||
if (id == null) return flowOf(default)
|
||||
return get(id).map { it ?: default }
|
||||
}
|
||||
|
||||
private fun getBuiltIn(): List<Transparencies> {
|
||||
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,
|
||||
)
|
||||
|
||||
}
|
||||
@ -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"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user