diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
index 275c5bef..bb270b80 100644
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ b/.idea/inspectionProfiles/Project_Default.xml
@@ -67,6 +67,7 @@
+
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheet.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheet.kt
index de72b888..da651816 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheet.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheet.kt
@@ -40,7 +40,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
-import de.mm20.launcher2.themes.Theme
+import de.mm20.launcher2.themes.Colors
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.component.BottomSheetDialog
import de.mm20.launcher2.ui.component.LargeMessage
@@ -63,7 +63,7 @@ fun ImportThemeSheet(
viewModel.readTheme(context, uri)
}
- val theme by viewModel.theme
+ val theme by viewModel.colors
val error by viewModel.error
var apply by viewModel.apply
@@ -132,13 +132,13 @@ fun ImportThemeSheet(
@Composable
fun ThemePreview(
- theme: Theme,
+ colors: Colors,
modifier: Modifier = Modifier,
) {
val darkMode = LocalDarkTheme.current
var darkTheme by remember { mutableStateOf(darkMode) }
- val colorScheme = if (darkTheme) darkColorSchemeOf(theme) else lightColorSchemeOf(theme)
+ val colorScheme = if (darkTheme) darkColorSchemeOf(colors) else lightColorSchemeOf(colors)
Column(modifier = modifier) {
Row(
@@ -146,7 +146,7 @@ fun ThemePreview(
verticalAlignment = Alignment.CenterVertically,
) {
Text(
- text = theme.name,
+ text = colors.name,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.weight(1f),
color = MaterialTheme.colorScheme.onSurfaceVariant
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheetVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheetVM.kt
index f3a6e8c8..bd842022 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheetVM.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/common/ImportThemeSheetVM.kt
@@ -5,11 +5,11 @@ import android.net.Uri
import androidx.compose.runtime.mutableStateOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import de.mm20.launcher2.preferences.ThemeDescriptor
+import de.mm20.launcher2.preferences.ColorsDescriptor
import de.mm20.launcher2.preferences.ui.UiSettings
-import de.mm20.launcher2.themes.Theme
+import de.mm20.launcher2.themes.Colors
import de.mm20.launcher2.themes.ThemeRepository
-import de.mm20.launcher2.themes.fromJson
+import de.mm20.launcher2.themes.fromLegacyJson
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.koin.core.component.KoinComponent
@@ -20,12 +20,12 @@ class ImportThemeSheetVM : ViewModel(), KoinComponent {
private val themeRepository: ThemeRepository by inject()
private val uiSettings: UiSettings by inject()
- val theme = mutableStateOf(null)
+ val colors = mutableStateOf(null)
val error = mutableStateOf(false)
val apply = mutableStateOf(false)
fun import() {
- val theme = theme.value
+ val theme = colors.value
val apply = apply.value
if (theme != null) {
viewModelScope.launch {
@@ -37,30 +37,30 @@ class ImportThemeSheetVM : ViewModel(), KoinComponent {
fun readTheme(context: Context, uri: Uri) {
error.value = false
- theme.value = null
+ colors.value = null
apply.value = true
viewModelScope.launch(Dispatchers.IO) {
val inputStream =
context.contentResolver.openInputStream(uri) ?: return@launch
- val theme = inputStream.use {
+ val colors = inputStream.use {
val json = it.readBytes().toString(Charsets.UTF_8)
try {
- Theme.fromJson(json)
+ Colors.fromLegacyJson(json)
} catch (e: IllegalArgumentException) {
null
}
}
- this@ImportThemeSheetVM.theme.value = theme
- if (theme == null) {
+ this@ImportThemeSheetVM.colors.value = colors
+ if (colors == null) {
error.value = true
}
}
}
- private fun importTheme(theme: Theme, apply: Boolean) {
- themeRepository.createTheme(theme)
+ private fun importTheme(colors: Colors, apply: Boolean) {
+ themeRepository.createColors(colors)
if (apply) {
- uiSettings.setTheme(ThemeDescriptor.Custom(theme.id.toString()))
+ uiSettings.setColors(ColorsDescriptor.Custom(colors.id.toString()))
}
}
}
\ No newline at end of file
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/location/LocationItem.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/location/LocationItem.kt
index f7b07a00..d475c918 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/location/LocationItem.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/location/LocationItem.kt
@@ -21,7 +21,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
-import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
@@ -49,6 +48,9 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.rounded.ArrowBack
import androidx.compose.material.icons.automirrored.rounded.NavigateNext
import androidx.compose.material.icons.automirrored.rounded.OpenInNew
+import androidx.compose.material.icons.outlined.CreditCardOff
+import androidx.compose.material.icons.outlined.CreditScore
+import androidx.compose.material.icons.outlined.Toll
import androidx.compose.material.icons.rounded.AirplanemodeActive
import androidx.compose.material.icons.rounded.BugReport
import androidx.compose.material.icons.rounded.Commute
@@ -92,9 +94,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.toArgb
-import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
-import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextAlign
@@ -102,7 +102,6 @@ import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.IntRect
import androidx.compose.ui.unit.IntSize
-import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType
import androidx.compose.ui.unit.dp
@@ -115,6 +114,7 @@ import blend.Blend.harmonize
import coil.compose.AsyncImage
import de.mm20.launcher2.i18n.R
import de.mm20.launcher2.icons.CableCar
+import de.mm20.launcher2.icons.TollOff
import de.mm20.launcher2.ktx.tryStartActivity
import de.mm20.launcher2.search.Location
import de.mm20.launcher2.search.isOpen
@@ -124,6 +124,7 @@ import de.mm20.launcher2.search.location.LineNameComparator
import de.mm20.launcher2.search.location.LineType
import de.mm20.launcher2.search.location.OpeningHours
import de.mm20.launcher2.search.location.OpeningSchedule
+import de.mm20.launcher2.search.location.PaymentMethod
import de.mm20.launcher2.search.location.isNotEmpty
import de.mm20.launcher2.ui.base.LocalTime
import de.mm20.launcher2.ui.component.DefaultToolbarAction
@@ -229,24 +230,33 @@ fun LocationItem(
)
val formattedDistance =
distance?.metersToLocalizedString(context, imperialUnits)
- val isOpenString = location.openingSchedule?.isOpen()
- ?.let { stringResource(if (it) R.string.location_open else R.string.location_closed) }
- val sublabel = listOf(location.category, formattedDistance, isOpenString)
+ val sublabel = listOf(location.category, formattedDistance)
.fastFilterNotNull()
.joinToString(" • ")
+ val isOpenString = location.openingSchedule?.isOpen()
+ ?.let { stringResource(if (it) R.string.location_open else R.string.location_closed) }
- if (sublabel.isNotBlank()) {
- Text(
- sublabel,
- style = MaterialTheme.typography.bodySmall,
- color = MaterialTheme.colorScheme.secondary,
- modifier = Modifier
- .padding(top = 2.dp)
- .sharedElement(
- rememberSharedContentState("sublabel"),
- this@AnimatedContent
- )
- )
+ Row(modifier = Modifier.padding(top = 2.dp)) {
+ if (sublabel.isNotBlank()) {
+ Text(
+ sublabel,
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.secondary,
+ modifier = Modifier
+ .sharedElement(
+ rememberSharedContentState("sublabel"),
+ this@AnimatedContent
+ )
+ )
+ }
+ if (!isOpenString.isNullOrBlank()) {
+ Text(
+ " • $isOpenString",
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.secondary,
+ modifier = Modifier.animateEnterExit()
+ )
+ }
}
}
Compass(
@@ -324,28 +334,55 @@ fun LocationItem(
this@AnimatedContent
)
)
- val category = location.category
- val formattedDistance = distance?.metersToLocalizedString(
- context, imperialUnits
- )
- if (category != null || formattedDistance != null) {
- Text(
- when {
- category != null && formattedDistance != null -> "$category • $formattedDistance"
-
- category != null -> category
- formattedDistance != null -> formattedDistance
- else -> ""
- },
- style = MaterialTheme.typography.bodySmall,
- color = MaterialTheme.colorScheme.secondary,
- modifier = Modifier
- .padding(top = 2.dp)
- .sharedElement(
- rememberSharedContentState("sublabel"),
- this@AnimatedContent
- )
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier.padding(top = 2.dp)
+ ) {
+ val category = location.category
+ val acceptedPaymentMethods = location.acceptedPaymentMethods
+ val formattedDistance = distance?.metersToLocalizedString(
+ context, imperialUnits
)
+ if (category != null || formattedDistance != null) {
+ Text(
+ when {
+ category != null && formattedDistance != null -> "$category • $formattedDistance"
+
+ category != null -> category
+ formattedDistance != null -> formattedDistance
+ else -> ""
+ },
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.secondary,
+ modifier = Modifier
+ .sharedElement(
+ rememberSharedContentState("sublabel"),
+ this@AnimatedContent
+ )
+ )
+ }
+ if (!acceptedPaymentMethods.isNullOrEmpty()) {
+ Text(
+ " • ",
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.secondary,
+ modifier = Modifier.animateEnterExit()
+ )
+ for ((method, available) in acceptedPaymentMethods) {
+ Icon(
+ when (method) {
+ PaymentMethod.Cash -> if (available) Icons.Outlined.Toll else Icons.Outlined.TollOff
+ PaymentMethod.Card -> if (available) Icons.Outlined.CreditScore else Icons.Outlined.CreditCardOff
+ },
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.secondary,
+ modifier = Modifier
+ .size(14.5.dp)
+ .padding(end = 2.dp)
+ .animateEnterExit()
+ )
+ }
+ }
}
if (location.userRating != null) {
RatingBar(
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/location/MapTiles.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/location/MapTiles.kt
index f068e668..da080a88 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/location/MapTiles.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/launcher/search/location/MapTiles.kt
@@ -75,6 +75,7 @@ import de.mm20.launcher2.search.location.Departure
import de.mm20.launcher2.search.location.LineType
import de.mm20.launcher2.search.location.LocationIcon
import de.mm20.launcher2.search.location.OpeningSchedule
+import de.mm20.launcher2.search.location.PaymentMethod
import de.mm20.launcher2.ui.ktx.DegreesConverter
import de.mm20.launcher2.ui.ktx.contrast
import de.mm20.launcher2.ui.ktx.hue
@@ -505,6 +506,9 @@ private object MockLocation : Location {
override val openingSchedule: OpeningSchedule =
OpeningSchedule.TwentyFourSeven
+ override val acceptedPaymentMethods: Map?
+ get() = mapOf(PaymentMethod.Card to true, PaymentMethod.Cash to false)
+
override val websiteUrl: String = "https://en.wikipedia.org/wiki/Brandenburg_Gate"
override val phoneNumber: String = "+49 1234567"
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/modifier/FadingEdges.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/modifier/FadingEdges.kt
index 1198eaa7..cda6ab17 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/modifier/FadingEdges.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/modifier/FadingEdges.kt
@@ -28,7 +28,7 @@ fun Modifier.verticalFadingEdges(
if(!enabled) return this
if (top == 0.dp && bottom == 0.dp) return this
- return this then drawWithContent {
+ return drawWithContent {
val topColors = if (top > 0.dp) createColors(
1f - amount,
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/modifier/Scrims.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/modifier/Scrims.kt
index bdffbec0..46fb2bad 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/modifier/Scrims.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/modifier/Scrims.kt
@@ -27,7 +27,7 @@ fun Modifier.verticalScrims(
if (!enabled) return this
if (top == 0.dp && bottom == 0.dp) return this
- return this then drawWithCache {
+ return drawWithCache {
onDrawWithContent {
val topColors = if (top > 0.dp) createColors(
1f - amount,
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt
index cdbf0985..e9df7668 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/SettingsActivity.kt
@@ -42,8 +42,8 @@ import de.mm20.launcher2.ui.settings.buildinfo.BuildInfoSettingsScreen
import de.mm20.launcher2.ui.settings.calendarsearch.CalendarProviderSettingsScreen
import de.mm20.launcher2.ui.settings.calendarsearch.CalendarSearchSettingsScreen
import de.mm20.launcher2.ui.settings.cards.CardsSettingsScreen
-import de.mm20.launcher2.ui.settings.colorscheme.ThemeSettingsScreen
-import de.mm20.launcher2.ui.settings.colorscheme.ThemesSettingsScreen
+import de.mm20.launcher2.ui.settings.colorscheme.ColorSchemeSettingsScreen
+import de.mm20.launcher2.ui.settings.colorscheme.ColorSchemesSettingsScreen
import de.mm20.launcher2.ui.settings.contacts.ContactsSettingsScreen
import de.mm20.launcher2.ui.settings.crashreporter.CrashReportScreen
import de.mm20.launcher2.ui.settings.crashreporter.CrashReporterScreen
@@ -69,6 +69,8 @@ import de.mm20.launcher2.ui.settings.plugins.PluginSettingsScreen
import de.mm20.launcher2.ui.settings.plugins.PluginsSettingsScreen
import de.mm20.launcher2.ui.settings.search.SearchSettingsScreen
import de.mm20.launcher2.ui.settings.searchactions.SearchActionsSettingsScreen
+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.unitconverter.UnitConverterHelpSettingsScreen
@@ -160,11 +162,11 @@ class SettingsActivity : BaseActivity() {
composable("settings/icons") {
IconsSettingsScreen()
}
- composable("settings/appearance/themes") {
- ThemesSettingsScreen()
+ composable("settings/appearance/colors") {
+ ColorSchemesSettingsScreen()
}
composable(
- "settings/appearance/themes/{id}",
+ "settings/appearance/colors/{id}",
arguments = listOf(navArgument("id") {
nullable = false
})
@@ -172,7 +174,21 @@ class SettingsActivity : BaseActivity() {
val id = it.arguments?.getString("id")?.let {
UUID.fromString(it)
} ?: return@composable
- ThemeSettingsScreen(id)
+ ColorSchemeSettingsScreen(id)
+ }
+ composable("settings/appearance/shapes") {
+ ShapeSchemesSettingsScreen()
+ }
+ composable(
+ "settings/appearance/shapes/{id}",
+ arguments = listOf(navArgument("id") {
+ nullable = false
+ })
+ ) {
+ val id = it.arguments?.getString("id")?.let {
+ UUID.fromString(it)
+ } ?: return@composable
+ ShapeSchemeSettingsScreen(id)
}
composable("settings/appearance/cards") {
CardsSettingsScreen()
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt
index be178f0d..ca99dfe5 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreen.kt
@@ -1,5 +1,10 @@
package de.mm20.launcher2.ui.settings.appearance
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.CropSquare
+import androidx.compose.material.icons.rounded.Palette
+import androidx.compose.material.icons.rounded.TextFields
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
@@ -45,12 +50,17 @@ fun AppearanceSettingsScreen() {
viewModel.setColorScheme(newValue)
}
)
+ }
+ }
+ item {
+ PreferenceCategory {
Preference(
title = stringResource(id = R.string.preference_screen_colors),
summary = themeName,
onClick = {
- navController?.navigate("settings/appearance/themes")
- }
+ navController?.navigate("settings/appearance/colors")
+ },
+ icon = Icons.Rounded.Palette,
)
val font by viewModel.font.collectAsState()
ListPreference(
@@ -68,7 +78,16 @@ fun AppearanceSettingsScreen() {
getTypography(context, it.value)
}
Text(it.first, style = typography.titleMedium)
- }
+ },
+ icon = Icons.Rounded.TextFields,
+ )
+ Preference(
+ title = stringResource(id = R.string.preference_screen_shapes),
+ summary = themeName,
+ onClick = {
+ navController?.navigate("settings/appearance/shapes")
+ },
+ icon = Icons.Rounded.CropSquare,
)
Preference(
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt
index 4ffa4c73..49da7338 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/appearance/AppearanceSettingsScreenVM.kt
@@ -2,7 +2,6 @@ package de.mm20.launcher2.ui.settings.appearance
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
-import de.mm20.launcher2.icons.IconService
import de.mm20.launcher2.preferences.ColorScheme
import de.mm20.launcher2.preferences.Font
import de.mm20.launcher2.preferences.ui.UiSettings
@@ -26,8 +25,8 @@ class AppearanceSettingsScreenVM : ViewModel(), KoinComponent {
uiSettings.setColorScheme(colorScheme)
}
- val themeName = uiSettings.theme.flatMapLatest {
- themeRepository.getThemeOrDefault(it)
+ val themeName = uiSettings.colors.flatMapLatest {
+ themeRepository.getColorsOrDefault(it)
}.map {
it.name
}
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreen.kt
index 1d8b9f11..30b2db06 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreen.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreen.kt
@@ -47,28 +47,6 @@ fun CardsSettingsScreen() {
}
item {
PreferenceCategory {
- ListPreference(
- icon = Icons.Rounded.Rectangle,
- title = stringResource(R.string.preference_cards_shape),
- items = listOf(
- stringResource(R.string.preference_cards_shape_rounded) to SurfaceShape.Rounded,
- stringResource(R.string.preference_cards_shape_cut) to SurfaceShape.Cut,
- ),
- value = cardStyle.shape,
- onValueChanged = {
- viewModel.setShape(it)
- })
- SliderPreference(
- title = stringResource(R.string.preference_cards_corner_radius),
- icon = Icons.Rounded.RoundedCorner,
- value = cardStyle.cornerRadius,
- min = 0,
- max = 24,
- step = 1,
- onValueChanged = {
- viewModel.setRadius(it)
- }
- )
SliderPreference(
title = stringResource(R.string.preference_cards_opacity),
icon = Icons.Rounded.Opacity,
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreenVM.kt
index d5721b59..243674e7 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreenVM.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/cards/CardsSettingsScreenVM.kt
@@ -1,7 +1,6 @@
package de.mm20.launcher2.ui.settings.cards
import androidx.lifecycle.ViewModel
-import de.mm20.launcher2.preferences.SurfaceShape
import de.mm20.launcher2.preferences.ui.UiSettings
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
@@ -15,15 +14,8 @@ class CardsSettingsScreenVM : ViewModel(), KoinComponent {
uiSettings.setCardOpacity(opacity)
}
- fun setRadius(radius: Int) {
- uiSettings.setCardRadius(radius)
- }
-
fun setBorderWidth(borderWidth: Int) {
uiSettings.setCardBorderWidth(borderWidth)
}
- fun setShape(shape: SurfaceShape) {
- uiSettings.setCardShape(shape)
- }
}
\ No newline at end of file
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemePreferenceCategory.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemePreferenceCategory.kt
similarity index 86%
rename from app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemePreferenceCategory.kt
rename to app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemePreferenceCategory.kt
index 97ad7496..3258550f 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemePreferenceCategory.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemePreferenceCategory.kt
@@ -1,16 +1,11 @@
package de.mm20.launcher2.ui.settings.colorscheme
import androidx.compose.foundation.horizontalScroll
-import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.FlowRow
-import androidx.compose.foundation.layout.FlowRowScope
-import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.DarkMode
@@ -31,7 +26,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
@Composable
-fun ThemePreferenceCategory(
+fun ColorSchemePreferenceCategory(
title: String,
previewColorScheme: ColorScheme,
darkMode: Boolean,
@@ -95,15 +90,8 @@ fun ThemePreferenceCategory(
}
}
- Row(
- modifier = Modifier
- .fillMaxWidth()
- .horizontalScroll(rememberScrollState())
- .padding(16.dp)
- ) {
- colorPreferences()
- }
- HorizontalDivider()
+ colorPreferences()
+ HorizontalDivider(modifier = Modifier.padding(top = 8.dp))
}
}
\ No newline at end of file
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemeSettingsScreen.kt
similarity index 87%
rename from app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeSettingsScreen.kt
rename to app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemeSettingsScreen.kt
index 6784158b..c61d6437 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeSettingsScreen.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemeSettingsScreen.kt
@@ -1,20 +1,16 @@
package de.mm20.launcher2.ui.settings.colorscheme
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
-import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.wrapContentWidth
-import androidx.compose.foundation.rememberScrollState
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.rounded.Add
import androidx.compose.material.icons.rounded.Edit
import androidx.compose.material.icons.rounded.Lock
import androidx.compose.material.icons.rounded.OpenInNew
@@ -60,6 +56,7 @@ import androidx.compose.ui.unit.dp
import androidx.core.content.ContextCompat
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
+import de.mm20.launcher2.badges.Badge
import de.mm20.launcher2.icons.ColorLayer
import de.mm20.launcher2.icons.StaticLauncherIcon
import de.mm20.launcher2.icons.TintedIconLayer
@@ -78,8 +75,8 @@ import palettes.CorePalette
import java.util.UUID
@Composable
-fun ThemeSettingsScreen(themeId: UUID) {
- val viewModel: ThemesSettingsScreenVM = viewModel()
+fun ColorSchemeSettingsScreen(themeId: UUID) {
+ val viewModel: ColorSchemesSettingsScreenVM = viewModel()
val context = LocalContext.current
val dark = LocalDarkTheme.current
@@ -107,7 +104,13 @@ fun ThemeSettingsScreen(themeId: UUID) {
var name by remember(theme) { mutableStateOf(theme?.name ?: "") }
AlertDialog(
onDismissRequest = { editName = false },
- text = { OutlinedTextField(value = name, onValueChange = { name = it }, singleLine = true) },
+ text = {
+ OutlinedTextField(
+ value = name,
+ onValueChange = { name = it },
+ singleLine = true
+ )
+ },
confirmButton = {
Button(
onClick = {
@@ -146,135 +149,129 @@ fun ThemeSettingsScreen(themeId: UUID) {
stringResource(R.string.preference_custom_colors_corepalette),
style = MaterialTheme.typography.titleSmall,
color = MaterialTheme.colorScheme.secondary,
- modifier = Modifier.padding(horizontal = 16.dp),
+ modifier = Modifier.padding(bottom = 16.dp, start = 16.dp, end = 16.dp),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
)
- Row(
- modifier = Modifier
- .horizontalScroll(rememberScrollState())
- .padding(16.dp)
- ) {
- CorePaletteColorPreference(
- title = "Primary",
- value = theme?.corePalette?.primary,
- onValueChange = {
- viewModel.updateTheme(
- theme!!.copy(
- corePalette = theme!!.corePalette.copy(
- primary = it
- )
+ CorePaletteColorPreference(
+ title = "Primary",
+ value = theme?.corePalette?.primary,
+ onValueChange = {
+ viewModel.updateTheme(
+ theme!!.copy(
+ corePalette = theme!!.corePalette.copy(
+ primary = it
)
)
- },
- defaultValue = systemPalette.primary,
- modifier = Modifier.padding(end = 12.dp),
- )
- CorePaletteColorPreference(
- title = "Secondary",
- value = theme?.corePalette?.secondary,
- onValueChange = {
- viewModel.updateTheme(
- theme!!.copy(
- corePalette = theme!!.corePalette.copy(
- secondary = it
- )
+ )
+ },
+ defaultValue = systemPalette.primary,
+ modifier = Modifier.padding(end = 12.dp),
+ )
+ CorePaletteColorPreference(
+ title = "Secondary",
+ value = theme?.corePalette?.secondary,
+ onValueChange = {
+ viewModel.updateTheme(
+ theme!!.copy(
+ corePalette = theme!!.corePalette.copy(
+ secondary = it
)
)
- },
- defaultValue = systemPalette.secondary,
- modifier = Modifier.padding(end = 12.dp),
- autoGenerate = {
- theme!!.corePalette.primary?.let {
- CorePalette.of(it).a2.keyColor.toInt()
- }
- },
- )
- CorePaletteColorPreference(
- title = "Tertiary",
- value = theme?.corePalette?.tertiary,
- onValueChange = {
- viewModel.updateTheme(
- theme!!.copy(
- corePalette = theme!!.corePalette.copy(
- tertiary = it
- )
+ )
+ },
+ defaultValue = systemPalette.secondary,
+ modifier = Modifier.padding(end = 12.dp),
+ autoGenerate = {
+ theme!!.corePalette.primary?.let {
+ CorePalette.of(it).a2.keyColor.toInt()
+ }
+ },
+ )
+ CorePaletteColorPreference(
+ title = "Tertiary",
+ value = theme?.corePalette?.tertiary,
+ onValueChange = {
+ viewModel.updateTheme(
+ theme!!.copy(
+ corePalette = theme!!.corePalette.copy(
+ tertiary = it
)
)
- },
- defaultValue = systemPalette.tertiary,
- modifier = Modifier.padding(end = 12.dp),
- autoGenerate = {
- theme!!.corePalette.primary?.let {
- CorePalette.of(it).a3.keyColor.toInt()
- }
- },
- )
- CorePaletteColorPreference(
- title = "Neutral",
- value = theme?.corePalette?.neutral,
- onValueChange = {
- viewModel.updateTheme(
- theme!!.copy(
- corePalette = theme!!.corePalette.copy(
- neutral = it
- )
+ )
+ },
+ defaultValue = systemPalette.tertiary,
+ modifier = Modifier.padding(end = 12.dp),
+ autoGenerate = {
+ theme!!.corePalette.primary?.let {
+ CorePalette.of(it).a3.keyColor.toInt()
+ }
+ },
+ )
+ CorePaletteColorPreference(
+ title = "Neutral",
+ value = theme?.corePalette?.neutral,
+ onValueChange = {
+ viewModel.updateTheme(
+ theme!!.copy(
+ corePalette = theme!!.corePalette.copy(
+ neutral = it
)
)
- },
- defaultValue = systemPalette.neutral,
- modifier = Modifier.padding(end = 12.dp),
- autoGenerate = {
- theme!!.corePalette.primary?.let {
- CorePalette.of(it).n1.keyColor.toInt()
- }
- },
- )
- CorePaletteColorPreference(
- title = "Neutral Variant",
- value = theme?.corePalette?.neutralVariant,
- onValueChange = {
- viewModel.updateTheme(
- theme!!.copy(
- corePalette = theme!!.corePalette.copy(
- neutralVariant = it
- )
+ )
+ },
+ defaultValue = systemPalette.neutral,
+ modifier = Modifier.padding(end = 12.dp),
+ autoGenerate = {
+ theme!!.corePalette.primary?.let {
+ CorePalette.of(it).n1.keyColor.toInt()
+ }
+ },
+ )
+ CorePaletteColorPreference(
+ title = "Neutral Variant",
+ value = theme?.corePalette?.neutralVariant,
+ onValueChange = {
+ viewModel.updateTheme(
+ theme!!.copy(
+ corePalette = theme!!.corePalette.copy(
+ neutralVariant = it
)
)
- },
- defaultValue = systemPalette.neutralVariant,
- modifier = Modifier.padding(end = 12.dp),
- autoGenerate = {
- theme!!.corePalette.primary?.let {
- CorePalette.of(it).n2.keyColor.toInt()
- }
- },
- )
- CorePaletteColorPreference(
- title = "Error",
- value = theme?.corePalette?.error,
- onValueChange = {
- viewModel.updateTheme(
- theme!!.copy(
- corePalette = theme!!.corePalette.copy(
- error = it
- )
+ )
+ },
+ defaultValue = systemPalette.neutralVariant,
+ modifier = Modifier.padding(end = 12.dp),
+ autoGenerate = {
+ theme!!.corePalette.primary?.let {
+ CorePalette.of(it).n2.keyColor.toInt()
+ }
+ },
+ )
+ CorePaletteColorPreference(
+ title = "Error",
+ value = theme?.corePalette?.error,
+ onValueChange = {
+ viewModel.updateTheme(
+ theme!!.copy(
+ corePalette = theme!!.corePalette.copy(
+ error = it
)
)
- },
- defaultValue = systemPalette.error,
- autoGenerate = {
- theme!!.corePalette.primary?.let {
- CorePalette.of(it).error.keyColor.toInt()
- }
- },
- )
- }
- HorizontalDivider()
+ )
+ },
+ defaultValue = systemPalette.error,
+ autoGenerate = {
+ theme!!.corePalette.primary?.let {
+ CorePalette.of(it).error.keyColor.toInt()
+ }
+ },
+ )
+ HorizontalDivider(modifier = Modifier.padding(top = 8.dp))
}
}
item {
- ThemePreferenceCategory(
+ ColorSchemePreferenceCategory(
title = "Primary colors",
previewColorScheme = previewColorScheme,
darkMode = previewDarkTheme,
@@ -302,7 +299,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.primary,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "On Primary",
@@ -326,7 +322,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.onPrimary,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Primary Container",
@@ -350,7 +345,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.primaryContainer,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "On Primary Container",
@@ -374,7 +368,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.onPrimaryContainer,
- modifier = Modifier.padding(end = 12.dp),
)
},
@@ -419,7 +412,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
}
}
item {
- ThemePreferenceCategory(
+ ColorSchemePreferenceCategory(
title = "Secondary colors",
previewColorScheme = previewColorScheme,
darkMode = previewDarkTheme,
@@ -447,7 +440,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.secondary,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "On Secondary",
@@ -471,7 +463,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.onSecondary,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Secondary Container",
@@ -495,7 +486,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.secondaryContainer,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "On Secondary Container",
@@ -519,7 +509,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.onSecondaryContainer,
- modifier = Modifier.padding(end = 12.dp),
)
},
) {
@@ -557,7 +546,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
}
}
item {
- ThemePreferenceCategory(
+ ColorSchemePreferenceCategory(
title = "Tertiary colors",
previewColorScheme = previewColorScheme,
darkMode = previewDarkTheme,
@@ -585,7 +574,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.tertiary,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "On Tertiary",
@@ -609,7 +597,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.onTertiary,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Tertiary Container",
@@ -633,7 +620,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.tertiaryContainer,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "On Tertiary Container",
@@ -657,14 +643,17 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.onTertiaryContainer,
- modifier = Modifier.padding(end = 12.dp),
)
},
) {
+ ShapedLauncherIcon(
+ badge = { Badge() },
+ size = 48.dp,
+ )
}
}
item {
- ThemePreferenceCategory(
+ ColorSchemePreferenceCategory(
title = "Surface colors",
previewColorScheme = previewColorScheme,
darkMode = previewDarkTheme,
@@ -716,7 +705,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.surface,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Surface Bright",
@@ -740,7 +728,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.surfaceBright,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Surface Tint",
@@ -764,7 +751,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.surfaceTint,
- modifier = Modifier.padding(end = 12.dp),
)
},
) {
@@ -824,7 +810,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
}
}
item {
- ThemePreferenceCategory(
+ ColorSchemePreferenceCategory(
title = "Surface container colors",
previewColorScheme = previewColorScheme,
darkMode = previewDarkTheme,
@@ -852,7 +838,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.surfaceContainerLowest,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Surface Container Low",
@@ -876,7 +861,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.surfaceContainerLow,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Surface Container",
@@ -900,7 +884,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.surfaceContainer,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Surface Container High",
@@ -924,7 +907,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.surfaceContainerHigh,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Surface Container Highest",
@@ -948,7 +930,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.surfaceContainerHighest,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Surface Variant",
@@ -972,7 +953,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.surfaceVariant,
- modifier = Modifier.padding(end = 12.dp),
)
},
) {
@@ -994,7 +974,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
}
}
item {
- ThemePreferenceCategory(
+ ColorSchemePreferenceCategory(
title = "Content colors",
previewColorScheme = previewColorScheme,
darkMode = previewDarkTheme,
@@ -1022,7 +1002,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.onSurface,
- modifier = Modifier.padding(end = 12.dp),
)
@@ -1048,7 +1027,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.onSurfaceVariant,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
@@ -1073,7 +1051,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.onSurfaceVariant,
- modifier = Modifier.padding(end = 12.dp),
)
},
) {
@@ -1126,7 +1103,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
}
Surface(
- color = MaterialTheme.colorScheme.surface,
+ color = MaterialTheme.colorScheme.surfaceContainer,
shape = MaterialTheme.shapes.extraSmall,
tonalElevation = 3.dp,
shadowElevation = 3.dp,
@@ -1148,7 +1125,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
}
}
item {
- ThemePreferenceCategory(
+ ColorSchemePreferenceCategory(
title = "Outline colors",
previewColorScheme = previewColorScheme,
darkMode = previewDarkTheme,
@@ -1176,7 +1153,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.outline,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Outline Variant",
@@ -1200,7 +1176,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.outlineVariant,
- modifier = Modifier.padding(end = 12.dp),
)
},
) {
@@ -1240,7 +1215,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
}
}
item {
- ThemePreferenceCategory(
+ ColorSchemePreferenceCategory(
title = "Error colors",
previewColorScheme = previewColorScheme,
darkMode = previewDarkTheme,
@@ -1268,7 +1243,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.error,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "On Error",
@@ -1292,7 +1266,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.onError,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Error Container",
@@ -1316,7 +1289,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.errorContainer,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "On Error Container",
@@ -1340,7 +1312,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.onErrorContainer,
- modifier = Modifier.padding(end = 12.dp),
)
},
) {
@@ -1354,7 +1325,7 @@ fun ThemeSettingsScreen(themeId: UUID) {
}
}
item {
- ThemePreferenceCategory(
+ ColorSchemePreferenceCategory(
title = "Inverse colors",
previewColorScheme = previewColorScheme,
darkMode = previewDarkTheme,
@@ -1382,7 +1353,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.inverseSurface,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Inverse Surface",
@@ -1406,7 +1376,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.inverseOnSurface,
- modifier = Modifier.padding(end = 12.dp),
)
ThemeColorPreference(
title = "Inverse Primary",
@@ -1430,7 +1399,6 @@ fun ThemeSettingsScreen(themeId: UUID) {
)
},
defaultValue = selectedDefaultScheme.inversePrimary,
- modifier = Modifier.padding(end = 12.dp),
)
},
) {
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemesSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreen.kt
similarity index 91%
rename from app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemesSettingsScreen.kt
rename to app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreen.kt
index 97f03e6a..d4649183 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemesSettingsScreen.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreen.kt
@@ -45,7 +45,7 @@ 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.Theme
+import de.mm20.launcher2.themes.Colors
import de.mm20.launcher2.ui.R
import de.mm20.launcher2.ui.common.ImportThemeSheet
import de.mm20.launcher2.ui.component.preferences.Preference
@@ -57,15 +57,15 @@ import de.mm20.launcher2.ui.theme.colorscheme.darkColorSchemeOf
import de.mm20.launcher2.ui.theme.colorscheme.lightColorSchemeOf
@Composable
-fun ThemesSettingsScreen() {
- val viewModel: ThemesSettingsScreenVM = viewModel()
+fun ColorSchemesSettingsScreen() {
+ val viewModel: ColorSchemesSettingsScreenVM = viewModel()
val navController = LocalNavController.current
val context = LocalContext.current
- val selectedTheme by viewModel.selectedTheme.collectAsStateWithLifecycle(null)
- val themes by viewModel.themes.collectAsStateWithLifecycle(emptyList())
+ val selectedTheme by viewModel.selectedColors.collectAsStateWithLifecycle(null)
+ val themes by viewModel.colors.collectAsStateWithLifecycle(emptyList())
- var deleteTheme by remember { mutableStateOf(null) }
+ var deleteColors by remember { mutableStateOf(null) }
var importThemeUri by remember { mutableStateOf(null) }
@@ -114,7 +114,7 @@ fun ThemesSettingsScreen() {
},
text = { Text(stringResource(R.string.edit)) },
onClick = {
- navController?.navigate("settings/appearance/themes/${theme.id}")
+ navController?.navigate("settings/appearance/colors/${theme.id}")
showMenu = false
}
)
@@ -146,7 +146,7 @@ fun ThemesSettingsScreen() {
},
text = { Text(stringResource(R.string.menu_delete)) },
onClick = {
- deleteTheme = theme
+ deleteColors = theme
showMenu = false
}
)
@@ -162,22 +162,22 @@ fun ThemesSettingsScreen() {
}
}
}
- if (deleteTheme != null) {
+ if (deleteColors != null) {
AlertDialog(
- onDismissRequest = { deleteTheme = null },
+ onDismissRequest = { deleteColors = null },
text = {
Text(
stringResource(
R.string.confirmation_delete_color_scheme,
- deleteTheme!!.name
+ deleteColors!!.name
)
)
},
confirmButton = {
TextButton(
onClick = {
- viewModel.delete(deleteTheme!!)
- deleteTheme = null
+ viewModel.delete(deleteColors!!)
+ deleteColors = null
}
) {
Text(stringResource(android.R.string.ok))
@@ -185,7 +185,7 @@ fun ThemesSettingsScreen() {
},
dismissButton = {
TextButton(
- onClick = { deleteTheme = null }
+ onClick = { deleteColors = null }
) {
Text(stringResource(android.R.string.cancel))
}
@@ -199,9 +199,9 @@ fun ThemesSettingsScreen() {
}
@Composable
-fun ColorSchemePreview(theme: Theme) {
+fun ColorSchemePreview(colors: Colors) {
val dark = LocalDarkTheme.current
- val scheme = if (dark) darkColorSchemeOf(theme) else lightColorSchemeOf(theme)
+ val scheme = if (dark) darkColorSchemeOf(colors) else lightColorSchemeOf(colors)
Box(
modifier = Modifier
.height(28.dp)
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemesSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreenVM.kt
similarity index 55%
rename from app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemesSettingsScreenVM.kt
rename to app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreenVM.kt
index c4def5e7..a0acdb75 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemesSettingsScreenVM.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ColorSchemesSettingsScreenVM.kt
@@ -6,13 +6,13 @@ import androidx.core.content.FileProvider
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import de.mm20.launcher2.ktx.tryStartActivity
-import de.mm20.launcher2.preferences.ThemeDescriptor
+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.Theme
+import de.mm20.launcher2.themes.Colors
import de.mm20.launcher2.themes.ThemeRepository
-import de.mm20.launcher2.themes.toJson
+import de.mm20.launcher2.themes.toLegacyJson
import de.mm20.launcher2.ui.R
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
@@ -24,49 +24,49 @@ import org.koin.core.component.inject
import java.io.File
import java.util.UUID
-class ThemesSettingsScreenVM : ViewModel(), KoinComponent {
+class ColorSchemesSettingsScreenVM : ViewModel(), KoinComponent {
private val themeRepository: ThemeRepository by inject()
private val uiSettings: UiSettings by inject()
- val selectedTheme = uiSettings.theme.map {
+ val selectedColors = uiSettings.colors.map {
when(it) {
- ThemeDescriptor.Default -> DefaultThemeId
- ThemeDescriptor.BlackAndWhite -> BlackAndWhiteThemeId
- is ThemeDescriptor.Custom -> UUID.fromString(it.id)
+ ColorsDescriptor.Default -> DefaultThemeId
+ ColorsDescriptor.BlackAndWhite -> BlackAndWhiteThemeId
+ is ColorsDescriptor.Custom -> UUID.fromString(it.id)
}
}
- val themes: Flow> = themeRepository.getThemes()
+ val colors: Flow> = themeRepository.getAllColors()
- fun getTheme(id: UUID): Flow {
- return themeRepository.getTheme(id)
+ fun getTheme(id: UUID): Flow {
+ return themeRepository.getColors(id)
}
- fun updateTheme(theme: Theme) {
- themeRepository.updateTheme(theme)
+ fun updateTheme(colors: Colors) {
+ themeRepository.updateColors(colors)
}
- fun selectTheme(theme: Theme) {
- uiSettings.setTheme(when(theme.id) {
- DefaultThemeId -> ThemeDescriptor.Default
- BlackAndWhiteThemeId -> ThemeDescriptor.BlackAndWhite
- else -> ThemeDescriptor.Custom(theme.id.toString())
+ fun selectTheme(colors: Colors) {
+ uiSettings.setColors(when(colors.id) {
+ DefaultThemeId -> ColorsDescriptor.Default
+ BlackAndWhiteThemeId -> ColorsDescriptor.BlackAndWhite
+ else -> ColorsDescriptor.Custom(colors.id.toString())
})
}
- fun duplicate(theme: Theme) {
- themeRepository.createTheme(theme.copy(id = UUID.randomUUID()))
+ fun duplicate(colors: Colors) {
+ themeRepository.createColors(colors.copy(id = UUID.randomUUID()))
}
- fun delete(theme: Theme) {
- themeRepository.deleteTheme(theme)
+ fun delete(colors: Colors) {
+ themeRepository.deleteColors(colors)
}
- fun exportTheme(context: Context, theme: Theme) {
+ fun exportTheme(context: Context, colors: Colors) {
viewModelScope.launch {
val file = withContext(Dispatchers.IO) {
- val file = File(context.cacheDir, "${theme.name}.kvtheme")
- file.writeText(theme.toJson())
+ val file = File(context.cacheDir, "${colors.name}.kvtheme")
+ file.writeText(colors.toLegacyJson())
file
}
context.tryStartActivity(Intent().apply {
@@ -82,8 +82,8 @@ class ThemesSettingsScreenVM : ViewModel(), KoinComponent {
}
fun createNew(context: Context) {
- themeRepository.createTheme(
- Theme(
+ themeRepository.createColors(
+ Colors(
id = UUID.randomUUID(),
name = context.getString(R.string.new_color_scheme_name)
)
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/CorePaletteColorPreference.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/CorePaletteColorPreference.kt
index 025932b1..41482fd7 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/CorePaletteColorPreference.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/CorePaletteColorPreference.kt
@@ -3,8 +3,10 @@ package de.mm20.launcher2.ui.settings.colorscheme
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
+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
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -16,6 +18,7 @@ import androidx.compose.material.icons.rounded.SettingsSuggest
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
@@ -28,7 +31,11 @@ import androidx.compose.ui.Modifier
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
@@ -47,19 +54,25 @@ fun CorePaletteColorPreference(
) {
var showDialog by remember { mutableStateOf(false) }
- Tooltip(
- tooltipText = title
+ Row(
+ modifier = modifier.fillMaxWidth()
+ .clickable(
+ onClick = { showDialog = true },
+ )
+ .padding(horizontal = 16.dp, vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
) {
ColorSwatch(
color = Color(value ?: defaultValue),
- modifier = modifier
- .size(48.dp)
- .combinedClickable(
- onClick = { showDialog = true },
- onLongClick = {
- onValueChange(null)
- }
- ),
+ modifier = Modifier.padding(end = 20.dp).size(48.dp),
+ )
+
+ Text(
+ title,
+ style = MaterialTheme.typography.titleMedium,
+ textAlign = TextAlign.Center,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1,
)
}
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeColorPreference.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeColorPreference.kt
index 9a525311..adea0f60 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeColorPreference.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/colorscheme/ThemeColorPreference.kt
@@ -4,6 +4,7 @@ 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
@@ -11,6 +12,7 @@ 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
@@ -45,7 +47,9 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.drawscope.Fill
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.ColorRef
import de.mm20.launcher2.themes.CorePaletteColor
@@ -66,7 +70,7 @@ import de.mm20.launcher2.themes.Color as ThemeColor
@Composable
fun ThemeColorPreference(
title: String,
- value: de.mm20.launcher2.themes.Color?,
+ value: ThemeColor?,
corePalette: FullCorePalette,
onValueChange: (ThemeColor?) -> Unit,
defaultValue: ThemeColor,
@@ -74,16 +78,25 @@ fun ThemeColorPreference(
) {
var showDialog by remember { mutableStateOf(false) }
- Tooltip(
- tooltipText = title
+ Row(
+ modifier = modifier.fillMaxWidth()
+ .clickable(
+ onClick = { showDialog = true },
+ )
+ .padding(horizontal = 16.dp, vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
) {
ColorSwatch(
color = Color((value ?: defaultValue).get(corePalette)),
- modifier = modifier
- .size(48.dp)
- .clickable(
- onClick = { showDialog = true },
- ),
+ modifier = Modifier.padding(end = 20.dp).size(48.dp),
+ )
+
+ Text(
+ title,
+ style = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace),
+ textAlign = TextAlign.Center,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1,
)
}
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemeSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemeSettingsScreen.kt
new file mode 100644
index 00000000..4a2c1399
--- /dev/null
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemeSettingsScreen.kt
@@ -0,0 +1,733 @@
+package de.mm20.launcher2.ui.settings.shapes
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.IntrinsicSize
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.rounded.Edit
+import androidx.compose.material.icons.rounded.OpenInNew
+import androidx.compose.material.icons.rounded.RestartAlt
+import androidx.compose.material.icons.rounded.RoundedCorner
+import androidx.compose.material.icons.rounded.Search
+import androidx.compose.material3.AlertDialog
+import androidx.compose.material3.BottomSheetDefaults
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.FilterChip
+import androidx.compose.material3.FloatingActionButton
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.SegmentedButton
+import androidx.compose.material3.SegmentedButtonDefaults
+import androidx.compose.material3.Shapes
+import androidx.compose.material3.SingleChoiceSegmentedButtonRow
+import androidx.compose.material3.Slider
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+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.rotate
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.TextStyle
+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.compose.ui.unit.min
+import androidx.compose.ui.unit.times
+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.ui.R
+import de.mm20.launcher2.ui.component.BottomSheetDialog
+import de.mm20.launcher2.ui.component.preferences.PreferenceCategory
+import de.mm20.launcher2.ui.component.preferences.PreferenceScreen
+import de.mm20.launcher2.ui.ktx.withCorners
+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
+
+@Composable
+fun ShapeSchemeSettingsScreen(themeId: UUID) {
+ val viewModel: ShapeSchemesSettingsScreenVM = viewModel()
+
+ val context = LocalContext.current
+
+ val theme by remember(
+ viewModel,
+ themeId
+ ) { viewModel.getShapes(themeId) }.collectAsStateWithLifecycle(null)
+ val previewShapes = theme?.let { shapesOf(it) }
+
+
+ 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.updateShapes(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 || previewShapes == null) return@PreferenceScreen
+ val baseShape = theme!!.baseShape
+
+ item {
+ PreferenceCategory {
+ ShapePreference(
+ title = stringResource(R.string.preference_shapes_base),
+ shape = baseShape,
+ baseShape = baseShape,
+ factor = 1f,
+ onValueChange = {
+ viewModel.updateShapes(
+ theme!!.copy(
+ baseShape = it ?: ThemeShape(
+ corners = CornerStyle.Rounded,
+ radii = intArrayOf(12, 12, 12, 12)
+ )
+ )
+ )
+ },
+ titleTextStyle = MaterialTheme.typography.titleMedium
+ )
+ }
+ }
+
+ item {
+ PreferenceCategory {
+ ShapePreview(
+ previewShapes = previewShapes,
+ ) {
+ Column(
+ modifier = Modifier
+ .width(200.dp)
+ ) {
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .height(32.dp)
+ .background(
+ MaterialTheme.colorScheme.surface,
+ MaterialTheme.shapes.extraSmall.withCorners(
+ topStart = false,
+ topEnd = false
+ )
+ )
+ )
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .padding(top = 2.dp)
+ .height(32.dp)
+ .background(
+ MaterialTheme.colorScheme.surface,
+ MaterialTheme.shapes.extraSmall.withCorners(
+ bottomStart = false,
+ bottomEnd = false
+ )
+ )
+ )
+ }
+ Surface(
+ color = MaterialTheme.colorScheme.surfaceContainer,
+ shape = MaterialTheme.shapes.extraSmall,
+ tonalElevation = 3.dp,
+ shadowElevation = 3.dp,
+ modifier = Modifier.wrapContentWidth()
+ ) {
+ Column(
+ modifier = Modifier
+ .padding(vertical = 8.dp)
+ .width(IntrinsicSize.Max),
+ ) {
+ DropdownMenuItem(
+ leadingIcon = {
+ Icon(Icons.Rounded.OpenInNew, null)
+ },
+ text = { Text("Menu") },
+ onClick = { })
+ }
+ }
+ }
+ ShapePreference(
+ title = "Extra small",
+ shape = theme!!.extraSmall,
+ baseShape = baseShape,
+ factor = 1f / 3f,
+ onValueChange = {
+ viewModel.updateShapes(
+ theme!!.copy(extraSmall = it)
+ )
+ }
+ )
+ }
+ PreferenceCategory {
+ ShapePreview(
+ previewShapes = previewShapes,
+ ) {
+ FilterChip(
+ onClick = {},
+ label = {
+ Text("Chip")
+ },
+ selected = false,
+ )
+ }
+ ShapePreference(
+ title = "Small",
+ shape = theme!!.small,
+ baseShape = baseShape,
+ factor = 2f / 3f,
+ onValueChange = {
+ viewModel.updateShapes(
+ theme!!.copy(small = it)
+ )
+ }
+ )
+ }
+ PreferenceCategory {
+ ShapePreview(
+ previewShapes = previewShapes,
+ ) {
+ Box(
+ Modifier
+ .fillMaxWidth()
+ .width(200.dp)
+ .height(48.dp)
+ .background(
+ MaterialTheme.colorScheme.surface,
+ MaterialTheme.shapes.medium
+ )
+ ) {
+ Icon(
+ Icons.Rounded.Search,
+ null,
+ modifier = Modifier.padding(12.dp)
+ )
+ }
+ }
+ ShapePreference(
+ title = "Medium",
+ shape = theme!!.medium,
+ baseShape = baseShape,
+ factor = 1f,
+ onValueChange = {
+ viewModel.updateShapes(
+ theme!!.copy(medium = it)
+ )
+ }
+ )
+ }
+ PreferenceCategory {
+ ShapePreview(
+ previewShapes = previewShapes,
+ ) {
+ FloatingActionButton(onClick = {}) {
+ Icon(Icons.Rounded.Edit, null)
+ }
+ }
+ ShapePreference(
+ title = "Large",
+ shape = theme!!.large,
+ baseShape = baseShape,
+ factor = 4f / 3f,
+ onValueChange = {
+ viewModel.updateShapes(
+ theme!!.copy(large = it)
+ )
+ }
+ )
+ }
+ PreferenceCategory {
+ ShapePreference(
+ title = "Large increased",
+ shape = theme!!.largeIncreased,
+ baseShape = baseShape,
+ factor = 5f / 3f,
+ onValueChange = {
+ viewModel.updateShapes(
+ theme!!.copy(largeIncreased = it)
+ )
+ }
+ )
+ }
+ PreferenceCategory {
+ ShapePreview(
+ previewShapes = previewShapes,
+ ) {
+ Surface(
+ shape = BottomSheetDefaults.ExpandedShape,
+ color = BottomSheetDefaults.ContainerColor,
+ shadowElevation = BottomSheetDefaults.Elevation,
+ tonalElevation = BottomSheetDefaults.Elevation,
+ modifier = Modifier
+ .width(250.dp)
+ .height(144.dp),
+ ) {
+ Box(
+ contentAlignment = Alignment.TopCenter,
+ ) {
+ BottomSheetDefaults.DragHandle()
+ }
+ }
+ }
+ ShapePreference(
+ title = "Extra large",
+ shape = theme!!.extraLarge,
+ baseShape = baseShape,
+ factor = 7f / 3f,
+ onValueChange = {
+ viewModel.updateShapes(
+ theme!!.copy(extraLarge = it)
+ )
+ }
+ )
+ }
+ PreferenceCategory {
+ ShapePreference(
+ title = "Extra large increased",
+ shape = theme!!.extraLargeIncreased,
+ baseShape = baseShape,
+ factor = 8f / 3f,
+ onValueChange = {
+ viewModel.updateShapes(
+ theme!!.copy(extraLargeIncreased = it)
+ )
+ }
+ )
+ }
+ PreferenceCategory {
+ ShapePreference(
+ title = "Extra extra large",
+ shape = theme!!.extraExtraLarge,
+ baseShape = baseShape,
+ factor = 12f / 3f,
+ onValueChange = {
+ viewModel.updateShapes(
+ theme!!.copy(extraExtraLarge = it)
+ )
+ }
+ )
+ }
+ }
+ }
+}
+
+@Composable
+fun ShapePreference(
+ title: String,
+ shape: ThemeShape?,
+ baseShape: ThemeShape,
+ factor: Float = 1f,
+ onValueChange: (ThemeShape?) -> Unit,
+ titleTextStyle: TextStyle = MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace)
+) {
+ var showDialog by remember { mutableStateOf(false) }
+
+ val f = min(1f, factor)
+ val topStart =
+ (shape?.radii?.get(0)?.div(factor) ?: baseShape.radii?.get(0)?.toFloat() ?: 12f) * f
+ val topEnd =
+ (shape?.radii?.get(1)?.div(factor) ?: baseShape.radii?.get(1)?.toFloat() ?: 12f) * f
+ val bottomEnd =
+ (shape?.radii?.get(2)?.div(factor) ?: baseShape.radii?.get(2)?.toFloat() ?: 12f) * f
+ val bottomStart =
+ (shape?.radii?.get(3)?.div(factor) ?: baseShape.radii?.get(3)?.toFloat() ?: 12f) * f
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(bottom = 8.dp)
+ .clickable(
+ onClick = { showDialog = true },
+ )
+ .padding(horizontal = 16.dp, vertical = 8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Box(
+ modifier = Modifier
+ .padding(end = 20.dp)
+ .size(48.dp),
+ contentAlignment = Alignment.Center
+ ) {
+ Box(
+ modifier = Modifier
+ .size(min(48.dp, factor * 48.dp))
+ .border(
+ 2.dp,
+ MaterialTheme.colorScheme.primary,
+ if ((shape?.corners ?: baseShape.corners
+ ?: CornerStyle.Rounded) == CornerStyle.Cut
+ ) {
+ CutCornerShape(
+ topStart = topStart.dp,
+ topEnd = topEnd.dp,
+ bottomEnd = bottomEnd.dp,
+ bottomStart = bottomStart.dp
+ )
+ } else {
+ RoundedCornerShape(
+ topStart = topStart.dp,
+ topEnd = topEnd.dp,
+ bottomEnd = bottomEnd.dp,
+ bottomStart = bottomStart.dp
+ )
+ }
+ )
+ )
+ }
+
+
+ Text(
+ title,
+ style = titleTextStyle,
+ textAlign = TextAlign.Center,
+ overflow = TextOverflow.Ellipsis,
+ maxLines = 1,
+ )
+ }
+
+ if (showDialog) {
+ val maxRadius = (24 * factor).toInt()
+
+ val baseTopStart = ((baseShape.radii?.get(0) ?: 12) * factor).toInt()
+ val baseTopEnd = ((baseShape.radii?.get(1) ?: 12) * factor).toInt()
+ val baseBottomEnd = ((baseShape.radii?.get(2) ?: 12) * factor).toInt()
+ val baseBottomStart = ((baseShape.radii?.get(3) ?: 12) * factor).toInt()
+
+
+ var currentCornerStyle by remember(shape) { mutableStateOf(shape?.corners) }
+ var currentTopStart by remember(shape) { mutableStateOf(shape?.radii?.get(0)) }
+ var currentTopEnd by remember(shape) { mutableStateOf(shape?.radii?.get(1)) }
+ var currentBottomEnd by remember(shape) { mutableStateOf(shape?.radii?.get(2)) }
+ var currentBottomStart by remember(shape) { mutableStateOf(shape?.radii?.get(3)) }
+
+ val actualCornerStyle = currentCornerStyle ?: baseShape.corners ?: CornerStyle.Rounded
+ val actualTopStart = currentTopStart ?: baseTopStart
+ val actualTopEnd = currentTopEnd ?: baseTopEnd
+ val actualBottomEnd = currentBottomEnd ?: baseBottomEnd
+ val actualBottomStart = currentBottomStart ?: baseBottomStart
+
+ BottomSheetDialog(
+ onDismissRequest = {
+ showDialog = false
+ onValueChange(
+ ThemeShape(
+ corners = currentCornerStyle,
+ radii = if (currentTopStart != null || currentTopEnd != null || currentBottomEnd != null || currentBottomStart != null) {
+ intArrayOf(
+ currentTopStart ?: baseTopStart,
+ currentTopEnd ?: baseTopEnd,
+ currentBottomEnd ?: baseBottomEnd,
+ currentBottomStart ?: baseBottomStart,
+ )
+ } else null
+ )
+ )
+ }) {
+
+ Column(
+ modifier = Modifier
+ .verticalScroll(rememberScrollState())
+ .padding(it),
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ val previewShape = if ((currentCornerStyle ?: baseShape.corners
+ ?: CornerStyle.Rounded) == CornerStyle.Rounded
+ ) {
+ RoundedCornerShape(
+ topStart = (currentTopStart ?: baseTopStart).dp,
+ topEnd = (currentTopEnd ?: baseTopEnd).dp,
+ bottomEnd = (currentBottomEnd ?: baseBottomEnd).dp,
+ bottomStart = (currentBottomStart ?: baseBottomStart).dp
+
+ )
+ } else {
+ CutCornerShape(
+ topStart = (currentTopStart ?: baseTopStart).dp,
+ topEnd = (currentTopEnd ?: baseTopEnd).dp,
+ bottomEnd = (currentBottomEnd ?: baseBottomEnd).dp,
+ bottomStart = (currentBottomStart ?: baseBottomStart).dp
+ )
+ }
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .height(max(96, maxRadius * 2).dp)
+ .background(MaterialTheme.colorScheme.surfaceContainer, previewShape)
+ .border(
+ 2.dp,
+ MaterialTheme.colorScheme.primary,
+ previewShape
+ )
+ )
+
+ SingleChoiceSegmentedButtonRow(
+ modifier = Modifier
+ .fillMaxWidth()
+ ) {
+ SegmentedButton(
+ selected = actualCornerStyle == CornerStyle.Rounded,
+ onClick = {
+ currentCornerStyle = CornerStyle.Rounded
+ },
+ shape = SegmentedButtonDefaults.itemShape(0, 2),
+ icon = {
+ SegmentedButtonDefaults.Icon(
+ active = actualCornerStyle == CornerStyle.Rounded,
+ ) {
+ Icon(Icons.Rounded.RoundedCornerAlt, null)
+ }
+ }
+ ) {
+ Text(stringResource(R.string.preference_cards_shape_rounded))
+ }
+ SegmentedButton(
+ selected = actualCornerStyle == CornerStyle.Cut,
+ onClick = {
+ currentCornerStyle = CornerStyle.Cut
+ },
+ shape = SegmentedButtonDefaults.itemShape(1, 2),
+ icon = {
+ SegmentedButtonDefaults.Icon(
+ active = actualCornerStyle == CornerStyle.Cut,
+ ) {
+ Icon(Icons.Rounded.CutCorner, null)
+ }
+ }
+ ) {
+ Text(stringResource(R.string.preference_cards_shape_cut))
+ }
+ }
+
+
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ Icons.Rounded.RoundedCorner,
+ null,
+ modifier = Modifier
+ .padding(end = 8.dp)
+ .rotate(-90f)
+ )
+ Slider(
+ modifier = Modifier
+ .weight(1f),
+ value = actualTopStart.toFloat(),
+ onValueChange = {
+ currentTopStart = it.toInt()
+ },
+ valueRange = 0f..maxRadius.toFloat(),
+ steps = maxRadius + 1
+ )
+ Text(
+ text = actualTopStart.toString(),
+ modifier = Modifier.width(32.dp),
+ style = MaterialTheme.typography.labelMedium,
+ textAlign = TextAlign.Center,
+ )
+ }
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ Icons.Rounded.RoundedCorner,
+ null,
+ modifier = Modifier.padding(end = 8.dp)
+ )
+ Slider(
+ modifier = Modifier
+ .weight(1f),
+ value = actualTopEnd.toFloat(),
+ onValueChange = {
+ currentTopEnd = it.toInt()
+ },
+ valueRange = 0f..maxRadius.toFloat(),
+ steps = maxRadius + 1,
+ )
+ Text(
+ text = actualTopEnd.toString(),
+ modifier = Modifier.width(32.dp),
+ style = MaterialTheme.typography.labelMedium,
+ textAlign = TextAlign.Center,
+ )
+ }
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ Icons.Rounded.RoundedCorner,
+ null,
+ modifier = Modifier
+ .padding(end = 8.dp)
+ .rotate(90f)
+ )
+ Slider(
+ modifier = Modifier
+ .weight(1f),
+ value = actualBottomEnd.toFloat(),
+ onValueChange = {
+ currentBottomEnd = it.toInt()
+ },
+ valueRange = 0f..maxRadius.toFloat(),
+ steps = maxRadius + 1,
+ )
+ Text(
+ text = actualBottomEnd.toString(),
+ modifier = Modifier.width(32.dp),
+ style = MaterialTheme.typography.labelMedium,
+ textAlign = TextAlign.Center,
+ )
+ }
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ ) {
+ Icon(
+ Icons.Rounded.RoundedCorner,
+ null,
+ modifier = Modifier
+ .padding(end = 8.dp)
+ .rotate(180f)
+ )
+ Slider(
+ modifier = Modifier
+ .weight(1f),
+ value = actualBottomStart.toFloat(),
+ onValueChange = {
+ currentBottomStart = it.toInt()
+ },
+ valueRange = 0f..maxRadius.toFloat(),
+ steps = maxRadius + 1,
+ )
+ Text(
+ text = actualBottomStart.toString(),
+ modifier = Modifier.width(32.dp),
+ style = MaterialTheme.typography.labelMedium,
+ textAlign = TextAlign.Center,
+ )
+
+ }
+
+ HorizontalDivider(
+ modifier = Modifier.padding(top = 16.dp)
+ )
+
+ TextButton(
+ modifier = Modifier
+ .padding(top = 8.dp)
+ .align(Alignment.End),
+ contentPadding = ButtonDefaults.TextButtonWithIconContentPadding,
+ onClick = {
+ currentTopStart = null
+ currentTopEnd = null
+ currentBottomEnd = null
+ currentBottomStart = null
+ currentCornerStyle = null
+ onValueChange(null)
+ }
+ ) {
+ Icon(
+ Icons.Rounded.RestartAlt, null,
+ modifier = Modifier
+ .padding(ButtonDefaults.IconSpacing)
+ .size(ButtonDefaults.IconSize)
+ )
+ Text(stringResource(R.string.preference_restore_default))
+ }
+ }
+ }
+ }
+}
+
+@Composable
+private fun ShapePreview(
+ previewShapes: Shapes,
+ content: @Composable () -> Unit,
+) {
+ MaterialTheme(
+ shapes = previewShapes
+ ) {
+ Card(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(16.dp),
+ colors = CardDefaults.cardColors(
+ containerColor = MaterialTheme.colorScheme.surfaceContainer
+ )
+ ) {
+
+ Row(
+ modifier = Modifier
+ .fillMaxWidth()
+ .horizontalScroll(rememberScrollState())
+ .padding(16.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ content()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreen.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreen.kt
new file mode 100644
index 00000000..292abf64
--- /dev/null
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreen.kt
@@ -0,0 +1,204 @@
+package de.mm20.launcher2.ui.settings.shapes
+
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+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.FloatingActionButton
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextButton
+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.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.CornerStyle
+import de.mm20.launcher2.themes.Shapes
+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.shapes.shapesOf
+
+@Composable
+fun ShapeSchemesSettingsScreen() {
+ val viewModel: ShapeSchemesSettingsScreenVM = viewModel()
+ val navController = LocalNavController.current
+ val context = LocalContext.current
+
+ val selectedTheme by viewModel.selectedShapes.collectAsStateWithLifecycle(null)
+ val themes by viewModel.shapes.collectAsStateWithLifecycle(emptyList())
+
+ var deleteShapes by remember { mutableStateOf(null) }
+
+ PreferenceScreen(
+ title = stringResource(R.string.preference_screen_shapes),
+ floatingActionButton = {
+ FloatingActionButton(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,
+ ) {
+ ShapesPreview(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("settings/appearance/shapes/${theme.id}")
+ 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 = {
+ deleteShapes = theme
+ showMenu = false
+ }
+ )
+ }
+ }
+ }
+ },
+ onClick = {
+ viewModel.selectShapes(theme)
+ }
+ )
+ }
+ }
+ }
+ }
+ if (deleteShapes != null) {
+ AlertDialog(
+ onDismissRequest = { deleteShapes = null },
+ text = {
+ Text(
+ stringResource(
+ R.string.confirmation_delete_shapes_scheme,
+ deleteShapes!!.name
+ )
+ )
+ },
+ confirmButton = {
+ TextButton(
+ onClick = {
+ viewModel.delete(deleteShapes!!)
+ deleteShapes = null
+ }
+ ) {
+ Text(stringResource(android.R.string.ok))
+ }
+ },
+ dismissButton = {
+ TextButton(
+ onClick = { deleteShapes = null }
+ ) {
+ Text(stringResource(android.R.string.cancel))
+ }
+ }
+ )
+ }
+}
+
+@Composable
+private fun ShapesPreview(theme: Shapes) {
+ val shape = theme.medium
+ val baseShape = theme.baseShape
+
+ val topStart =
+ (shape?.radii?.get(0)?.toFloat() ?: baseShape.radii?.get(0)?.toFloat() ?: 8f) / 3f * 2f
+ val topEnd =
+ (shape?.radii?.get(1)?.toFloat() ?: baseShape.radii?.get(1)?.toFloat() ?: 8f) / 3f * 2f
+ val bottomEnd =
+ (shape?.radii?.get(2)?.toFloat() ?: baseShape.radii?.get(2)?.toFloat() ?: 8f) / 3f * 2f
+ val bottomStart =
+ (shape?.radii?.get(3)?.toFloat() ?: baseShape.radii?.get(3)?.toFloat() ?: 8f) / 3f * 2f
+ Box(
+ modifier = Modifier
+ .size(32.dp)
+ .border(
+ 2.dp,
+ MaterialTheme.colorScheme.primary,
+ if ((theme.medium?.corners ?: theme.baseShape.corners
+ ?: CornerStyle.Rounded) == CornerStyle.Cut
+ ) {
+ CutCornerShape(
+ topStart = topStart.dp,
+ topEnd = topEnd.dp,
+ bottomEnd = bottomEnd.dp,
+ bottomStart = bottomStart.dp
+ )
+ } else {
+ RoundedCornerShape(
+ topStart = topStart.dp,
+ topEnd = topEnd.dp,
+ bottomEnd = bottomEnd.dp,
+ bottomStart = bottomStart.dp
+ )
+ }
+ )
+ )
+
+}
\ No newline at end of file
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreenVM.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreenVM.kt
new file mode 100644
index 00000000..1f5f8bc2
--- /dev/null
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/settings/shapes/ShapeSchemesSettingsScreenVM.kt
@@ -0,0 +1,68 @@
+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.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
+import kotlin.getValue
+
+class ShapeSchemesSettingsScreenVM : ViewModel(), KoinComponent {
+
+ private val themeRepository: ThemeRepository by inject()
+ private val uiSettings: UiSettings by inject()
+
+ val selectedShapes = uiSettings.shapes.map {
+ when(it) {
+ ShapesDescriptor.Default -> DefaultThemeId
+ ShapesDescriptor.Cut -> CutShapesId
+ ShapesDescriptor.ExtraRound -> ExtraRoundShapesId
+ ShapesDescriptor.Rect -> RectShapesId
+ is ShapesDescriptor.Custom -> UUID.fromString(it.id)
+ }
+ }
+ val shapes: Flow> = themeRepository.getAllShapes()
+
+ fun getShapes(id: UUID): Flow {
+ return themeRepository.getShapes(id)
+ }
+
+ fun updateShapes(shapes: Shapes) {
+ themeRepository.updateShapes(shapes)
+ }
+
+ fun selectShapes(shapes: Shapes) {
+ uiSettings.setShapes(when(shapes.id) {
+ DefaultThemeId -> ShapesDescriptor.Default
+ else -> ShapesDescriptor.Custom(shapes.id.toString())
+ })
+ }
+
+ fun duplicate(shapes: Shapes) {
+ themeRepository.createShapes(shapes.copy(id = UUID.randomUUID()))
+ }
+
+ fun delete(shapes: Shapes) {
+ themeRepository.deleteShapes(shapes)
+ }
+
+ fun createNew(context: Context) {
+ themeRepository.createShapes(
+ Shapes(
+ id = UUID.randomUUID(),
+ name = context.getString(R.string.new_shapes_name)
+ )
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/LauncherTheme.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/LauncherTheme.kt
index 37b9ee63..9cee27e7 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/LauncherTheme.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/LauncherTheme.kt
@@ -16,6 +16,7 @@ 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.typography.DefaultTypography
import de.mm20.launcher2.ui.theme.typography.getDeviceDefaultTypography
import kotlinx.coroutines.flow.flatMapLatest
@@ -33,11 +34,17 @@ fun LauncherTheme(
val uiSettings: UiSettings = koinInject()
val themeRepository: ThemeRepository = koinInject()
- val theme by remember {
- uiSettings.theme.flatMapLatest {
- themeRepository.getThemeOrDefault(it)
+ val themeColors by remember {
+ uiSettings.colors.flatMapLatest {
+ themeRepository.getColorsOrDefault(it)
}
- }.collectAsState(themeRepository.getDefaultTheme())
+ }.collectAsState(null)
+
+ val themeShapes by remember {
+ uiSettings.shapes.flatMapLatest {
+ themeRepository.getShapesOrDefault(it)
+ }
+ }.collectAsState(null)
val colorSchemePref by remember { uiSettings.colorScheme }.collectAsState(
ColorSchemePref.System
@@ -45,27 +52,20 @@ fun LauncherTheme(
val darkTheme =
colorSchemePref == ColorSchemePref.Dark || colorSchemePref == ColorSchemePref.System && isSystemInDarkTheme()
- val cornerRadius by remember {
- uiSettings.cardStyle.map {
- it.cornerRadius.dp
- }
- }.collectAsState(8.dp)
-
- val baseShape by remember {
- uiSettings.cardStyle.map {
- when (it.shape) {
- SurfaceShape.Cut -> CutCornerShape(0f)
- else -> RoundedCornerShape(0f)
- }
- }
- }.collectAsState(RoundedCornerShape(0f))
+ if (themeColors == null || themeShapes == null) {
+ return
+ }
val colorScheme = if (darkTheme) {
- darkColorSchemeOf(theme)
+ darkColorSchemeOf(themeColors!!)
} else {
- lightColorSchemeOf(theme)
+ lightColorSchemeOf(themeColors!!)
}
+ val shapes = shapesOf(themeShapes!!)
+
+
+
val font by remember { uiSettings.font }.collectAsState(
Font.Outfit
)
@@ -80,13 +80,7 @@ fun LauncherTheme(
MaterialExpressiveTheme(
colorScheme = colorScheme,
typography = typography,
- shapes = Shapes(
- extraSmall = baseShape.copy(CornerSize(cornerRadius / 3f)),
- small = baseShape.copy(CornerSize(cornerRadius / 3f * 2f)),
- medium = baseShape.copy(CornerSize(cornerRadius)),
- large = baseShape.copy(CornerSize((cornerRadius / 3f * 4f).coerceAtMost(16.dp))),
- extraLarge = baseShape.copy(CornerSize((cornerRadius / 3f * 7f).coerceAtMost(28.dp))),
- ),
+ shapes = shapes,
content = content
)
}
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/BlackAndWhite.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/BlackAndWhite.kt
deleted file mode 100644
index f284e0aa..00000000
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/BlackAndWhite.kt
+++ /dev/null
@@ -1,83 +0,0 @@
-package de.mm20.launcher2.ui.theme.colorscheme
-
-import androidx.compose.material3.darkColorScheme
-import androidx.compose.material3.lightColorScheme
-import androidx.compose.ui.graphics.Color
-
-
-val LightBlackAndWhiteColorScheme = lightColorScheme(
- primary = Color.Black,
- onPrimary = Color.White,
- primaryContainer = Color.White,
- onPrimaryContainer = Color.Black,
- inversePrimary = Color.White,
- secondary = Color.Black,
- onSecondary = Color.White,
- secondaryContainer = Color.White,
- onSecondaryContainer = Color.Black,
- tertiary = Color.Black,
- onTertiary = Color.White,
- tertiaryContainer = Color.White,
- onTertiaryContainer = Color.Black,
- background = Color.White,
- onBackground = Color.Black,
- surface = Color.White,
- onSurface = Color.Black,
- surfaceVariant = Color.White,
- onSurfaceVariant = Color.Black,
- inverseSurface = Color.Black,
- inverseOnSurface = Color.White,
- error = Color(0xFFC10000),
- onError = Color(0xFFFFFFFF),
- errorContainer = Color(0xFFFFDAD3),
- onErrorContainer = Color(0xFF410000),
- outline = Color.Black,
- surfaceTint = Color.White,
- outlineVariant = Color.Black,
- scrim = Color.Black,
- surfaceDim = Color.White,
- surfaceBright = Color.White,
- surfaceContainer = Color.White,
- surfaceContainerHigh = Color.White,
- surfaceContainerHighest = Color.White,
- surfaceContainerLow = Color.White,
- surfaceContainerLowest = Color.White,
-)
-val DarkBlackAndWhiteColorScheme = darkColorScheme(
- primary = Color.White,
- onPrimary = Color.Black,
- primaryContainer = Color.Black,
- onPrimaryContainer = Color.White,
- inversePrimary = Color.Black,
- secondary = Color.White,
- onSecondary = Color.Black,
- secondaryContainer = Color.Black,
- onSecondaryContainer = Color.White,
- tertiary = Color.White,
- onTertiary = Color.Black,
- tertiaryContainer = Color.Black,
- onTertiaryContainer = Color.White,
- background = Color.Black,
- onBackground = Color.White,
- surface = Color.Black,
- onSurface = Color.White,
- surfaceVariant = Color.Black,
- onSurfaceVariant = Color.White,
- inverseSurface = Color.White,
- inverseOnSurface = Color.Black,
- error = Color(0xffffb4a6),
- onError = Color(0xff690000),
- errorContainer = Color(0xff940000),
- onErrorContainer = Color(0xffffb4a6),
- outline = Color.White,
- surfaceTint = Color.White,
- outlineVariant = Color.White,
- scrim = Color.White,
- surfaceDim = Color.Black,
- surfaceBright = Color.Black,
- surfaceContainer = Color.Black,
- surfaceContainerHigh = Color.Black,
- surfaceContainerHighest = Color.Black,
- surfaceContainerLow = Color.Black,
- surfaceContainerLowest = Color.Black,
-)
\ No newline at end of file
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Custom.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/ColorScheme.kt
similarity index 93%
rename from app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Custom.kt
rename to app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/ColorScheme.kt
index 35fd8e93..bd0b3c25 100644
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Custom.kt
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/ColorScheme.kt
@@ -16,20 +16,20 @@ 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.Theme
+import de.mm20.launcher2.themes.Colors as ThemeColors
import de.mm20.launcher2.themes.get
import de.mm20.launcher2.themes.merge
import de.mm20.launcher2.ui.locals.LocalWallpaperColors
import org.koin.compose.koinInject
@Composable
-fun lightColorSchemeOf(theme: Theme): ColorScheme {
- return colorSchemeOf(theme.lightColorScheme.merge(DefaultLightColorScheme), theme.corePalette)
+fun lightColorSchemeOf(colors: ThemeColors): ColorScheme {
+ return colorSchemeOf(colors.lightColorScheme.merge(DefaultLightColorScheme), colors.corePalette)
}
@Composable
-fun darkColorSchemeOf(theme: Theme): ColorScheme {
- return colorSchemeOf(theme.darkColorScheme.merge(DefaultDarkColorScheme), theme.corePalette)
+fun darkColorSchemeOf(colors: ThemeColors): ColorScheme {
+ return colorSchemeOf(colors.darkColorScheme.merge(DefaultDarkColorScheme), colors.corePalette)
}
@Composable
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Default.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Default.kt
deleted file mode 100644
index cc8b5e96..00000000
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Default.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-package de.mm20.launcher2.ui.theme.colorscheme
-
-import androidx.compose.material3.darkColorScheme
-import androidx.compose.material3.lightColorScheme
-import androidx.compose.ui.graphics.Color
-
-val LightDefaultColorScheme = lightColorScheme(
- primary = Color(0xFF3D608A),
- onPrimary = Color(0xFFFFFFFF),
- primaryContainer = Color(0xFFD2E4FF),
- onPrimaryContainer = Color(0xFF001C37),
- inversePrimary = Color(0xFFA5C9F8),
- secondary = Color(0xFF535F70),
- onSecondary = Color(0xFFFFFFFF),
- secondaryContainer = Color(0xFFD7E3F8),
- onSecondaryContainer = Color(0xFF101C2B),
- tertiary = Color(0xFF6B5778),
- onTertiary = Color(0xFFFFFFFF),
- tertiaryContainer = Color(0xFFF3DAFF),
- onTertiaryContainer = Color(0xFF251431),
- surface = Color(0xFFFAF9FC),
- onSurface = Color(0xFF1A1C1E),
- onSurfaceVariant = Color(0xFF43474E),
- inverseSurface = Color(0xFF2F3033),
- inverseOnSurface = Color(0xFFF1F0F3),
- error = Color(0xFFBA1A1A),
- onError = Color(0xFFFFFFFF),
- errorContainer = Color(0xFFFFDAD5),
- onErrorContainer = Color(0xFF410002),
- outline = Color(0xFF73777F),
- outlineVariant = Color(0xFFC3C6CF),
- scrim = Color(0xFF000000),
- background = Color(0xFFFDFCFF),
- onBackground = Color(0xFF1A1C1E),
- surfaceVariant = Color(0xFFDFE2EB),
-)
-
-val DarkDefaultColorScheme = darkColorScheme(
- primary = Color(0xFFA5C9F8),
- onPrimary = Color(0xFF023258),
- primaryContainer = Color(0xFF224970),
- onPrimaryContainer = Color(0xFFD2E4FF),
- inversePrimary = Color(0xFF3D608A),
- secondary = Color(0xFFBBC7DB),
- onSecondary = Color(0xFF253141),
- secondaryContainer = Color(0xFF3C4858),
- onSecondaryContainer = Color(0xFFD7E3F8),
- tertiary = Color(0xFFD6BEE4),
- onTertiary = Color(0xFF3B2947),
- tertiaryContainer = Color(0xFF523F5F),
- onTertiaryContainer = Color(0xFFF3DAFF),
- surface = Color(0xFF1A1C1E),
- onSurface = Color(0xFFE3E2E5),
- onSurfaceVariant = Color(0xFFC3C6CF),
- inverseSurface = Color(0xFFE3E2E5),
- inverseOnSurface = Color(0xFF2F3033),
- error = Color(0xFFFFB4AB),
- onError = Color(0xFF690004),
- errorContainer = Color(0xFF930009),
- onErrorContainer = Color(0xFFFFB4AB),
- outline = Color(0xFF8D9199),
- outlineVariant = Color(0xFF43474E),
- scrim = Color(0xFF000000),
- background = Color(0xFF1A1C1E),
- onBackground = Color(0xFFE3E2E5),
- surfaceVariant = Color(0xFF43474E),
-)
\ No newline at end of file
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/EasterEgg.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/EasterEgg.kt
deleted file mode 100644
index 66e582d0..00000000
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/EasterEgg.kt
+++ /dev/null
@@ -1,67 +0,0 @@
-package de.mm20.launcher2.ui.theme.colorscheme
-
-import androidx.compose.material3.darkColorScheme
-import androidx.compose.material3.lightColorScheme
-import androidx.compose.ui.graphics.Color
-
-val LightEasterEggColorScheme = lightColorScheme(
- primary = Color(0xFFB40180),
- onPrimary = Color(0xFFFFFFFF),
- primaryContainer = Color(0xFFFFD8E9),
- onPrimaryContainer = Color(0xFF3C0029),
- inversePrimary = Color(0xFFFFAFD7),
- secondary = Color(0xFF725763),
- onSecondary = Color(0xFFFFFFFF),
- secondaryContainer = Color(0xFFFDD9E7),
- onSecondaryContainer = Color(0xFF29151F),
- tertiary = Color(0xFF7F543B),
- onTertiary = Color(0xFFFFFFFF),
- tertiaryContainer = Color(0xFFFFDBC9),
- onTertiaryContainer = Color(0xFF311302),
- surface = Color(0xFFFCF8FC),
- onSurface = Color(0xFF1C1B1E),
- onSurfaceVariant = Color(0xFF4F4448),
- inverseSurface = Color(0xFF313033),
- inverseOnSurface = Color(0xFFF3EFF3),
- error = Color(0xFFBA1A1A),
- onError = Color(0xFFFFFFFF),
- errorContainer = Color(0xFFFFDAD5),
- onErrorContainer = Color(0xFF410002),
- outline = Color(0xFF817379),
- outlineVariant = Color(0xFFD3C2C8),
- scrim = Color(0xFF000000),
- background = Color(0xFFFFFBFF),
- onBackground = Color(0xFF1C1B1E),
- surfaceVariant = Color(0xFFF0DEE4),
-)
-
-val DarkEasterEggColorScheme = darkColorScheme(
- primary = Color(0xFFFFAFD7),
- onPrimary = Color(0xFF620044),
- primaryContainer = Color(0xFF8A0061),
- onPrimaryContainer = Color(0xFFFFD8E9),
- inversePrimary = Color(0xFFB40180),
- secondary = Color(0xFFE0BDCB),
- onSecondary = Color(0xFF402A35),
- secondaryContainer = Color(0xFF59404B),
- onSecondaryContainer = Color(0xFFFDD9E7),
- tertiary = Color(0xFFF3BA9B),
- onTertiary = Color(0xFF4A2812),
- tertiaryContainer = Color(0xFF643D26),
- onTertiaryContainer = Color(0xFFFFDBC9),
- surface = Color(0xFF1C1B1E),
- onSurface = Color(0xFFE5E1E5),
- onSurfaceVariant = Color(0xFFD3C2C8),
- inverseSurface = Color(0xFFE5E1E5),
- inverseOnSurface = Color(0xFF313033),
- error = Color(0xFFFFB4AB),
- onError = Color(0xFF690004),
- errorContainer = Color(0xFF930009),
- onErrorContainer = Color(0xFFFFB4AB),
- outline = Color(0xFF9C8D92),
- outlineVariant = Color(0xFF4F4448),
- scrim = Color(0xFF000000),
- background = Color(0xFF1C1B1E),
- onBackground = Color(0xFFE5E1E5),
- surfaceVariant = Color(0xFF4F4448),
-)
\ No newline at end of file
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Wallpaper.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Wallpaper.kt
deleted file mode 100644
index 2943537c..00000000
--- a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/colorscheme/Wallpaper.kt
+++ /dev/null
@@ -1,56 +0,0 @@
-package de.mm20.launcher2.ui.theme.colorscheme
-
-import androidx.compose.material3.ColorScheme
-import androidx.compose.material3.darkColorScheme
-import androidx.compose.material3.lightColorScheme
-import androidx.compose.ui.graphics.Color
-import androidx.compose.ui.graphics.toArgb
-import de.mm20.launcher2.ui.theme.WallpaperColors
-import palettes.TonalPalette
-import scheme.Scheme
-
-fun MaterialYouCompatScheme(wallpaperColors: WallpaperColors, darkTheme: Boolean): ColorScheme {
- val scheme = if (darkTheme) {
- Scheme.dark(wallpaperColors.primary.toArgb())
- } else {
- Scheme.light(wallpaperColors.primary.toArgb())
- }
- return ColorScheme(
- primary = Color(scheme.primary),
- onPrimary = Color(scheme.onPrimary),
- primaryContainer = Color(scheme.primaryContainer),
- onPrimaryContainer = Color(scheme.onPrimaryContainer),
- secondary = Color(scheme.secondary),
- onSecondary = Color(scheme.onSecondary),
- secondaryContainer = Color(scheme.secondaryContainer),
- onSecondaryContainer = Color(scheme.onSecondaryContainer),
- tertiary = Color(scheme.tertiary),
- onTertiary = Color(scheme.onTertiary),
- tertiaryContainer = Color(scheme.tertiaryContainer),
- onTertiaryContainer = Color(scheme.onTertiaryContainer),
- background = Color(scheme.background),
- onBackground = Color(scheme.onBackground),
- surface = Color(scheme.surface),
- onSurface = Color(scheme.onSurface),
- surfaceVariant = Color(scheme.surfaceVariant),
- onSurfaceVariant = Color(scheme.onSurfaceVariant),
- outline = Color(scheme.outline),
- inverseSurface = Color(scheme.inverseSurface),
- inverseOnSurface = Color(scheme.inverseOnSurface),
- inversePrimary = Color(scheme.inversePrimary),
- surfaceTint = Color(scheme.primary),
- error = Color(scheme.error),
- onError = Color(scheme.onError),
- errorContainer = Color(scheme.errorContainer),
- onErrorContainer = Color(scheme.onErrorContainer),
- scrim = Color(scheme.scrim),
- outlineVariant = Color(scheme.outlineVariant),
- surfaceBright = Color(scheme.surfaceBright),
- surfaceContainer = Color(scheme.surfaceContainer),
- surfaceContainerHigh = Color(scheme.surfaceContainerHigh),
- surfaceContainerHighest = Color(scheme.surfaceContainerHighest),
- surfaceContainerLow = Color(scheme.surfaceContainerLow),
- surfaceContainerLowest = Color(scheme.surfaceContainerLowest),
- surfaceDim = Color(scheme.surfaceDim),
- )
-}
\ No newline at end of file
diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/theme/shapes/Shapes.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/shapes/Shapes.kt
new file mode 100644
index 00000000..63d3ad0c
--- /dev/null
+++ b/app/ui/src/main/java/de/mm20/launcher2/ui/theme/shapes/Shapes.kt
@@ -0,0 +1,64 @@
+package de.mm20.launcher2.ui.theme.shapes
+
+import androidx.compose.foundation.shape.CornerBasedShape
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.foundation.shape.CutCornerShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+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
+
+@Composable
+fun shapesOf(shapes: ThemeShapes): Shapes {
+ return remember(shapes) {
+ Shapes(
+ extraSmall = fromShape(shapes.extraSmall, shapes.baseShape, 1f / 3f),
+ small = fromShape(shapes.small, shapes.baseShape, 2f / 3f),
+ medium = fromShape(shapes.medium, shapes.baseShape, 1f),
+ large = fromShape(shapes.large, shapes.baseShape, 4f / 3f),
+ largeIncreased = fromShape(shapes.largeIncreased, shapes.baseShape, 5f / 3f),
+ extraLarge = fromShape(shapes.extraLarge, shapes.baseShape, 7f / 3f),
+ extraLargeIncreased = fromShape(shapes.extraLargeIncreased, shapes.baseShape, 8f / 3f),
+ extraExtraLarge = fromShape(shapes.extraExtraLarge, shapes.baseShape, 12f / 3f),
+ )
+ }
+}
+
+private fun fromShape(shape: ThemeShape?, baseShape: ThemeShape, factor: Float): CornerBasedShape {
+ val topStart = getCornerRadius(shape, baseShape, factor, 0)
+ val topEnd = getCornerRadius(shape, baseShape, factor, 1)
+ val bottomEnd = getCornerRadius(shape, baseShape, factor, 2)
+ val bottomStart = getCornerRadius(shape, baseShape, factor, 3)
+
+ return if ((shape?.corners ?: baseShape.corners) == CornerStyle.Cut) {
+ CutCornerShape(
+ topStart = topStart,
+ topEnd = topEnd,
+ bottomEnd = bottomEnd,
+ bottomStart = bottomStart
+ )
+ } else {
+ RoundedCornerShape(
+ topStart = topStart,
+ topEnd = topEnd,
+ bottomEnd = bottomEnd,
+ bottomStart = bottomStart
+ )
+ }
+}
+
+private fun getCornerRadius(
+ shape: ThemeShape?,
+ baseShape: ThemeShape,
+ factor: Float,
+ index: Int
+): CornerSize {
+ return CornerSize(
+ (shape?.radii?.get(index)?.toFloat() ?: ((baseShape.radii?.get(index)?.toFloat()
+ ?: 12f) * factor)).dp
+ )
+}
\ No newline at end of file
diff --git a/core/base/src/main/java/de/mm20/launcher2/icons/Icons.kt b/core/base/src/main/java/de/mm20/launcher2/icons/Icons.kt
index ff61acb0..4ab48fe6 100644
--- a/core/base/src/main/java/de/mm20/launcher2/icons/Icons.kt
+++ b/core/base/src/main/java/de/mm20/launcher2/icons/Icons.kt
@@ -1740,4 +1740,98 @@ private val _BreezyWeather = materialIcon("Icons.Rounded.BreezyWeather") {
}
val Icons.Rounded.BreezyWeather
- get() = _BreezyWeather
\ No newline at end of file
+ get() = _BreezyWeather
+
+private val _TollOff = materialIcon("Icons.Rounded.TollOff") {
+ materialPath {
+ moveTo(2.8066406f, 3.2207031f)
+ curveToRelative(-0.2556191f, 0f, -0.5111625f, 0.099053f, -0.7070312f, 0.2949219f)
+ curveToRelative(-0.3917371f, 0.3917372f, -0.3917371f, 1.0223253f, 0f, 1.4140625f)
+ lineToRelative(1.3339844f, 1.3320312f)
+ verticalLineToRelative(0.00195f)
+ curveTo(1.933574f, 7.7156369f, 0.99999996f, 9.7483802f, 1f, 12f)
+ curveToRelative(3e-7f, 3.23f, 1.9196876f, 6.009532f, 4.6796875f, 7.269531f)
+ curveTo(6.2896876f, 19.549531f, 7.0000003f, 19.129219f, 7f, 18.449219f)
+ verticalLineToRelative(-0.179688f)
+ curveToRelative(-3e-7f, -0.37f, -0.2303127f, -0.689609f, -0.5703125f, -0.849609f)
+ curveTo(4.399687f, 16.459922f, 2.9999999f, 14.39f, 3f, 12f)
+ curveToRelative(-2e-7f, -1.698485f, 0.7060878f, -3.2344795f, 1.84375f, -4.3261719f)
+ lineToRelative(2.3925781f, 2.3906249f)
+ verticalLineToRelative(0.002f)
+ curveTo(7.0825208f, 10.685478f, 6.9999996f, 11.333026f, 7f, 12f)
+ curveToRelative(0f, 4.42f, 3.58f, 8f, 8f, 8f)
+ curveToRelative(0.666973f, 0f, 1.314475f, -0.08252f, 1.933594f, -0.236328f)
+ horizontalLineToRelative(0.002f)
+ lineToRelative(2.134765f, 2.136719f)
+ curveToRelative(0.391737f, 0.391737f, 1.022326f, 0.391737f, 1.414063f, 0f)
+ curveToRelative(0.391737f, -0.391738f, 0.391737f, -1.024279f, 0f, -1.416016f)
+ lineTo(18.955078f, 18.955078f)
+ lineTo(17.46875f, 17.46875f)
+ lineTo(9.53125f, 9.53125f)
+ lineTo(8.0449219f, 8.0449219f)
+ lineTo(6.5273437f, 6.5273437f)
+ lineTo(5.0507812f, 5.0507812f)
+ lineTo(3.515625f, 3.515625f)
+ curveTo(3.3197565f, 3.3197565f, 3.0622597f, 3.2207031f, 2.8066406f, 3.2207031f)
+ close()
+ moveTo(15f, 4f)
+ curveToRelative(-2.25366f, 2e-7f, -4.288489f, 0.9314658f, -5.7421875f, 2.4296875f)
+ lineTo(10.673828f, 7.8457031f)
+ curveTo(11.766133f, 6.7086818f, 13.30143f, 6.0000002f, 15f, 6f)
+ curveToRelative(3.31f, 4e-7f, 6f, 2.6899997f, 6f, 6f)
+ curveToRelative(0f, 1.69857f, -0.708682f, 3.233867f, -1.845703f, 4.326172f)
+ lineToRelative(1.416015f, 1.416015f)
+ curveTo(22.068533f, 16.288488f, 23f, 14.25366f, 23f, 12f)
+ curveTo(23f, 7.5799994f, 19.42f, 3.9999997f, 15f, 4f)
+ close()
+ moveToRelative(-5.9980469f, 7.832031f)
+ lineToRelative(6.1660159f, 6.166016f)
+ curveTo(15.112392f, 17.999575f, 15.055942f, 18f, 15f, 18f)
+ curveTo(11.69f, 18f, 9.0000004f, 15.31f, 9f, 12f)
+ curveToRelative(2e-7f, -0.05594f, 0.0004292f, -0.112391f, 0.00195f, -0.167969f)
+ close()
+ }
+}
+
+val Icons.Outlined.TollOff
+ get() = _TollOff
+
+val _CutCorner = materialIcon("Icons.Rounded.CutCorner") {
+ materialPath {
+ moveTo(2f, 3f)
+ verticalLineTo(22f)
+ horizontalLineTo(21f)
+ verticalLineTo(11.585938f)
+ lineTo(12.414063f, 3f)
+ close()
+ moveToRelative(2f, 2f)
+ horizontalLineToRelative(7.585938f)
+ lineTo(19f, 12.414063f)
+ verticalLineTo(20f)
+ horizontalLineTo(4f)
+ close()
+ }
+}
+
+val Icons.Rounded.CutCorner
+ get() = _CutCorner
+
+val _RoundedCornerAlt = materialIcon("Icons.Rounded.RoundedCornerAlt") {
+ materialPath {
+ moveTo(2f, 3f)
+ verticalLineTo(22f)
+ horizontalLineTo(21f)
+ verticalLineTo(12f)
+ curveTo(21f, 7.0412819f, 16.958718f, 3f, 12f, 3f)
+ close()
+ moveToRelative(2f, 2f)
+ horizontalLineToRelative(8f)
+ curveToRelative(3.877838f, 0f, 7f, 3.1221621f, 7f, 7f)
+ verticalLineToRelative(8f)
+ horizontalLineTo(4f)
+ close()
+ }
+}
+
+val Icons.Rounded.RoundedCornerAlt
+ get() = _RoundedCornerAlt
\ No newline at end of file
diff --git a/core/base/src/main/java/de/mm20/launcher2/search/Location.kt b/core/base/src/main/java/de/mm20/launcher2/search/Location.kt
index d106fdad..64bf12a8 100644
--- a/core/base/src/main/java/de/mm20/launcher2/search/Location.kt
+++ b/core/base/src/main/java/de/mm20/launcher2/search/Location.kt
@@ -129,6 +129,7 @@ import de.mm20.launcher2.search.location.Departure
import de.mm20.launcher2.search.location.LocationIcon
import de.mm20.launcher2.search.location.OpeningHours
import de.mm20.launcher2.search.location.OpeningSchedule
+import de.mm20.launcher2.search.location.PaymentMethod
import java.time.LocalDateTime
import java.time.temporal.TemporalAdjusters
import android.location.Location as AndroidLocation
@@ -154,6 +155,8 @@ interface Location : SavableSearchable {
val openingSchedule: OpeningSchedule?
val departures: List?
+ val acceptedPaymentMethods: Map?
+
val attribution: Attribution?
get() = null
diff --git a/core/i18n/src/main/res/values/strings.xml b/core/i18n/src/main/res/values/strings.xml
index 6bd3c410..50bffc28 100644
--- a/core/i18n/src/main/res/values/strings.xml
+++ b/core/i18n/src/main/res/values/strings.xml
@@ -438,6 +438,11 @@
Source for dynamic colors
System
Wallpaper
+ Shapes
+ Default
+ Extra round
+ Rectangualar
+ Base shape
Font
System default
About
@@ -819,7 +824,9 @@
Error saving note
The note could not be written to the linked file. Possibly, it has been moved or deleted. A copy has been saved to the launcher\'s internal storage.
Do you really want to delete the color scheme %1$s\?
+ Do you really want to delete the shapes scheme %1$s\?
New color scheme
+ New shapes
Use system default
From primary color
Palette
diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt
index efde951b..d02c0c47 100644
--- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt
+++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/LauncherSettingsData.kt
@@ -11,7 +11,10 @@ data class LauncherSettingsData internal constructor(
val schemaVersion: Int = 5,
val uiColorScheme: ColorScheme = ColorScheme.System,
- val uiTheme: ThemeDescriptor = ThemeDescriptor.Default,
+ @JsonNames("uiTheme")
+ val uiColors: ColorsDescriptor = ColorsDescriptor.Default,
+ val uiShapes: ShapesDescriptor = ShapesDescriptor.Default,
+
val uiCompatModeColors: Boolean = false,
val uiFont: Font = Font.Outfit,
@Deprecated("No longer in use, only used for migration")
@@ -121,8 +124,10 @@ data class LauncherSettingsData internal constructor(
val systemBarsNavColors: SystemBarColors = SystemBarColors.Auto,
val surfacesOpacity: Float = 1f,
+ @Deprecated("Replaces with shape schemes")
val surfacesRadius: Int = 24,
val surfacesBorderWidth: Int = 0,
+ @Deprecated("Replaces with shape schemes")
val surfacesShape: SurfaceShape = SurfaceShape.Rounded,
val widgetsEditButton: Boolean = true,
@@ -201,20 +206,45 @@ enum class Font {
@Serializable
-sealed interface ThemeDescriptor {
+sealed interface ColorsDescriptor {
@Serializable
@SerialName("default")
- data object Default : ThemeDescriptor
+ data object Default : ColorsDescriptor
@Serializable
@SerialName("bw")
- data object BlackAndWhite : ThemeDescriptor
+ data object BlackAndWhite : ColorsDescriptor
@Serializable
@SerialName("custom")
data class Custom(
val id: String,
- ) : ThemeDescriptor
+ ) : 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 {
diff --git a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiSettings.kt b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiSettings.kt
index ae17f311..8e1d0e4c 100644
--- a/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiSettings.kt
+++ b/core/preferences/src/main/java/de/mm20/launcher2/preferences/ui/UiSettings.kt
@@ -7,16 +7,14 @@ import de.mm20.launcher2.preferences.LauncherDataStore
import de.mm20.launcher2.preferences.ScreenOrientation
import de.mm20.launcher2.preferences.SearchBarColors
import de.mm20.launcher2.preferences.SearchBarStyle
-import de.mm20.launcher2.preferences.SurfaceShape
import de.mm20.launcher2.preferences.SystemBarColors
-import de.mm20.launcher2.preferences.ThemeDescriptor
+import de.mm20.launcher2.preferences.ColorsDescriptor
+import de.mm20.launcher2.preferences.ShapesDescriptor
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
data class CardStyle(
val opacity: Float = 1f,
- val cornerRadius: Int = 0,
- val shape: SurfaceShape = SurfaceShape.Rounded,
val borderWidth: Int = 0,
)
@@ -90,8 +88,6 @@ class UiSettings internal constructor(
get() = launcherDataStore.data.map {
CardStyle(
opacity = it.surfacesOpacity,
- cornerRadius = it.surfacesRadius,
- shape = it.surfacesShape,
borderWidth = it.surfacesBorderWidth,
)
}
@@ -102,24 +98,12 @@ class UiSettings internal constructor(
}
}
- fun setCardRadius(radius: Int) {
- launcherDataStore.update {
- it.copy(surfacesRadius = radius)
- }
- }
-
fun setCardBorderWidth(borderWidth: Int) {
launcherDataStore.update {
it.copy(surfacesBorderWidth = borderWidth)
}
}
- fun setCardShape(shape: SurfaceShape) {
- launcherDataStore.update {
- it.copy(surfacesShape = shape)
- }
- }
-
val dimWallpaper
get() = launcherDataStore.data.map {
it.wallpaperDim
@@ -302,14 +286,25 @@ class UiSettings internal constructor(
}
- val theme
+ val colors
get() = launcherDataStore.data.map {
- it.uiTheme
+ it.uiColors
}.distinctUntilChanged()
- fun setTheme(theme: ThemeDescriptor) {
+ fun setColors(colors: ColorsDescriptor) {
launcherDataStore.update {
- it.copy(uiTheme = theme)
+ it.copy(uiColors = colors)
+ }
+ }
+
+ val shapes
+ get() = launcherDataStore.data.map {
+ it.uiShapes
+ }.distinctUntilChanged()
+
+ fun setShapes(shapes: ShapesDescriptor) {
+ launcherDataStore.update {
+ it.copy(uiShapes = shapes)
}
}
diff --git a/core/shared/src/main/java/de/mm20/launcher2/plugin/contracts/LocationPluginContract.kt b/core/shared/src/main/java/de/mm20/launcher2/plugin/contracts/LocationPluginContract.kt
index d944180e..6a526a85 100644
--- a/core/shared/src/main/java/de/mm20/launcher2/plugin/contracts/LocationPluginContract.kt
+++ b/core/shared/src/main/java/de/mm20/launcher2/plugin/contracts/LocationPluginContract.kt
@@ -5,6 +5,7 @@ import de.mm20.launcher2.search.location.Attribution
import de.mm20.launcher2.search.location.Departure
import de.mm20.launcher2.search.location.LocationIcon
import de.mm20.launcher2.search.location.OpeningSchedule
+import de.mm20.launcher2.search.location.PaymentMethod
abstract class LocationPluginContract {
object Paths {
@@ -139,5 +140,11 @@ abstract class LocationPluginContract {
* Type: String? (JSON)
*/
val Attribution = column("attribution")
+
+ /**
+ * Accepted payment methods at this location, encoded as JSON.
+ * Type: String? (JSON)
+ */
+ val AcceptedPaymentMethods = column