mirror of
https://github.com/d4rken-org/capod.git
synced 2026-09-14 18:26:11 -04:00
feat(ui): Add theme mode, style, and color settings
Add user-facing theme preferences with three independent axes: - Theme mode (System/Dark/Light) - Theme style (Default/Material You/Medium Contrast/High Contrast) - Theme color (Blue/Green/Amber) Includes safe Moshi enum fallback for corrupted preference values, color palettes for all combinations, and window background sync to prevent flash during navigation transitions.
This commit is contained in:
@@ -7,7 +7,7 @@ import eu.darken.capod.common.theming.CapodTheme
|
||||
|
||||
@Composable
|
||||
fun PreviewWrapper(content: @Composable () -> Unit) {
|
||||
CapodTheme(dynamicColor = false) {
|
||||
CapodTheme {
|
||||
Surface(color = MaterialTheme.colorScheme.background) {
|
||||
content()
|
||||
}
|
||||
|
||||
+24
-3
@@ -1,16 +1,36 @@
|
||||
package eu.darken.capod.common.preferences
|
||||
|
||||
import android.content.SharedPreferences
|
||||
import com.squareup.moshi.JsonDataException
|
||||
import com.squareup.moshi.JsonEncodingException
|
||||
import com.squareup.moshi.Moshi
|
||||
import eu.darken.capod.common.debug.logging.Logging.Priority.WARN
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
|
||||
inline fun <reified T> moshiReader(
|
||||
moshi: Moshi,
|
||||
defaultValue: T,
|
||||
onErrorFallbackToDefault: Boolean = false,
|
||||
): (Any?) -> T {
|
||||
val adapter = moshi.adapter(T::class.java)
|
||||
return { rawValue ->
|
||||
rawValue as String?
|
||||
rawValue?.let { adapter.fromJson(it) } ?: defaultValue
|
||||
if (rawValue == null) {
|
||||
defaultValue
|
||||
} else if (onErrorFallbackToDefault) {
|
||||
try {
|
||||
adapter.fromJson(rawValue) ?: defaultValue
|
||||
} catch (e: JsonDataException) {
|
||||
log(logTag("FlowPreference"), WARN) { "Failed to decode, fallback to default: ${e.message}" }
|
||||
defaultValue
|
||||
} catch (e: JsonEncodingException) {
|
||||
log(logTag("FlowPreference"), WARN) { "Failed to decode, fallback to default: ${e.message}" }
|
||||
defaultValue
|
||||
}
|
||||
} else {
|
||||
adapter.fromJson(rawValue) ?: defaultValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,9 +47,10 @@ inline fun <reified T : Any?> SharedPreferences.createFlowPreference(
|
||||
key: String,
|
||||
defaultValue: T = null as T,
|
||||
moshi: Moshi,
|
||||
onErrorFallbackToDefault: Boolean = false,
|
||||
) = FlowPreference(
|
||||
preferences = this,
|
||||
key = key,
|
||||
rawReader = moshiReader(moshi, defaultValue),
|
||||
rawReader = moshiReader(moshi, defaultValue, onErrorFallbackToDefault),
|
||||
rawWriter = moshiWriter(moshi)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
package eu.darken.capod.common.settings
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.selection.selectableGroup
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.RadioButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.unit.dp
|
||||
import eu.darken.capod.common.theming.CapodColorsAmber
|
||||
import eu.darken.capod.common.theming.CapodColorsBlue
|
||||
import eu.darken.capod.common.theming.CapodColorsGreen
|
||||
import eu.darken.capod.common.theming.ThemeColor
|
||||
|
||||
@Composable
|
||||
fun ThemeColorSelectorDialog(
|
||||
selectedColor: ThemeColor,
|
||||
onColorSelected: (ThemeColor) -> Unit,
|
||||
onDismiss: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text(text = stringResource(eu.darken.capod.R.string.ui_theme_color_label)) },
|
||||
text = {
|
||||
Column(Modifier.selectableGroup()) {
|
||||
ThemeColor.entries.forEach { color ->
|
||||
val isSelected = color == selectedColor
|
||||
val (lightPreview, darkPreview) = colorPreviewPair(color)
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.selectable(
|
||||
selected = isSelected,
|
||||
onClick = { onColorSelected(color) },
|
||||
role = Role.RadioButton,
|
||||
)
|
||||
.padding(vertical = 12.dp, horizontal = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
RadioButton(
|
||||
selected = isSelected,
|
||||
onClick = null,
|
||||
)
|
||||
Text(
|
||||
text = stringResource(color.labelRes),
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(start = 16.dp),
|
||||
)
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.clip(CircleShape)
|
||||
.background(lightPreview)
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.clip(CircleShape)
|
||||
.background(darkPreview)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(text = stringResource(android.R.string.cancel))
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
private fun colorPreviewPair(color: ThemeColor): Pair<Color, Color> = when (color) {
|
||||
ThemeColor.BLUE -> CapodColorsBlue.primaryLight to CapodColorsBlue.primaryDark
|
||||
ThemeColor.GREEN -> CapodColorsGreen.primaryLight to CapodColorsGreen.primaryDark
|
||||
ThemeColor.AMBER -> CapodColorsAmber.primaryLight to CapodColorsAmber.primaryDark
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package eu.darken.capod.common.theming
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
object CapodColorsAmber {
|
||||
// Light (seed #E65100)
|
||||
val primaryLight = Color(0xFF9D4300)
|
||||
val onPrimaryLight = Color(0xFFFFFFFF)
|
||||
val primaryContainerLight = Color(0xFFFFDBCA)
|
||||
val onPrimaryContainerLight = Color(0xFF341100)
|
||||
val secondaryLight = Color(0xFF765848)
|
||||
val onSecondaryLight = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLight = Color(0xFFFFDBCA)
|
||||
val onSecondaryContainerLight = Color(0xFF2C160A)
|
||||
val tertiaryLight = Color(0xFF636032)
|
||||
val onTertiaryLight = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLight = Color(0xFFEAE5AB)
|
||||
val onTertiaryContainerLight = Color(0xFF1E1C00)
|
||||
val errorLight = Color(0xFFBA1B1B)
|
||||
val onErrorLight = Color(0xFFFFFFFF)
|
||||
val errorContainerLight = Color(0xFFFFDAD4)
|
||||
val onErrorContainerLight = Color(0xFF410001)
|
||||
val backgroundLight = Color(0xFFFFFBFF)
|
||||
val onBackgroundLight = Color(0xFF201A17)
|
||||
val surfaceLight = Color(0xFFFFFBFF)
|
||||
val onSurfaceLight = Color(0xFF201A17)
|
||||
val surfaceVariantLight = Color(0xFFF4DED4)
|
||||
val onSurfaceVariantLight = Color(0xFF52443C)
|
||||
val outlineLight = Color(0xFF85746B)
|
||||
val inverseSurfaceLight = Color(0xFF362F2B)
|
||||
val inverseOnSurfaceLight = Color(0xFFFBEEE8)
|
||||
val inversePrimaryLight = Color(0xFFFFB68E)
|
||||
|
||||
// Dark
|
||||
val primaryDark = Color(0xFFFFB68E)
|
||||
val onPrimaryDark = Color(0xFF552100)
|
||||
val primaryContainerDark = Color(0xFF783200)
|
||||
val onPrimaryContainerDark = Color(0xFFFFDBCA)
|
||||
val secondaryDark = Color(0xFFE6BEAB)
|
||||
val onSecondaryDark = Color(0xFF432B1D)
|
||||
val secondaryContainerDark = Color(0xFF5C4132)
|
||||
val onSecondaryContainerDark = Color(0xFFFFDBCA)
|
||||
val tertiaryDark = Color(0xFFCEC991)
|
||||
val onTertiaryDark = Color(0xFF343109)
|
||||
val tertiaryContainerDark = Color(0xFF4B481D)
|
||||
val onTertiaryContainerDark = Color(0xFFEAE5AB)
|
||||
val errorDark = Color(0xFFFFB4A9)
|
||||
val onErrorDark = Color(0xFF680003)
|
||||
val errorContainerDark = Color(0xFF930006)
|
||||
val onErrorContainerDark = Color(0xFFFFDAD4)
|
||||
val backgroundDark = Color(0xFF201A17)
|
||||
val onBackgroundDark = Color(0xFFECE0DA)
|
||||
val surfaceDark = Color(0xFF201A17)
|
||||
val onSurfaceDark = Color(0xFFECE0DA)
|
||||
val surfaceVariantDark = Color(0xFF52443C)
|
||||
val onSurfaceVariantDark = Color(0xFFD7C2B8)
|
||||
val outlineDark = Color(0xFF9F8D84)
|
||||
val inverseSurfaceDark = Color(0xFFECE0DA)
|
||||
val inverseOnSurfaceDark = Color(0xFF201A17)
|
||||
val inversePrimaryDark = Color(0xFF9D4300)
|
||||
|
||||
// Light Medium Contrast
|
||||
val primaryLightMediumContrast = Color(0xFF6E2D00)
|
||||
val onPrimaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val primaryContainerLightMediumContrast = Color(0xFFBB5918)
|
||||
val onPrimaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val secondaryLightMediumContrast = Color(0xFF583D2E)
|
||||
val onSecondaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLightMediumContrast = Color(0xFF8E6E5C)
|
||||
val onSecondaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryLightMediumContrast = Color(0xFF474419)
|
||||
val onTertiaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLightMediumContrast = Color(0xFF7A7646)
|
||||
val onTertiaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val errorLightMediumContrast = Color(0xFF8C0009)
|
||||
val onErrorLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val errorContainerLightMediumContrast = Color(0xFFDA342E)
|
||||
val onErrorContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val backgroundLightMediumContrast = Color(0xFFFFFBFF)
|
||||
val onBackgroundLightMediumContrast = Color(0xFF201A17)
|
||||
val surfaceLightMediumContrast = Color(0xFFFFFBFF)
|
||||
val onSurfaceLightMediumContrast = Color(0xFF201A17)
|
||||
val surfaceVariantLightMediumContrast = Color(0xFFF4DED4)
|
||||
val onSurfaceVariantLightMediumContrast = Color(0xFF4E4038)
|
||||
val outlineLightMediumContrast = Color(0xFF6B5C54)
|
||||
val inverseSurfaceLightMediumContrast = Color(0xFF362F2B)
|
||||
val inverseOnSurfaceLightMediumContrast = Color(0xFFFBEEE8)
|
||||
val inversePrimaryLightMediumContrast = Color(0xFFFFB68E)
|
||||
|
||||
// Dark Medium Contrast
|
||||
val primaryDarkMediumContrast = Color(0xFFFFBC98)
|
||||
val onPrimaryDarkMediumContrast = Color(0xFF2B0D00)
|
||||
val primaryContainerDarkMediumContrast = Color(0xFFC87B49)
|
||||
val onPrimaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val secondaryDarkMediumContrast = Color(0xFFEBC2AF)
|
||||
val onSecondaryDarkMediumContrast = Color(0xFF261106)
|
||||
val secondaryContainerDarkMediumContrast = Color(0xFFAC8978)
|
||||
val onSecondaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val tertiaryDarkMediumContrast = Color(0xFFD2CD95)
|
||||
val onTertiaryDarkMediumContrast = Color(0xFF181600)
|
||||
val tertiaryContainerDarkMediumContrast = Color(0xFF97935F)
|
||||
val onTertiaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val errorDarkMediumContrast = Color(0xFFFFBAB1)
|
||||
val onErrorDarkMediumContrast = Color(0xFF370001)
|
||||
val errorContainerDarkMediumContrast = Color(0xFFFF5449)
|
||||
val onErrorContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val backgroundDarkMediumContrast = Color(0xFF201A17)
|
||||
val onBackgroundDarkMediumContrast = Color(0xFFECE0DA)
|
||||
val surfaceDarkMediumContrast = Color(0xFF201A17)
|
||||
val onSurfaceDarkMediumContrast = Color(0xFFFFFAF8)
|
||||
val surfaceVariantDarkMediumContrast = Color(0xFF52443C)
|
||||
val onSurfaceVariantDarkMediumContrast = Color(0xFFDCC6BD)
|
||||
val outlineDarkMediumContrast = Color(0xFFB29F96)
|
||||
val inverseSurfaceDarkMediumContrast = Color(0xFFECE0DA)
|
||||
val inverseOnSurfaceDarkMediumContrast = Color(0xFF201A17)
|
||||
val inversePrimaryDarkMediumContrast = Color(0xFF7A3300)
|
||||
|
||||
// Light High Contrast
|
||||
val primaryLightHighContrast = Color(0xFF3D1600)
|
||||
val onPrimaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val primaryContainerLightHighContrast = Color(0xFF6E2D00)
|
||||
val onPrimaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val secondaryLightHighContrast = Color(0xFF331C0F)
|
||||
val onSecondaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLightHighContrast = Color(0xFF583D2E)
|
||||
val onSecondaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryLightHighContrast = Color(0xFF252300)
|
||||
val onTertiaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLightHighContrast = Color(0xFF474419)
|
||||
val onTertiaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val errorLightHighContrast = Color(0xFF4E0002)
|
||||
val onErrorLightHighContrast = Color(0xFFFFFFFF)
|
||||
val errorContainerLightHighContrast = Color(0xFF8C0009)
|
||||
val onErrorContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val backgroundLightHighContrast = Color(0xFFFFFBFF)
|
||||
val onBackgroundLightHighContrast = Color(0xFF201A17)
|
||||
val surfaceLightHighContrast = Color(0xFFFFFBFF)
|
||||
val onSurfaceLightHighContrast = Color(0xFF000000)
|
||||
val surfaceVariantLightHighContrast = Color(0xFFF4DED4)
|
||||
val onSurfaceVariantLightHighContrast = Color(0xFF2D211B)
|
||||
val outlineLightHighContrast = Color(0xFF4E4038)
|
||||
val inverseSurfaceLightHighContrast = Color(0xFF362F2B)
|
||||
val inverseOnSurfaceLightHighContrast = Color(0xFFFFFFFF)
|
||||
val inversePrimaryLightHighContrast = Color(0xFFFFE7DA)
|
||||
|
||||
// Dark High Contrast
|
||||
val primaryDarkHighContrast = Color(0xFFFFFAF8)
|
||||
val onPrimaryDarkHighContrast = Color(0xFF000000)
|
||||
val primaryContainerDarkHighContrast = Color(0xFFFFB68E)
|
||||
val onPrimaryContainerDarkHighContrast = Color(0xFF000000)
|
||||
val secondaryDarkHighContrast = Color(0xFFFFFAF8)
|
||||
val onSecondaryDarkHighContrast = Color(0xFF000000)
|
||||
val secondaryContainerDarkHighContrast = Color(0xFFEBC2AF)
|
||||
val onSecondaryContainerDarkHighContrast = Color(0xFF000000)
|
||||
val tertiaryDarkHighContrast = Color(0xFFFFFBE6)
|
||||
val onTertiaryDarkHighContrast = Color(0xFF000000)
|
||||
val tertiaryContainerDarkHighContrast = Color(0xFFD2CD95)
|
||||
val onTertiaryContainerDarkHighContrast = Color(0xFF000000)
|
||||
val errorDarkHighContrast = Color(0xFFFFF9F9)
|
||||
val onErrorDarkHighContrast = Color(0xFF000000)
|
||||
val errorContainerDarkHighContrast = Color(0xFFFFBAB1)
|
||||
val onErrorContainerDarkHighContrast = Color(0xFF000000)
|
||||
val backgroundDarkHighContrast = Color(0xFF201A17)
|
||||
val onBackgroundDarkHighContrast = Color(0xFFECE0DA)
|
||||
val surfaceDarkHighContrast = Color(0xFF201A17)
|
||||
val onSurfaceDarkHighContrast = Color(0xFFFFFFFF)
|
||||
val surfaceVariantDarkHighContrast = Color(0xFF52443C)
|
||||
val onSurfaceVariantDarkHighContrast = Color(0xFFFFFAF8)
|
||||
val outlineDarkHighContrast = Color(0xFFDCC6BD)
|
||||
val inverseSurfaceDarkHighContrast = Color(0xFFECE0DA)
|
||||
val inverseOnSurfaceDarkHighContrast = Color(0xFF000000)
|
||||
val inversePrimaryDarkHighContrast = Color(0xFF4C1D00)
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package eu.darken.capod.common.theming
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
object CapodColorsBlue {
|
||||
// Light
|
||||
val primaryLight = Color(0xFF3F7AFF)
|
||||
val onPrimaryLight = Color(0xFFFFFFFF)
|
||||
val primaryContainerLight = Color(0xFFDAE2FF)
|
||||
val onPrimaryContainerLight = Color(0xFF00174B)
|
||||
val secondaryLight = Color(0xFF715C00)
|
||||
val onSecondaryLight = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLight = Color(0xFFFFE16C)
|
||||
val onSecondaryContainerLight = Color(0xFF231B00)
|
||||
val tertiaryLight = Color(0xFF006D39)
|
||||
val onTertiaryLight = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLight = Color(0xFF5CFFA0)
|
||||
val onTertiaryContainerLight = Color(0xFF00210D)
|
||||
val errorLight = Color(0xFFBA1B1B)
|
||||
val onErrorLight = Color(0xFFFFFFFF)
|
||||
val errorContainerLight = Color(0xFFFFDAD4)
|
||||
val onErrorContainerLight = Color(0xFF410001)
|
||||
val backgroundLight = Color(0xFFFEFBFF)
|
||||
val onBackgroundLight = Color(0xFF1B1B1F)
|
||||
val surfaceLight = Color(0xFFFEFBFF)
|
||||
val onSurfaceLight = Color(0xFF1B1B1F)
|
||||
val surfaceVariantLight = Color(0xFFE2E2EC)
|
||||
val onSurfaceVariantLight = Color(0xFF44464E)
|
||||
val outlineLight = Color(0xFF75767F)
|
||||
val inverseSurfaceLight = Color(0xFF303033)
|
||||
val inverseOnSurfaceLight = Color(0xFFF2F0F5)
|
||||
val inversePrimaryLight = Color(0xFFB1C5FF)
|
||||
|
||||
// Dark
|
||||
val primaryDark = Color(0xFF3F7AFF)
|
||||
val onPrimaryDark = Color(0xFF002A78)
|
||||
val primaryContainerDark = Color(0xFF003EA7)
|
||||
val onPrimaryContainerDark = Color(0xFFDAE2FF)
|
||||
val secondaryDark = Color(0xFFE9C426)
|
||||
val onSecondaryDark = Color(0xFF3B2F00)
|
||||
val secondaryContainerDark = Color(0xFF564500)
|
||||
val onSecondaryContainerDark = Color(0xFFFFE16C)
|
||||
val tertiaryDark = Color(0xFF33E286)
|
||||
val onTertiaryDark = Color(0xFF00391B)
|
||||
val tertiaryContainerDark = Color(0xFF005229)
|
||||
val onTertiaryContainerDark = Color(0xFF5CFFA0)
|
||||
val errorDark = Color(0xFFFFB4A9)
|
||||
val onErrorDark = Color(0xFF680003)
|
||||
val errorContainerDark = Color(0xFF930006)
|
||||
val onErrorContainerDark = Color(0xFFFFDAD4)
|
||||
val backgroundDark = Color(0xFF1B1B1F)
|
||||
val onBackgroundDark = Color(0xFFE3E1E6)
|
||||
val surfaceDark = Color(0xFF1B1B1F)
|
||||
val onSurfaceDark = Color(0xFFE3E1E6)
|
||||
val surfaceVariantDark = Color(0xFF44464E)
|
||||
val onSurfaceVariantDark = Color(0xFFC6C6D0)
|
||||
val outlineDark = Color(0xFF8F909A)
|
||||
val inverseSurfaceDark = Color(0xFFE3E1E6)
|
||||
val inverseOnSurfaceDark = Color(0xFF1B1B1F)
|
||||
val inversePrimaryDark = Color(0xFF0054D9)
|
||||
|
||||
// Light Medium Contrast
|
||||
val primaryLightMediumContrast = Color(0xFF003DA3)
|
||||
val onPrimaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val primaryContainerLightMediumContrast = Color(0xFF5B8FFF)
|
||||
val onPrimaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val secondaryLightMediumContrast = Color(0xFF504100)
|
||||
val onSecondaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLightMediumContrast = Color(0xFF8A7320)
|
||||
val onSecondaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryLightMediumContrast = Color(0xFF004F28)
|
||||
val onTertiaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLightMediumContrast = Color(0xFF1F864D)
|
||||
val onTertiaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val errorLightMediumContrast = Color(0xFF8C0009)
|
||||
val onErrorLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val errorContainerLightMediumContrast = Color(0xFFDA342E)
|
||||
val onErrorContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val backgroundLightMediumContrast = Color(0xFFFEFBFF)
|
||||
val onBackgroundLightMediumContrast = Color(0xFF1B1B1F)
|
||||
val surfaceLightMediumContrast = Color(0xFFFEFBFF)
|
||||
val onSurfaceLightMediumContrast = Color(0xFF1B1B1F)
|
||||
val surfaceVariantLightMediumContrast = Color(0xFFE2E2EC)
|
||||
val onSurfaceVariantLightMediumContrast = Color(0xFF40424A)
|
||||
val outlineLightMediumContrast = Color(0xFF5D5E67)
|
||||
val inverseSurfaceLightMediumContrast = Color(0xFF303033)
|
||||
val inverseOnSurfaceLightMediumContrast = Color(0xFFF2F0F5)
|
||||
val inversePrimaryLightMediumContrast = Color(0xFFB1C5FF)
|
||||
|
||||
// Dark Medium Contrast
|
||||
val primaryDarkMediumContrast = Color(0xFF7FA3FF)
|
||||
val onPrimaryDarkMediumContrast = Color(0xFF00133F)
|
||||
val primaryContainerDarkMediumContrast = Color(0xFF3F7AFF)
|
||||
val onPrimaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val secondaryDarkMediumContrast = Color(0xFFEDC828)
|
||||
val onSecondaryDarkMediumContrast = Color(0xFF1C1500)
|
||||
val secondaryContainerDarkMediumContrast = Color(0xFFAE8F00)
|
||||
val onSecondaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val tertiaryDarkMediumContrast = Color(0xFF3FE78B)
|
||||
val onTertiaryDarkMediumContrast = Color(0xFF001B09)
|
||||
val tertiaryContainerDarkMediumContrast = Color(0xFF00AA55)
|
||||
val onTertiaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val errorDarkMediumContrast = Color(0xFFFFBAB1)
|
||||
val onErrorDarkMediumContrast = Color(0xFF370001)
|
||||
val errorContainerDarkMediumContrast = Color(0xFFFF5449)
|
||||
val onErrorContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val backgroundDarkMediumContrast = Color(0xFF1B1B1F)
|
||||
val onBackgroundDarkMediumContrast = Color(0xFFE3E1E6)
|
||||
val surfaceDarkMediumContrast = Color(0xFF1B1B1F)
|
||||
val onSurfaceDarkMediumContrast = Color(0xFFFEFAFF)
|
||||
val surfaceVariantDarkMediumContrast = Color(0xFF44464E)
|
||||
val onSurfaceVariantDarkMediumContrast = Color(0xFFCACAD4)
|
||||
val outlineDarkMediumContrast = Color(0xFFA2A2AC)
|
||||
val inverseSurfaceDarkMediumContrast = Color(0xFFE3E1E6)
|
||||
val inverseOnSurfaceDarkMediumContrast = Color(0xFF1B1B1F)
|
||||
val inversePrimaryDarkMediumContrast = Color(0xFF003FB0)
|
||||
|
||||
// Light High Contrast
|
||||
val primaryLightHighContrast = Color(0xFF001F5C)
|
||||
val onPrimaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val primaryContainerLightHighContrast = Color(0xFF003DA3)
|
||||
val onPrimaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val secondaryLightHighContrast = Color(0xFF2A2100)
|
||||
val onSecondaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLightHighContrast = Color(0xFF504100)
|
||||
val onSecondaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryLightHighContrast = Color(0xFF002913)
|
||||
val onTertiaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLightHighContrast = Color(0xFF004F28)
|
||||
val onTertiaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val errorLightHighContrast = Color(0xFF4E0002)
|
||||
val onErrorLightHighContrast = Color(0xFFFFFFFF)
|
||||
val errorContainerLightHighContrast = Color(0xFF8C0009)
|
||||
val onErrorContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val backgroundLightHighContrast = Color(0xFFFEFBFF)
|
||||
val onBackgroundLightHighContrast = Color(0xFF1B1B1F)
|
||||
val surfaceLightHighContrast = Color(0xFFFEFBFF)
|
||||
val onSurfaceLightHighContrast = Color(0xFF000000)
|
||||
val surfaceVariantLightHighContrast = Color(0xFFE2E2EC)
|
||||
val onSurfaceVariantLightHighContrast = Color(0xFF21232B)
|
||||
val outlineLightHighContrast = Color(0xFF40424A)
|
||||
val inverseSurfaceLightHighContrast = Color(0xFF303033)
|
||||
val inverseOnSurfaceLightHighContrast = Color(0xFFFFFFFF)
|
||||
val inversePrimaryLightHighContrast = Color(0xFFE2E8FF)
|
||||
|
||||
// Dark High Contrast
|
||||
val primaryDarkHighContrast = Color(0xFFFCFAFF)
|
||||
val onPrimaryDarkHighContrast = Color(0xFF000000)
|
||||
val primaryContainerDarkHighContrast = Color(0xFFB1C5FF)
|
||||
val onPrimaryContainerDarkHighContrast = Color(0xFF000000)
|
||||
val secondaryDarkHighContrast = Color(0xFFFFF0C1)
|
||||
val onSecondaryDarkHighContrast = Color(0xFF000000)
|
||||
val secondaryContainerDarkHighContrast = Color(0xFFE9C426)
|
||||
val onSecondaryContainerDarkHighContrast = Color(0xFF000000)
|
||||
val tertiaryDarkHighContrast = Color(0xFFEDFFED)
|
||||
val onTertiaryDarkHighContrast = Color(0xFF000000)
|
||||
val tertiaryContainerDarkHighContrast = Color(0xFF33E286)
|
||||
val onTertiaryContainerDarkHighContrast = Color(0xFF000000)
|
||||
val errorDarkHighContrast = Color(0xFFFFF9F9)
|
||||
val onErrorDarkHighContrast = Color(0xFF000000)
|
||||
val errorContainerDarkHighContrast = Color(0xFFFFBAB1)
|
||||
val onErrorContainerDarkHighContrast = Color(0xFF000000)
|
||||
val backgroundDarkHighContrast = Color(0xFF1B1B1F)
|
||||
val onBackgroundDarkHighContrast = Color(0xFFE3E1E6)
|
||||
val surfaceDarkHighContrast = Color(0xFF1B1B1F)
|
||||
val onSurfaceDarkHighContrast = Color(0xFFFFFFFF)
|
||||
val surfaceVariantDarkHighContrast = Color(0xFF44464E)
|
||||
val onSurfaceVariantDarkHighContrast = Color(0xFFFCFAFF)
|
||||
val outlineDarkHighContrast = Color(0xFFCACAD4)
|
||||
val inverseSurfaceDarkHighContrast = Color(0xFFE3E1E6)
|
||||
val inverseOnSurfaceDarkHighContrast = Color(0xFF000000)
|
||||
val inversePrimaryDarkHighContrast = Color(0xFF002568)
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package eu.darken.capod.common.theming
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
object CapodColorsGreen {
|
||||
// Light (seed #006D39)
|
||||
val primaryLight = Color(0xFF006D39)
|
||||
val onPrimaryLight = Color(0xFFFFFFFF)
|
||||
val primaryContainerLight = Color(0xFF5CFFA0)
|
||||
val onPrimaryContainerLight = Color(0xFF00210D)
|
||||
val secondaryLight = Color(0xFF4E6354)
|
||||
val onSecondaryLight = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLight = Color(0xFFD1E8D5)
|
||||
val onSecondaryContainerLight = Color(0xFF0C1F14)
|
||||
val tertiaryLight = Color(0xFF3C6472)
|
||||
val onTertiaryLight = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLight = Color(0xFFBFE9F9)
|
||||
val onTertiaryContainerLight = Color(0xFF001F28)
|
||||
val errorLight = Color(0xFFBA1B1B)
|
||||
val onErrorLight = Color(0xFFFFFFFF)
|
||||
val errorContainerLight = Color(0xFFFFDAD4)
|
||||
val onErrorContainerLight = Color(0xFF410001)
|
||||
val backgroundLight = Color(0xFFFBFDF8)
|
||||
val onBackgroundLight = Color(0xFF191C19)
|
||||
val surfaceLight = Color(0xFFFBFDF8)
|
||||
val onSurfaceLight = Color(0xFF191C19)
|
||||
val surfaceVariantLight = Color(0xFFDDE5DB)
|
||||
val onSurfaceVariantLight = Color(0xFF414942)
|
||||
val outlineLight = Color(0xFF717971)
|
||||
val inverseSurfaceLight = Color(0xFF2E312E)
|
||||
val inverseOnSurfaceLight = Color(0xFFF0F1EC)
|
||||
val inversePrimaryLight = Color(0xFF33E286)
|
||||
|
||||
// Dark
|
||||
val primaryDark = Color(0xFF33E286)
|
||||
val onPrimaryDark = Color(0xFF00391B)
|
||||
val primaryContainerDark = Color(0xFF005229)
|
||||
val onPrimaryContainerDark = Color(0xFF5CFFA0)
|
||||
val secondaryDark = Color(0xFFB5CCBA)
|
||||
val onSecondaryDark = Color(0xFF213528)
|
||||
val secondaryContainerDark = Color(0xFF374B3D)
|
||||
val onSecondaryContainerDark = Color(0xFFD1E8D5)
|
||||
val tertiaryDark = Color(0xFFA4CDDD)
|
||||
val onTertiaryDark = Color(0xFF053542)
|
||||
val tertiaryContainerDark = Color(0xFF234C59)
|
||||
val onTertiaryContainerDark = Color(0xFFBFE9F9)
|
||||
val errorDark = Color(0xFFFFB4A9)
|
||||
val onErrorDark = Color(0xFF680003)
|
||||
val errorContainerDark = Color(0xFF930006)
|
||||
val onErrorContainerDark = Color(0xFFFFDAD4)
|
||||
val backgroundDark = Color(0xFF191C19)
|
||||
val onBackgroundDark = Color(0xFFE1E3DE)
|
||||
val surfaceDark = Color(0xFF191C19)
|
||||
val onSurfaceDark = Color(0xFFE1E3DE)
|
||||
val surfaceVariantDark = Color(0xFF414942)
|
||||
val onSurfaceVariantDark = Color(0xFFC1C9C0)
|
||||
val outlineDark = Color(0xFF8B938B)
|
||||
val inverseSurfaceDark = Color(0xFFE1E3DE)
|
||||
val inverseOnSurfaceDark = Color(0xFF191C19)
|
||||
val inversePrimaryDark = Color(0xFF006D39)
|
||||
|
||||
// Light Medium Contrast
|
||||
val primaryLightMediumContrast = Color(0xFF004F28)
|
||||
val onPrimaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val primaryContainerLightMediumContrast = Color(0xFF1F864D)
|
||||
val onPrimaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val secondaryLightMediumContrast = Color(0xFF334839)
|
||||
val onSecondaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLightMediumContrast = Color(0xFF647A6A)
|
||||
val onSecondaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryLightMediumContrast = Color(0xFF1E4855)
|
||||
val onTertiaryLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLightMediumContrast = Color(0xFF537A88)
|
||||
val onTertiaryContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val errorLightMediumContrast = Color(0xFF8C0009)
|
||||
val onErrorLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val errorContainerLightMediumContrast = Color(0xFFDA342E)
|
||||
val onErrorContainerLightMediumContrast = Color(0xFFFFFFFF)
|
||||
val backgroundLightMediumContrast = Color(0xFFFBFDF8)
|
||||
val onBackgroundLightMediumContrast = Color(0xFF191C19)
|
||||
val surfaceLightMediumContrast = Color(0xFFFBFDF8)
|
||||
val onSurfaceLightMediumContrast = Color(0xFF191C19)
|
||||
val surfaceVariantLightMediumContrast = Color(0xFFDDE5DB)
|
||||
val onSurfaceVariantLightMediumContrast = Color(0xFF3D453E)
|
||||
val outlineLightMediumContrast = Color(0xFF596159)
|
||||
val inverseSurfaceLightMediumContrast = Color(0xFF2E312E)
|
||||
val inverseOnSurfaceLightMediumContrast = Color(0xFFF0F1EC)
|
||||
val inversePrimaryLightMediumContrast = Color(0xFF33E286)
|
||||
|
||||
// Dark Medium Contrast
|
||||
val primaryDarkMediumContrast = Color(0xFF3FE78B)
|
||||
val onPrimaryDarkMediumContrast = Color(0xFF001B09)
|
||||
val primaryContainerDarkMediumContrast = Color(0xFF00AA55)
|
||||
val onPrimaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val secondaryDarkMediumContrast = Color(0xFFBAD0BF)
|
||||
val onSecondaryDarkMediumContrast = Color(0xFF071A0F)
|
||||
val secondaryContainerDarkMediumContrast = Color(0xFF809686)
|
||||
val onSecondaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val tertiaryDarkMediumContrast = Color(0xFFA8D1E1)
|
||||
val onTertiaryDarkMediumContrast = Color(0xFF001921)
|
||||
val tertiaryContainerDarkMediumContrast = Color(0xFF6F97A6)
|
||||
val onTertiaryContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val errorDarkMediumContrast = Color(0xFFFFBAB1)
|
||||
val onErrorDarkMediumContrast = Color(0xFF370001)
|
||||
val errorContainerDarkMediumContrast = Color(0xFFFF5449)
|
||||
val onErrorContainerDarkMediumContrast = Color(0xFF000000)
|
||||
val backgroundDarkMediumContrast = Color(0xFF191C19)
|
||||
val onBackgroundDarkMediumContrast = Color(0xFFE1E3DE)
|
||||
val surfaceDarkMediumContrast = Color(0xFF191C19)
|
||||
val onSurfaceDarkMediumContrast = Color(0xFFFCFEF9)
|
||||
val surfaceVariantDarkMediumContrast = Color(0xFF414942)
|
||||
val onSurfaceVariantDarkMediumContrast = Color(0xFFC5CDC4)
|
||||
val outlineDarkMediumContrast = Color(0xFF9DA59D)
|
||||
val inverseSurfaceDarkMediumContrast = Color(0xFFE1E3DE)
|
||||
val inverseOnSurfaceDarkMediumContrast = Color(0xFF191C19)
|
||||
val inversePrimaryDarkMediumContrast = Color(0xFF00542A)
|
||||
|
||||
// Light High Contrast
|
||||
val primaryLightHighContrast = Color(0xFF002913)
|
||||
val onPrimaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val primaryContainerLightHighContrast = Color(0xFF004F28)
|
||||
val onPrimaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val secondaryLightHighContrast = Color(0xFF132719)
|
||||
val onSecondaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val secondaryContainerLightHighContrast = Color(0xFF334839)
|
||||
val onSecondaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryLightHighContrast = Color(0xFF002732)
|
||||
val onTertiaryLightHighContrast = Color(0xFFFFFFFF)
|
||||
val tertiaryContainerLightHighContrast = Color(0xFF1E4855)
|
||||
val onTertiaryContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val errorLightHighContrast = Color(0xFF4E0002)
|
||||
val onErrorLightHighContrast = Color(0xFFFFFFFF)
|
||||
val errorContainerLightHighContrast = Color(0xFF8C0009)
|
||||
val onErrorContainerLightHighContrast = Color(0xFFFFFFFF)
|
||||
val backgroundLightHighContrast = Color(0xFFFBFDF8)
|
||||
val onBackgroundLightHighContrast = Color(0xFF191C19)
|
||||
val surfaceLightHighContrast = Color(0xFFFBFDF8)
|
||||
val onSurfaceLightHighContrast = Color(0xFF000000)
|
||||
val surfaceVariantLightHighContrast = Color(0xFFDDE5DB)
|
||||
val onSurfaceVariantLightHighContrast = Color(0xFF1E2620)
|
||||
val outlineLightHighContrast = Color(0xFF3D453E)
|
||||
val inverseSurfaceLightHighContrast = Color(0xFF2E312E)
|
||||
val inverseOnSurfaceLightHighContrast = Color(0xFFFFFFFF)
|
||||
val inversePrimaryLightHighContrast = Color(0xFFBBFFCA)
|
||||
|
||||
// Dark High Contrast
|
||||
val primaryDarkHighContrast = Color(0xFFEDFFED)
|
||||
val onPrimaryDarkHighContrast = Color(0xFF000000)
|
||||
val primaryContainerDarkHighContrast = Color(0xFF33E286)
|
||||
val onPrimaryContainerDarkHighContrast = Color(0xFF000000)
|
||||
val secondaryDarkHighContrast = Color(0xFFEEFFF0)
|
||||
val onSecondaryDarkHighContrast = Color(0xFF000000)
|
||||
val secondaryContainerDarkHighContrast = Color(0xFFBAD0BF)
|
||||
val onSecondaryContainerDarkHighContrast = Color(0xFF000000)
|
||||
val tertiaryDarkHighContrast = Color(0xFFF2FCFF)
|
||||
val onTertiaryDarkHighContrast = Color(0xFF000000)
|
||||
val tertiaryContainerDarkHighContrast = Color(0xFFA8D1E1)
|
||||
val onTertiaryContainerDarkHighContrast = Color(0xFF000000)
|
||||
val errorDarkHighContrast = Color(0xFFFFF9F9)
|
||||
val onErrorDarkHighContrast = Color(0xFF000000)
|
||||
val errorContainerDarkHighContrast = Color(0xFFFFBAB1)
|
||||
val onErrorContainerDarkHighContrast = Color(0xFF000000)
|
||||
val backgroundDarkHighContrast = Color(0xFF191C19)
|
||||
val onBackgroundDarkHighContrast = Color(0xFFE1E3DE)
|
||||
val surfaceDarkHighContrast = Color(0xFF191C19)
|
||||
val onSurfaceDarkHighContrast = Color(0xFFFFFFFF)
|
||||
val surfaceVariantDarkHighContrast = Color(0xFF414942)
|
||||
val onSurfaceVariantDarkHighContrast = Color(0xFFF7FFF4)
|
||||
val outlineDarkHighContrast = Color(0xFFC5CDC4)
|
||||
val inverseSurfaceDarkHighContrast = Color(0xFFE1E3DE)
|
||||
val inverseOnSurfaceDarkHighContrast = Color(0xFF000000)
|
||||
val inversePrimaryDarkHighContrast = Color(0xFF003118)
|
||||
}
|
||||
@@ -3,86 +3,33 @@ package eu.darken.capod.common.theming
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Color(0xFF3F7AFF),
|
||||
onPrimary = Color(0xFFFFFFFF),
|
||||
primaryContainer = Color(0xFFDAE2FF),
|
||||
onPrimaryContainer = Color(0xFF00174B),
|
||||
secondary = Color(0xFF715C00),
|
||||
onSecondary = Color(0xFFFFFFFF),
|
||||
secondaryContainer = Color(0xFFFFE16C),
|
||||
onSecondaryContainer = Color(0xFF231B00),
|
||||
tertiary = Color(0xFF006D39),
|
||||
onTertiary = Color(0xFFFFFFFF),
|
||||
tertiaryContainer = Color(0xFF5CFFA0),
|
||||
onTertiaryContainer = Color(0xFF00210D),
|
||||
error = Color(0xFFBA1B1B),
|
||||
errorContainer = Color(0xFFFFDAD4),
|
||||
onError = Color(0xFFFFFFFF),
|
||||
onErrorContainer = Color(0xFF410001),
|
||||
background = Color(0xFFFEFBFF),
|
||||
onBackground = Color(0xFF1B1B1F),
|
||||
surface = Color(0xFFFEFBFF),
|
||||
onSurface = Color(0xFF1B1B1F),
|
||||
surfaceVariant = Color(0xFFE2E2EC),
|
||||
onSurfaceVariant = Color(0xFF44464E),
|
||||
outline = Color(0xFF75767F),
|
||||
inverseSurface = Color(0xFF303033),
|
||||
inverseOnSurface = Color(0xFFF2F0F5),
|
||||
inversePrimary = Color(0xFFB1C5FF),
|
||||
)
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Color(0xFF3F7AFF),
|
||||
onPrimary = Color(0xFF002A78),
|
||||
primaryContainer = Color(0xFF003EA7),
|
||||
onPrimaryContainer = Color(0xFFDAE2FF),
|
||||
secondary = Color(0xFFE9C426),
|
||||
onSecondary = Color(0xFF3B2F00),
|
||||
secondaryContainer = Color(0xFF564500),
|
||||
onSecondaryContainer = Color(0xFFFFE16C),
|
||||
tertiary = Color(0xFF33E286),
|
||||
onTertiary = Color(0xFF00391B),
|
||||
tertiaryContainer = Color(0xFF005229),
|
||||
onTertiaryContainer = Color(0xFF5CFFA0),
|
||||
error = Color(0xFFFFB4A9),
|
||||
errorContainer = Color(0xFF930006),
|
||||
onError = Color(0xFF680003),
|
||||
onErrorContainer = Color(0xFFFFDAD4),
|
||||
background = Color(0xFF1B1B1F),
|
||||
onBackground = Color(0xFFE3E1E6),
|
||||
surface = Color(0xFF1B1B1F),
|
||||
onSurface = Color(0xFFE3E1E6),
|
||||
surfaceVariant = Color(0xFF44464E),
|
||||
onSurfaceVariant = Color(0xFFC6C6D0),
|
||||
outline = Color(0xFF8F909A),
|
||||
inverseSurface = Color(0xFFE3E1E6),
|
||||
inverseOnSurface = Color(0xFF1B1B1F),
|
||||
inversePrimary = Color(0xFF0054D9),
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun CapodTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
dynamicColor: Boolean = true,
|
||||
state: ThemeState = ThemeState(),
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
val darkTheme = when (state.mode) {
|
||||
ThemeMode.SYSTEM -> isSystemInDarkTheme()
|
||||
ThemeMode.DARK -> true
|
||||
ThemeMode.LIGHT -> false
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
val dynamicColors = state.style == ThemeStyle.MATERIAL_YOU && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||
|
||||
val colorScheme = if (dynamicColors) {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
} else {
|
||||
if (darkTheme) {
|
||||
ThemeColorProvider.getDarkColorScheme(state.color, state.style)
|
||||
} else {
|
||||
ThemeColorProvider.getLightColorScheme(state.color, state.style)
|
||||
}
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package eu.darken.capod.common.theming
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import eu.darken.capod.R
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class ThemeColor(
|
||||
@StringRes val labelRes: Int
|
||||
) {
|
||||
@Json(name = "theme.color.blue") BLUE(R.string.ui_theme_color_blue_label),
|
||||
@Json(name = "theme.color.green") GREEN(R.string.ui_theme_color_green_label),
|
||||
@Json(name = "theme.color.amber") AMBER(R.string.ui_theme_color_amber_label),
|
||||
}
|
||||
@@ -0,0 +1,560 @@
|
||||
package eu.darken.capod.common.theming
|
||||
|
||||
import androidx.compose.material3.ColorScheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
|
||||
object ThemeColorProvider {
|
||||
|
||||
fun getLightColorScheme(color: ThemeColor, style: ThemeStyle): ColorScheme = when (color) {
|
||||
ThemeColor.BLUE -> blueLightScheme(style)
|
||||
ThemeColor.GREEN -> greenLightScheme(style)
|
||||
ThemeColor.AMBER -> amberLightScheme(style)
|
||||
}
|
||||
|
||||
fun getDarkColorScheme(color: ThemeColor, style: ThemeStyle): ColorScheme = when (color) {
|
||||
ThemeColor.BLUE -> blueDarkScheme(style)
|
||||
ThemeColor.GREEN -> greenDarkScheme(style)
|
||||
ThemeColor.AMBER -> amberDarkScheme(style)
|
||||
}
|
||||
|
||||
private fun blueLightScheme(style: ThemeStyle): ColorScheme {
|
||||
val c = CapodColorsBlue
|
||||
return when (style) {
|
||||
ThemeStyle.MEDIUM_CONTRAST -> lightColorScheme(
|
||||
primary = c.primaryLightMediumContrast,
|
||||
onPrimary = c.onPrimaryLightMediumContrast,
|
||||
primaryContainer = c.primaryContainerLightMediumContrast,
|
||||
onPrimaryContainer = c.onPrimaryContainerLightMediumContrast,
|
||||
secondary = c.secondaryLightMediumContrast,
|
||||
onSecondary = c.onSecondaryLightMediumContrast,
|
||||
secondaryContainer = c.secondaryContainerLightMediumContrast,
|
||||
onSecondaryContainer = c.onSecondaryContainerLightMediumContrast,
|
||||
tertiary = c.tertiaryLightMediumContrast,
|
||||
onTertiary = c.onTertiaryLightMediumContrast,
|
||||
tertiaryContainer = c.tertiaryContainerLightMediumContrast,
|
||||
onTertiaryContainer = c.onTertiaryContainerLightMediumContrast,
|
||||
error = c.errorLightMediumContrast,
|
||||
onError = c.onErrorLightMediumContrast,
|
||||
errorContainer = c.errorContainerLightMediumContrast,
|
||||
onErrorContainer = c.onErrorContainerLightMediumContrast,
|
||||
background = c.backgroundLightMediumContrast,
|
||||
onBackground = c.onBackgroundLightMediumContrast,
|
||||
surface = c.surfaceLightMediumContrast,
|
||||
onSurface = c.onSurfaceLightMediumContrast,
|
||||
surfaceVariant = c.surfaceVariantLightMediumContrast,
|
||||
onSurfaceVariant = c.onSurfaceVariantLightMediumContrast,
|
||||
outline = c.outlineLightMediumContrast,
|
||||
inverseSurface = c.inverseSurfaceLightMediumContrast,
|
||||
inverseOnSurface = c.inverseOnSurfaceLightMediumContrast,
|
||||
inversePrimary = c.inversePrimaryLightMediumContrast,
|
||||
)
|
||||
ThemeStyle.HIGH_CONTRAST -> lightColorScheme(
|
||||
primary = c.primaryLightHighContrast,
|
||||
onPrimary = c.onPrimaryLightHighContrast,
|
||||
primaryContainer = c.primaryContainerLightHighContrast,
|
||||
onPrimaryContainer = c.onPrimaryContainerLightHighContrast,
|
||||
secondary = c.secondaryLightHighContrast,
|
||||
onSecondary = c.onSecondaryLightHighContrast,
|
||||
secondaryContainer = c.secondaryContainerLightHighContrast,
|
||||
onSecondaryContainer = c.onSecondaryContainerLightHighContrast,
|
||||
tertiary = c.tertiaryLightHighContrast,
|
||||
onTertiary = c.onTertiaryLightHighContrast,
|
||||
tertiaryContainer = c.tertiaryContainerLightHighContrast,
|
||||
onTertiaryContainer = c.onTertiaryContainerLightHighContrast,
|
||||
error = c.errorLightHighContrast,
|
||||
onError = c.onErrorLightHighContrast,
|
||||
errorContainer = c.errorContainerLightHighContrast,
|
||||
onErrorContainer = c.onErrorContainerLightHighContrast,
|
||||
background = c.backgroundLightHighContrast,
|
||||
onBackground = c.onBackgroundLightHighContrast,
|
||||
surface = c.surfaceLightHighContrast,
|
||||
onSurface = c.onSurfaceLightHighContrast,
|
||||
surfaceVariant = c.surfaceVariantLightHighContrast,
|
||||
onSurfaceVariant = c.onSurfaceVariantLightHighContrast,
|
||||
outline = c.outlineLightHighContrast,
|
||||
inverseSurface = c.inverseSurfaceLightHighContrast,
|
||||
inverseOnSurface = c.inverseOnSurfaceLightHighContrast,
|
||||
inversePrimary = c.inversePrimaryLightHighContrast,
|
||||
)
|
||||
else -> lightColorScheme(
|
||||
primary = c.primaryLight,
|
||||
onPrimary = c.onPrimaryLight,
|
||||
primaryContainer = c.primaryContainerLight,
|
||||
onPrimaryContainer = c.onPrimaryContainerLight,
|
||||
secondary = c.secondaryLight,
|
||||
onSecondary = c.onSecondaryLight,
|
||||
secondaryContainer = c.secondaryContainerLight,
|
||||
onSecondaryContainer = c.onSecondaryContainerLight,
|
||||
tertiary = c.tertiaryLight,
|
||||
onTertiary = c.onTertiaryLight,
|
||||
tertiaryContainer = c.tertiaryContainerLight,
|
||||
onTertiaryContainer = c.onTertiaryContainerLight,
|
||||
error = c.errorLight,
|
||||
onError = c.onErrorLight,
|
||||
errorContainer = c.errorContainerLight,
|
||||
onErrorContainer = c.onErrorContainerLight,
|
||||
background = c.backgroundLight,
|
||||
onBackground = c.onBackgroundLight,
|
||||
surface = c.surfaceLight,
|
||||
onSurface = c.onSurfaceLight,
|
||||
surfaceVariant = c.surfaceVariantLight,
|
||||
onSurfaceVariant = c.onSurfaceVariantLight,
|
||||
outline = c.outlineLight,
|
||||
inverseSurface = c.inverseSurfaceLight,
|
||||
inverseOnSurface = c.inverseOnSurfaceLight,
|
||||
inversePrimary = c.inversePrimaryLight,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun blueDarkScheme(style: ThemeStyle): ColorScheme {
|
||||
val c = CapodColorsBlue
|
||||
return when (style) {
|
||||
ThemeStyle.MEDIUM_CONTRAST -> darkColorScheme(
|
||||
primary = c.primaryDarkMediumContrast,
|
||||
onPrimary = c.onPrimaryDarkMediumContrast,
|
||||
primaryContainer = c.primaryContainerDarkMediumContrast,
|
||||
onPrimaryContainer = c.onPrimaryContainerDarkMediumContrast,
|
||||
secondary = c.secondaryDarkMediumContrast,
|
||||
onSecondary = c.onSecondaryDarkMediumContrast,
|
||||
secondaryContainer = c.secondaryContainerDarkMediumContrast,
|
||||
onSecondaryContainer = c.onSecondaryContainerDarkMediumContrast,
|
||||
tertiary = c.tertiaryDarkMediumContrast,
|
||||
onTertiary = c.onTertiaryDarkMediumContrast,
|
||||
tertiaryContainer = c.tertiaryContainerDarkMediumContrast,
|
||||
onTertiaryContainer = c.onTertiaryContainerDarkMediumContrast,
|
||||
error = c.errorDarkMediumContrast,
|
||||
onError = c.onErrorDarkMediumContrast,
|
||||
errorContainer = c.errorContainerDarkMediumContrast,
|
||||
onErrorContainer = c.onErrorContainerDarkMediumContrast,
|
||||
background = c.backgroundDarkMediumContrast,
|
||||
onBackground = c.onBackgroundDarkMediumContrast,
|
||||
surface = c.surfaceDarkMediumContrast,
|
||||
onSurface = c.onSurfaceDarkMediumContrast,
|
||||
surfaceVariant = c.surfaceVariantDarkMediumContrast,
|
||||
onSurfaceVariant = c.onSurfaceVariantDarkMediumContrast,
|
||||
outline = c.outlineDarkMediumContrast,
|
||||
inverseSurface = c.inverseSurfaceDarkMediumContrast,
|
||||
inverseOnSurface = c.inverseOnSurfaceDarkMediumContrast,
|
||||
inversePrimary = c.inversePrimaryDarkMediumContrast,
|
||||
)
|
||||
ThemeStyle.HIGH_CONTRAST -> darkColorScheme(
|
||||
primary = c.primaryDarkHighContrast,
|
||||
onPrimary = c.onPrimaryDarkHighContrast,
|
||||
primaryContainer = c.primaryContainerDarkHighContrast,
|
||||
onPrimaryContainer = c.onPrimaryContainerDarkHighContrast,
|
||||
secondary = c.secondaryDarkHighContrast,
|
||||
onSecondary = c.onSecondaryDarkHighContrast,
|
||||
secondaryContainer = c.secondaryContainerDarkHighContrast,
|
||||
onSecondaryContainer = c.onSecondaryContainerDarkHighContrast,
|
||||
tertiary = c.tertiaryDarkHighContrast,
|
||||
onTertiary = c.onTertiaryDarkHighContrast,
|
||||
tertiaryContainer = c.tertiaryContainerDarkHighContrast,
|
||||
onTertiaryContainer = c.onTertiaryContainerDarkHighContrast,
|
||||
error = c.errorDarkHighContrast,
|
||||
onError = c.onErrorDarkHighContrast,
|
||||
errorContainer = c.errorContainerDarkHighContrast,
|
||||
onErrorContainer = c.onErrorContainerDarkHighContrast,
|
||||
background = c.backgroundDarkHighContrast,
|
||||
onBackground = c.onBackgroundDarkHighContrast,
|
||||
surface = c.surfaceDarkHighContrast,
|
||||
onSurface = c.onSurfaceDarkHighContrast,
|
||||
surfaceVariant = c.surfaceVariantDarkHighContrast,
|
||||
onSurfaceVariant = c.onSurfaceVariantDarkHighContrast,
|
||||
outline = c.outlineDarkHighContrast,
|
||||
inverseSurface = c.inverseSurfaceDarkHighContrast,
|
||||
inverseOnSurface = c.inverseOnSurfaceDarkHighContrast,
|
||||
inversePrimary = c.inversePrimaryDarkHighContrast,
|
||||
)
|
||||
else -> darkColorScheme(
|
||||
primary = c.primaryDark,
|
||||
onPrimary = c.onPrimaryDark,
|
||||
primaryContainer = c.primaryContainerDark,
|
||||
onPrimaryContainer = c.onPrimaryContainerDark,
|
||||
secondary = c.secondaryDark,
|
||||
onSecondary = c.onSecondaryDark,
|
||||
secondaryContainer = c.secondaryContainerDark,
|
||||
onSecondaryContainer = c.onSecondaryContainerDark,
|
||||
tertiary = c.tertiaryDark,
|
||||
onTertiary = c.onTertiaryDark,
|
||||
tertiaryContainer = c.tertiaryContainerDark,
|
||||
onTertiaryContainer = c.onTertiaryContainerDark,
|
||||
error = c.errorDark,
|
||||
onError = c.onErrorDark,
|
||||
errorContainer = c.errorContainerDark,
|
||||
onErrorContainer = c.onErrorContainerDark,
|
||||
background = c.backgroundDark,
|
||||
onBackground = c.onBackgroundDark,
|
||||
surface = c.surfaceDark,
|
||||
onSurface = c.onSurfaceDark,
|
||||
surfaceVariant = c.surfaceVariantDark,
|
||||
onSurfaceVariant = c.onSurfaceVariantDark,
|
||||
outline = c.outlineDark,
|
||||
inverseSurface = c.inverseSurfaceDark,
|
||||
inverseOnSurface = c.inverseOnSurfaceDark,
|
||||
inversePrimary = c.inversePrimaryDark,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun greenLightScheme(style: ThemeStyle): ColorScheme {
|
||||
val c = CapodColorsGreen
|
||||
return when (style) {
|
||||
ThemeStyle.MEDIUM_CONTRAST -> lightColorScheme(
|
||||
primary = c.primaryLightMediumContrast,
|
||||
onPrimary = c.onPrimaryLightMediumContrast,
|
||||
primaryContainer = c.primaryContainerLightMediumContrast,
|
||||
onPrimaryContainer = c.onPrimaryContainerLightMediumContrast,
|
||||
secondary = c.secondaryLightMediumContrast,
|
||||
onSecondary = c.onSecondaryLightMediumContrast,
|
||||
secondaryContainer = c.secondaryContainerLightMediumContrast,
|
||||
onSecondaryContainer = c.onSecondaryContainerLightMediumContrast,
|
||||
tertiary = c.tertiaryLightMediumContrast,
|
||||
onTertiary = c.onTertiaryLightMediumContrast,
|
||||
tertiaryContainer = c.tertiaryContainerLightMediumContrast,
|
||||
onTertiaryContainer = c.onTertiaryContainerLightMediumContrast,
|
||||
error = c.errorLightMediumContrast,
|
||||
onError = c.onErrorLightMediumContrast,
|
||||
errorContainer = c.errorContainerLightMediumContrast,
|
||||
onErrorContainer = c.onErrorContainerLightMediumContrast,
|
||||
background = c.backgroundLightMediumContrast,
|
||||
onBackground = c.onBackgroundLightMediumContrast,
|
||||
surface = c.surfaceLightMediumContrast,
|
||||
onSurface = c.onSurfaceLightMediumContrast,
|
||||
surfaceVariant = c.surfaceVariantLightMediumContrast,
|
||||
onSurfaceVariant = c.onSurfaceVariantLightMediumContrast,
|
||||
outline = c.outlineLightMediumContrast,
|
||||
inverseSurface = c.inverseSurfaceLightMediumContrast,
|
||||
inverseOnSurface = c.inverseOnSurfaceLightMediumContrast,
|
||||
inversePrimary = c.inversePrimaryLightMediumContrast,
|
||||
)
|
||||
ThemeStyle.HIGH_CONTRAST -> lightColorScheme(
|
||||
primary = c.primaryLightHighContrast,
|
||||
onPrimary = c.onPrimaryLightHighContrast,
|
||||
primaryContainer = c.primaryContainerLightHighContrast,
|
||||
onPrimaryContainer = c.onPrimaryContainerLightHighContrast,
|
||||
secondary = c.secondaryLightHighContrast,
|
||||
onSecondary = c.onSecondaryLightHighContrast,
|
||||
secondaryContainer = c.secondaryContainerLightHighContrast,
|
||||
onSecondaryContainer = c.onSecondaryContainerLightHighContrast,
|
||||
tertiary = c.tertiaryLightHighContrast,
|
||||
onTertiary = c.onTertiaryLightHighContrast,
|
||||
tertiaryContainer = c.tertiaryContainerLightHighContrast,
|
||||
onTertiaryContainer = c.onTertiaryContainerLightHighContrast,
|
||||
error = c.errorLightHighContrast,
|
||||
onError = c.onErrorLightHighContrast,
|
||||
errorContainer = c.errorContainerLightHighContrast,
|
||||
onErrorContainer = c.onErrorContainerLightHighContrast,
|
||||
background = c.backgroundLightHighContrast,
|
||||
onBackground = c.onBackgroundLightHighContrast,
|
||||
surface = c.surfaceLightHighContrast,
|
||||
onSurface = c.onSurfaceLightHighContrast,
|
||||
surfaceVariant = c.surfaceVariantLightHighContrast,
|
||||
onSurfaceVariant = c.onSurfaceVariantLightHighContrast,
|
||||
outline = c.outlineLightHighContrast,
|
||||
inverseSurface = c.inverseSurfaceLightHighContrast,
|
||||
inverseOnSurface = c.inverseOnSurfaceLightHighContrast,
|
||||
inversePrimary = c.inversePrimaryLightHighContrast,
|
||||
)
|
||||
else -> lightColorScheme(
|
||||
primary = c.primaryLight,
|
||||
onPrimary = c.onPrimaryLight,
|
||||
primaryContainer = c.primaryContainerLight,
|
||||
onPrimaryContainer = c.onPrimaryContainerLight,
|
||||
secondary = c.secondaryLight,
|
||||
onSecondary = c.onSecondaryLight,
|
||||
secondaryContainer = c.secondaryContainerLight,
|
||||
onSecondaryContainer = c.onSecondaryContainerLight,
|
||||
tertiary = c.tertiaryLight,
|
||||
onTertiary = c.onTertiaryLight,
|
||||
tertiaryContainer = c.tertiaryContainerLight,
|
||||
onTertiaryContainer = c.onTertiaryContainerLight,
|
||||
error = c.errorLight,
|
||||
onError = c.onErrorLight,
|
||||
errorContainer = c.errorContainerLight,
|
||||
onErrorContainer = c.onErrorContainerLight,
|
||||
background = c.backgroundLight,
|
||||
onBackground = c.onBackgroundLight,
|
||||
surface = c.surfaceLight,
|
||||
onSurface = c.onSurfaceLight,
|
||||
surfaceVariant = c.surfaceVariantLight,
|
||||
onSurfaceVariant = c.onSurfaceVariantLight,
|
||||
outline = c.outlineLight,
|
||||
inverseSurface = c.inverseSurfaceLight,
|
||||
inverseOnSurface = c.inverseOnSurfaceLight,
|
||||
inversePrimary = c.inversePrimaryLight,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun greenDarkScheme(style: ThemeStyle): ColorScheme {
|
||||
val c = CapodColorsGreen
|
||||
return when (style) {
|
||||
ThemeStyle.MEDIUM_CONTRAST -> darkColorScheme(
|
||||
primary = c.primaryDarkMediumContrast,
|
||||
onPrimary = c.onPrimaryDarkMediumContrast,
|
||||
primaryContainer = c.primaryContainerDarkMediumContrast,
|
||||
onPrimaryContainer = c.onPrimaryContainerDarkMediumContrast,
|
||||
secondary = c.secondaryDarkMediumContrast,
|
||||
onSecondary = c.onSecondaryDarkMediumContrast,
|
||||
secondaryContainer = c.secondaryContainerDarkMediumContrast,
|
||||
onSecondaryContainer = c.onSecondaryContainerDarkMediumContrast,
|
||||
tertiary = c.tertiaryDarkMediumContrast,
|
||||
onTertiary = c.onTertiaryDarkMediumContrast,
|
||||
tertiaryContainer = c.tertiaryContainerDarkMediumContrast,
|
||||
onTertiaryContainer = c.onTertiaryContainerDarkMediumContrast,
|
||||
error = c.errorDarkMediumContrast,
|
||||
onError = c.onErrorDarkMediumContrast,
|
||||
errorContainer = c.errorContainerDarkMediumContrast,
|
||||
onErrorContainer = c.onErrorContainerDarkMediumContrast,
|
||||
background = c.backgroundDarkMediumContrast,
|
||||
onBackground = c.onBackgroundDarkMediumContrast,
|
||||
surface = c.surfaceDarkMediumContrast,
|
||||
onSurface = c.onSurfaceDarkMediumContrast,
|
||||
surfaceVariant = c.surfaceVariantDarkMediumContrast,
|
||||
onSurfaceVariant = c.onSurfaceVariantDarkMediumContrast,
|
||||
outline = c.outlineDarkMediumContrast,
|
||||
inverseSurface = c.inverseSurfaceDarkMediumContrast,
|
||||
inverseOnSurface = c.inverseOnSurfaceDarkMediumContrast,
|
||||
inversePrimary = c.inversePrimaryDarkMediumContrast,
|
||||
)
|
||||
ThemeStyle.HIGH_CONTRAST -> darkColorScheme(
|
||||
primary = c.primaryDarkHighContrast,
|
||||
onPrimary = c.onPrimaryDarkHighContrast,
|
||||
primaryContainer = c.primaryContainerDarkHighContrast,
|
||||
onPrimaryContainer = c.onPrimaryContainerDarkHighContrast,
|
||||
secondary = c.secondaryDarkHighContrast,
|
||||
onSecondary = c.onSecondaryDarkHighContrast,
|
||||
secondaryContainer = c.secondaryContainerDarkHighContrast,
|
||||
onSecondaryContainer = c.onSecondaryContainerDarkHighContrast,
|
||||
tertiary = c.tertiaryDarkHighContrast,
|
||||
onTertiary = c.onTertiaryDarkHighContrast,
|
||||
tertiaryContainer = c.tertiaryContainerDarkHighContrast,
|
||||
onTertiaryContainer = c.onTertiaryContainerDarkHighContrast,
|
||||
error = c.errorDarkHighContrast,
|
||||
onError = c.onErrorDarkHighContrast,
|
||||
errorContainer = c.errorContainerDarkHighContrast,
|
||||
onErrorContainer = c.onErrorContainerDarkHighContrast,
|
||||
background = c.backgroundDarkHighContrast,
|
||||
onBackground = c.onBackgroundDarkHighContrast,
|
||||
surface = c.surfaceDarkHighContrast,
|
||||
onSurface = c.onSurfaceDarkHighContrast,
|
||||
surfaceVariant = c.surfaceVariantDarkHighContrast,
|
||||
onSurfaceVariant = c.onSurfaceVariantDarkHighContrast,
|
||||
outline = c.outlineDarkHighContrast,
|
||||
inverseSurface = c.inverseSurfaceDarkHighContrast,
|
||||
inverseOnSurface = c.inverseOnSurfaceDarkHighContrast,
|
||||
inversePrimary = c.inversePrimaryDarkHighContrast,
|
||||
)
|
||||
else -> darkColorScheme(
|
||||
primary = c.primaryDark,
|
||||
onPrimary = c.onPrimaryDark,
|
||||
primaryContainer = c.primaryContainerDark,
|
||||
onPrimaryContainer = c.onPrimaryContainerDark,
|
||||
secondary = c.secondaryDark,
|
||||
onSecondary = c.onSecondaryDark,
|
||||
secondaryContainer = c.secondaryContainerDark,
|
||||
onSecondaryContainer = c.onSecondaryContainerDark,
|
||||
tertiary = c.tertiaryDark,
|
||||
onTertiary = c.onTertiaryDark,
|
||||
tertiaryContainer = c.tertiaryContainerDark,
|
||||
onTertiaryContainer = c.onTertiaryContainerDark,
|
||||
error = c.errorDark,
|
||||
onError = c.onErrorDark,
|
||||
errorContainer = c.errorContainerDark,
|
||||
onErrorContainer = c.onErrorContainerDark,
|
||||
background = c.backgroundDark,
|
||||
onBackground = c.onBackgroundDark,
|
||||
surface = c.surfaceDark,
|
||||
onSurface = c.onSurfaceDark,
|
||||
surfaceVariant = c.surfaceVariantDark,
|
||||
onSurfaceVariant = c.onSurfaceVariantDark,
|
||||
outline = c.outlineDark,
|
||||
inverseSurface = c.inverseSurfaceDark,
|
||||
inverseOnSurface = c.inverseOnSurfaceDark,
|
||||
inversePrimary = c.inversePrimaryDark,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun amberLightScheme(style: ThemeStyle): ColorScheme {
|
||||
val c = CapodColorsAmber
|
||||
return when (style) {
|
||||
ThemeStyle.MEDIUM_CONTRAST -> lightColorScheme(
|
||||
primary = c.primaryLightMediumContrast,
|
||||
onPrimary = c.onPrimaryLightMediumContrast,
|
||||
primaryContainer = c.primaryContainerLightMediumContrast,
|
||||
onPrimaryContainer = c.onPrimaryContainerLightMediumContrast,
|
||||
secondary = c.secondaryLightMediumContrast,
|
||||
onSecondary = c.onSecondaryLightMediumContrast,
|
||||
secondaryContainer = c.secondaryContainerLightMediumContrast,
|
||||
onSecondaryContainer = c.onSecondaryContainerLightMediumContrast,
|
||||
tertiary = c.tertiaryLightMediumContrast,
|
||||
onTertiary = c.onTertiaryLightMediumContrast,
|
||||
tertiaryContainer = c.tertiaryContainerLightMediumContrast,
|
||||
onTertiaryContainer = c.onTertiaryContainerLightMediumContrast,
|
||||
error = c.errorLightMediumContrast,
|
||||
onError = c.onErrorLightMediumContrast,
|
||||
errorContainer = c.errorContainerLightMediumContrast,
|
||||
onErrorContainer = c.onErrorContainerLightMediumContrast,
|
||||
background = c.backgroundLightMediumContrast,
|
||||
onBackground = c.onBackgroundLightMediumContrast,
|
||||
surface = c.surfaceLightMediumContrast,
|
||||
onSurface = c.onSurfaceLightMediumContrast,
|
||||
surfaceVariant = c.surfaceVariantLightMediumContrast,
|
||||
onSurfaceVariant = c.onSurfaceVariantLightMediumContrast,
|
||||
outline = c.outlineLightMediumContrast,
|
||||
inverseSurface = c.inverseSurfaceLightMediumContrast,
|
||||
inverseOnSurface = c.inverseOnSurfaceLightMediumContrast,
|
||||
inversePrimary = c.inversePrimaryLightMediumContrast,
|
||||
)
|
||||
ThemeStyle.HIGH_CONTRAST -> lightColorScheme(
|
||||
primary = c.primaryLightHighContrast,
|
||||
onPrimary = c.onPrimaryLightHighContrast,
|
||||
primaryContainer = c.primaryContainerLightHighContrast,
|
||||
onPrimaryContainer = c.onPrimaryContainerLightHighContrast,
|
||||
secondary = c.secondaryLightHighContrast,
|
||||
onSecondary = c.onSecondaryLightHighContrast,
|
||||
secondaryContainer = c.secondaryContainerLightHighContrast,
|
||||
onSecondaryContainer = c.onSecondaryContainerLightHighContrast,
|
||||
tertiary = c.tertiaryLightHighContrast,
|
||||
onTertiary = c.onTertiaryLightHighContrast,
|
||||
tertiaryContainer = c.tertiaryContainerLightHighContrast,
|
||||
onTertiaryContainer = c.onTertiaryContainerLightHighContrast,
|
||||
error = c.errorLightHighContrast,
|
||||
onError = c.onErrorLightHighContrast,
|
||||
errorContainer = c.errorContainerLightHighContrast,
|
||||
onErrorContainer = c.onErrorContainerLightHighContrast,
|
||||
background = c.backgroundLightHighContrast,
|
||||
onBackground = c.onBackgroundLightHighContrast,
|
||||
surface = c.surfaceLightHighContrast,
|
||||
onSurface = c.onSurfaceLightHighContrast,
|
||||
surfaceVariant = c.surfaceVariantLightHighContrast,
|
||||
onSurfaceVariant = c.onSurfaceVariantLightHighContrast,
|
||||
outline = c.outlineLightHighContrast,
|
||||
inverseSurface = c.inverseSurfaceLightHighContrast,
|
||||
inverseOnSurface = c.inverseOnSurfaceLightHighContrast,
|
||||
inversePrimary = c.inversePrimaryLightHighContrast,
|
||||
)
|
||||
else -> lightColorScheme(
|
||||
primary = c.primaryLight,
|
||||
onPrimary = c.onPrimaryLight,
|
||||
primaryContainer = c.primaryContainerLight,
|
||||
onPrimaryContainer = c.onPrimaryContainerLight,
|
||||
secondary = c.secondaryLight,
|
||||
onSecondary = c.onSecondaryLight,
|
||||
secondaryContainer = c.secondaryContainerLight,
|
||||
onSecondaryContainer = c.onSecondaryContainerLight,
|
||||
tertiary = c.tertiaryLight,
|
||||
onTertiary = c.onTertiaryLight,
|
||||
tertiaryContainer = c.tertiaryContainerLight,
|
||||
onTertiaryContainer = c.onTertiaryContainerLight,
|
||||
error = c.errorLight,
|
||||
onError = c.onErrorLight,
|
||||
errorContainer = c.errorContainerLight,
|
||||
onErrorContainer = c.onErrorContainerLight,
|
||||
background = c.backgroundLight,
|
||||
onBackground = c.onBackgroundLight,
|
||||
surface = c.surfaceLight,
|
||||
onSurface = c.onSurfaceLight,
|
||||
surfaceVariant = c.surfaceVariantLight,
|
||||
onSurfaceVariant = c.onSurfaceVariantLight,
|
||||
outline = c.outlineLight,
|
||||
inverseSurface = c.inverseSurfaceLight,
|
||||
inverseOnSurface = c.inverseOnSurfaceLight,
|
||||
inversePrimary = c.inversePrimaryLight,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun amberDarkScheme(style: ThemeStyle): ColorScheme {
|
||||
val c = CapodColorsAmber
|
||||
return when (style) {
|
||||
ThemeStyle.MEDIUM_CONTRAST -> darkColorScheme(
|
||||
primary = c.primaryDarkMediumContrast,
|
||||
onPrimary = c.onPrimaryDarkMediumContrast,
|
||||
primaryContainer = c.primaryContainerDarkMediumContrast,
|
||||
onPrimaryContainer = c.onPrimaryContainerDarkMediumContrast,
|
||||
secondary = c.secondaryDarkMediumContrast,
|
||||
onSecondary = c.onSecondaryDarkMediumContrast,
|
||||
secondaryContainer = c.secondaryContainerDarkMediumContrast,
|
||||
onSecondaryContainer = c.onSecondaryContainerDarkMediumContrast,
|
||||
tertiary = c.tertiaryDarkMediumContrast,
|
||||
onTertiary = c.onTertiaryDarkMediumContrast,
|
||||
tertiaryContainer = c.tertiaryContainerDarkMediumContrast,
|
||||
onTertiaryContainer = c.onTertiaryContainerDarkMediumContrast,
|
||||
error = c.errorDarkMediumContrast,
|
||||
onError = c.onErrorDarkMediumContrast,
|
||||
errorContainer = c.errorContainerDarkMediumContrast,
|
||||
onErrorContainer = c.onErrorContainerDarkMediumContrast,
|
||||
background = c.backgroundDarkMediumContrast,
|
||||
onBackground = c.onBackgroundDarkMediumContrast,
|
||||
surface = c.surfaceDarkMediumContrast,
|
||||
onSurface = c.onSurfaceDarkMediumContrast,
|
||||
surfaceVariant = c.surfaceVariantDarkMediumContrast,
|
||||
onSurfaceVariant = c.onSurfaceVariantDarkMediumContrast,
|
||||
outline = c.outlineDarkMediumContrast,
|
||||
inverseSurface = c.inverseSurfaceDarkMediumContrast,
|
||||
inverseOnSurface = c.inverseOnSurfaceDarkMediumContrast,
|
||||
inversePrimary = c.inversePrimaryDarkMediumContrast,
|
||||
)
|
||||
ThemeStyle.HIGH_CONTRAST -> darkColorScheme(
|
||||
primary = c.primaryDarkHighContrast,
|
||||
onPrimary = c.onPrimaryDarkHighContrast,
|
||||
primaryContainer = c.primaryContainerDarkHighContrast,
|
||||
onPrimaryContainer = c.onPrimaryContainerDarkHighContrast,
|
||||
secondary = c.secondaryDarkHighContrast,
|
||||
onSecondary = c.onSecondaryDarkHighContrast,
|
||||
secondaryContainer = c.secondaryContainerDarkHighContrast,
|
||||
onSecondaryContainer = c.onSecondaryContainerDarkHighContrast,
|
||||
tertiary = c.tertiaryDarkHighContrast,
|
||||
onTertiary = c.onTertiaryDarkHighContrast,
|
||||
tertiaryContainer = c.tertiaryContainerDarkHighContrast,
|
||||
onTertiaryContainer = c.onTertiaryContainerDarkHighContrast,
|
||||
error = c.errorDarkHighContrast,
|
||||
onError = c.onErrorDarkHighContrast,
|
||||
errorContainer = c.errorContainerDarkHighContrast,
|
||||
onErrorContainer = c.onErrorContainerDarkHighContrast,
|
||||
background = c.backgroundDarkHighContrast,
|
||||
onBackground = c.onBackgroundDarkHighContrast,
|
||||
surface = c.surfaceDarkHighContrast,
|
||||
onSurface = c.onSurfaceDarkHighContrast,
|
||||
surfaceVariant = c.surfaceVariantDarkHighContrast,
|
||||
onSurfaceVariant = c.onSurfaceVariantDarkHighContrast,
|
||||
outline = c.outlineDarkHighContrast,
|
||||
inverseSurface = c.inverseSurfaceDarkHighContrast,
|
||||
inverseOnSurface = c.inverseOnSurfaceDarkHighContrast,
|
||||
inversePrimary = c.inversePrimaryDarkHighContrast,
|
||||
)
|
||||
else -> darkColorScheme(
|
||||
primary = c.primaryDark,
|
||||
onPrimary = c.onPrimaryDark,
|
||||
primaryContainer = c.primaryContainerDark,
|
||||
onPrimaryContainer = c.onPrimaryContainerDark,
|
||||
secondary = c.secondaryDark,
|
||||
onSecondary = c.onSecondaryDark,
|
||||
secondaryContainer = c.secondaryContainerDark,
|
||||
onSecondaryContainer = c.onSecondaryContainerDark,
|
||||
tertiary = c.tertiaryDark,
|
||||
onTertiary = c.onTertiaryDark,
|
||||
tertiaryContainer = c.tertiaryContainerDark,
|
||||
onTertiaryContainer = c.onTertiaryContainerDark,
|
||||
error = c.errorDark,
|
||||
onError = c.onErrorDark,
|
||||
errorContainer = c.errorContainerDark,
|
||||
onErrorContainer = c.onErrorContainerDark,
|
||||
background = c.backgroundDark,
|
||||
onBackground = c.onBackgroundDark,
|
||||
surface = c.surfaceDark,
|
||||
onSurface = c.onSurfaceDark,
|
||||
surfaceVariant = c.surfaceVariantDark,
|
||||
onSurfaceVariant = c.onSurfaceVariantDark,
|
||||
outline = c.outlineDark,
|
||||
inverseSurface = c.inverseSurfaceDark,
|
||||
inverseOnSurface = c.inverseOnSurfaceDark,
|
||||
inversePrimary = c.inversePrimaryDark,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package eu.darken.capod.common.theming
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import eu.darken.capod.R
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class ThemeMode(
|
||||
@StringRes val labelRes: Int
|
||||
) {
|
||||
@Json(name = "theme.mode.system") SYSTEM(R.string.ui_theme_mode_system_label),
|
||||
@Json(name = "theme.mode.dark") DARK(R.string.ui_theme_mode_dark_label),
|
||||
@Json(name = "theme.mode.light") LIGHT(R.string.ui_theme_mode_light_label),
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package eu.darken.capod.common.theming
|
||||
|
||||
data class ThemeState(
|
||||
val mode: ThemeMode = ThemeMode.SYSTEM,
|
||||
val style: ThemeStyle = ThemeStyle.DEFAULT,
|
||||
val color: ThemeColor = ThemeColor.BLUE,
|
||||
)
|
||||
@@ -0,0 +1,16 @@
|
||||
package eu.darken.capod.common.theming
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import com.squareup.moshi.Json
|
||||
import com.squareup.moshi.JsonClass
|
||||
import eu.darken.capod.R
|
||||
|
||||
@JsonClass(generateAdapter = false)
|
||||
enum class ThemeStyle(
|
||||
@StringRes val labelRes: Int
|
||||
) {
|
||||
@Json(name = "theme.style.default") DEFAULT(R.string.ui_theme_style_default_label),
|
||||
@Json(name = "theme.style.materialyou") MATERIAL_YOU(R.string.ui_theme_style_materialyou_label),
|
||||
@Json(name = "theme.style.mediumcontrast") MEDIUM_CONTRAST(R.string.ui_theme_style_medium_contrast_label),
|
||||
@Json(name = "theme.style.highcontrast") HIGH_CONTRAST(R.string.ui_theme_style_high_contrast_label),
|
||||
}
|
||||
@@ -11,9 +11,15 @@ import eu.darken.capod.common.debug.DebugSettings
|
||||
import eu.darken.capod.common.preferences.PreferenceStoreMapper
|
||||
import eu.darken.capod.common.preferences.Settings
|
||||
import eu.darken.capod.common.preferences.createFlowPreference
|
||||
import eu.darken.capod.common.theming.ThemeColor
|
||||
import eu.darken.capod.common.theming.ThemeMode
|
||||
import eu.darken.capod.common.theming.ThemeState
|
||||
import eu.darken.capod.common.theming.ThemeStyle
|
||||
import eu.darken.capod.pods.core.PodDevice
|
||||
import eu.darken.capod.pods.core.apple.protocol.IdentityResolvingKey
|
||||
import eu.darken.capod.pods.core.apple.protocol.ProximityEncryptionKey
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@@ -62,6 +68,28 @@ class GeneralSettings @Inject constructor(
|
||||
|
||||
val isOnboardingDone = preferences.createFlowPreference("core.onboarding.done", false)
|
||||
|
||||
val themeMode = preferences.createFlowPreference(
|
||||
"core.ui.theme.mode", ThemeMode.SYSTEM, moshi, onErrorFallbackToDefault = true
|
||||
)
|
||||
val themeStyle = preferences.createFlowPreference(
|
||||
"core.ui.theme.style", ThemeStyle.DEFAULT, moshi, onErrorFallbackToDefault = true
|
||||
)
|
||||
val themeColor = preferences.createFlowPreference(
|
||||
"core.ui.theme.color", ThemeColor.BLUE, moshi, onErrorFallbackToDefault = true
|
||||
)
|
||||
|
||||
val currentThemeState: ThemeState
|
||||
get() = ThemeState(
|
||||
mode = themeMode.value,
|
||||
style = themeStyle.value,
|
||||
color = themeColor.value,
|
||||
)
|
||||
|
||||
val themeState: Flow<ThemeState>
|
||||
get() = combine(themeMode.flow, themeStyle.flow, themeColor.flow) { mode, style, color ->
|
||||
ThemeState(mode, style, color)
|
||||
}
|
||||
|
||||
override val preferenceDataStore: PreferenceDataStore = PreferenceStoreMapper(
|
||||
monitorMode,
|
||||
useExtraMonitorNotification,
|
||||
@@ -70,6 +98,9 @@ class GeneralSettings @Inject constructor(
|
||||
isOffloadedFilteringDisabled,
|
||||
isOffloadedBatchingDisabled,
|
||||
useIndirectScanResultCallback,
|
||||
themeMode,
|
||||
themeStyle,
|
||||
themeColor,
|
||||
debugSettings.isAutoReportingEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,15 @@ package eu.darken.capod.main.ui
|
||||
import android.os.Bundle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.graphics.luminance
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.navigation3.runtime.NavKey
|
||||
import androidx.navigation3.runtime.entryProvider
|
||||
import androidx.navigation3.runtime.rememberNavBackStack
|
||||
@@ -40,10 +47,20 @@ class MainActivity : Activity2() {
|
||||
}
|
||||
|
||||
setContent {
|
||||
val themeState by generalSettings.themeState.collectAsState(initial = generalSettings.currentThemeState)
|
||||
val backStack = rememberNavBackStack(startDestination)
|
||||
navCtrl.setup(backStack)
|
||||
|
||||
CapodTheme {
|
||||
CapodTheme(state = themeState) {
|
||||
val backgroundColor = MaterialTheme.colorScheme.background
|
||||
val useDarkIcons = backgroundColor.luminance() > 0.5f
|
||||
SideEffect {
|
||||
window.decorView.setBackgroundColor(backgroundColor.toArgb())
|
||||
val insetsController = WindowCompat.getInsetsController(window, window.decorView)
|
||||
insetsController.isAppearanceLightStatusBars = useDarkIcons
|
||||
insetsController.isAppearanceLightNavigationBars = useDarkIcons
|
||||
}
|
||||
|
||||
CompositionLocalProvider(LocalNavigationController provides navCtrl) {
|
||||
NavDisplay(
|
||||
backStack = backStack,
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package eu.darken.capod.main.ui.settings.general
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
@@ -11,10 +12,13 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.twotone.ArrowBack
|
||||
import androidx.compose.material.icons.twotone.AccountTree
|
||||
import androidx.compose.material.icons.twotone.BugReport
|
||||
import androidx.compose.material.icons.twotone.Contrast
|
||||
import androidx.compose.material.icons.twotone.DarkMode
|
||||
import androidx.compose.material.icons.twotone.DisabledVisible
|
||||
import androidx.compose.material.icons.twotone.FilterList
|
||||
import androidx.compose.material.icons.automirrored.twotone.Message
|
||||
import androidx.compose.material.icons.twotone.Notifications
|
||||
import androidx.compose.material.icons.twotone.Palette
|
||||
import androidx.compose.material.icons.twotone.SettingsBluetooth
|
||||
import androidx.compose.material.icons.automirrored.twotone.ViewList
|
||||
import androidx.compose.material3.AlertDialog
|
||||
@@ -48,6 +52,12 @@ import eu.darken.capod.common.error.ErrorEventHandler
|
||||
import eu.darken.capod.common.navigation.NavigationEventHandler
|
||||
import eu.darken.capod.common.settings.SettingsBaseItem
|
||||
import eu.darken.capod.common.settings.SettingsCategoryHeader
|
||||
import eu.darken.capod.common.settings.SettingsListPreferenceItem
|
||||
import eu.darken.capod.common.settings.ThemeColorSelectorDialog
|
||||
import eu.darken.capod.common.theming.ThemeColor
|
||||
import eu.darken.capod.common.theming.ThemeMode
|
||||
import eu.darken.capod.common.theming.ThemeState
|
||||
import eu.darken.capod.common.theming.ThemeStyle
|
||||
import eu.darken.capod.main.core.MonitorMode
|
||||
|
||||
@Composable
|
||||
@@ -68,6 +78,9 @@ fun GeneralSettingsScreenHost(vm: GeneralSettingsViewModel = hiltViewModel()) {
|
||||
onOffloadedFilteringDisabledChanged = { disabled -> vm.setOffloadedFilteringDisabled(disabled) },
|
||||
onOffloadedBatchingDisabledChanged = { disabled -> vm.setOffloadedBatchingDisabled(disabled) },
|
||||
onUseIndirectScanResultCallbackChanged = { enabled -> vm.setUseIndirectScanResultCallback(enabled) },
|
||||
onThemeModeSelected = { mode -> vm.setThemeMode(mode) },
|
||||
onThemeStyleSelected = { style -> vm.setThemeStyle(style) },
|
||||
onThemeColorSelected = { color -> vm.setThemeColor(color) },
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -85,10 +98,17 @@ fun GeneralSettingsScreen(
|
||||
onOffloadedFilteringDisabledChanged: (Boolean) -> Unit,
|
||||
onOffloadedBatchingDisabledChanged: (Boolean) -> Unit,
|
||||
onUseIndirectScanResultCallbackChanged: (Boolean) -> Unit,
|
||||
onThemeModeSelected: (ThemeMode) -> Unit = {},
|
||||
onThemeStyleSelected: (ThemeStyle) -> Unit = {},
|
||||
onThemeColorSelected: (ThemeColor) -> Unit = {},
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var showMonitorModeDialog by remember { mutableStateOf(false) }
|
||||
var showScannerModeDialog by remember { mutableStateOf(false) }
|
||||
var showColorDialog by remember { mutableStateOf(false) }
|
||||
|
||||
val isMaterialYouActive = state.themeState.style == ThemeStyle.MATERIAL_YOU &&
|
||||
Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
|
||||
|
||||
Scaffold(
|
||||
modifier = modifier,
|
||||
@@ -107,6 +127,42 @@ fun GeneralSettingsScreen(
|
||||
},
|
||||
) { innerPadding ->
|
||||
LazyColumn(modifier = Modifier.padding(innerPadding)) {
|
||||
item {
|
||||
SettingsCategoryHeader(text = stringResource(R.string.settings_category_appearance_label))
|
||||
}
|
||||
item {
|
||||
SettingsListPreferenceItem(
|
||||
icon = Icons.TwoTone.DarkMode,
|
||||
title = stringResource(R.string.ui_theme_mode_label),
|
||||
entries = ThemeMode.entries,
|
||||
selectedEntry = state.themeState.mode,
|
||||
onEntrySelected = onThemeModeSelected,
|
||||
entryLabel = { stringResource(it.labelRes) },
|
||||
)
|
||||
}
|
||||
item {
|
||||
SettingsListPreferenceItem(
|
||||
icon = Icons.TwoTone.Contrast,
|
||||
title = stringResource(R.string.ui_theme_style_label),
|
||||
entries = ThemeStyle.entries,
|
||||
selectedEntry = state.themeState.style,
|
||||
onEntrySelected = onThemeStyleSelected,
|
||||
entryLabel = { stringResource(it.labelRes) },
|
||||
)
|
||||
}
|
||||
item {
|
||||
SettingsBaseItem(
|
||||
title = stringResource(R.string.ui_theme_color_label),
|
||||
subtitle = if (isMaterialYouActive) {
|
||||
stringResource(R.string.ui_theme_color_disabled_materialyou)
|
||||
} else {
|
||||
stringResource(state.themeState.color.labelRes)
|
||||
},
|
||||
icon = Icons.TwoTone.Palette,
|
||||
onClick = { showColorDialog = true },
|
||||
enabled = !isMaterialYouActive,
|
||||
)
|
||||
}
|
||||
item {
|
||||
SettingsBaseItem(
|
||||
title = stringResource(R.string.settings_monitor_mode_label),
|
||||
@@ -244,6 +300,17 @@ fun GeneralSettingsScreen(
|
||||
onDismiss = { showScannerModeDialog = false },
|
||||
)
|
||||
}
|
||||
|
||||
if (showColorDialog) {
|
||||
ThemeColorSelectorDialog(
|
||||
selectedColor = state.themeState.color,
|
||||
onColorSelected = {
|
||||
onThemeColorSelected(it)
|
||||
showColorDialog = false
|
||||
},
|
||||
onDismiss = { showColorDialog = false },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview2
|
||||
@@ -258,6 +325,7 @@ private fun GeneralSettingsScreenPreview() = PreviewWrapper {
|
||||
isOffloadedFilteringDisabled = false,
|
||||
isOffloadedBatchingDisabled = false,
|
||||
useIndirectScanResultCallback = false,
|
||||
themeState = ThemeState(),
|
||||
),
|
||||
onNavigateUp = {},
|
||||
onMonitorModeSelected = {},
|
||||
|
||||
+44
-15
@@ -6,6 +6,10 @@ import eu.darken.capod.common.coroutine.DispatcherProvider
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.flow.shareLatest
|
||||
import eu.darken.capod.common.navigation.Nav
|
||||
import eu.darken.capod.common.theming.ThemeColor
|
||||
import eu.darken.capod.common.theming.ThemeMode
|
||||
import eu.darken.capod.common.theming.ThemeState
|
||||
import eu.darken.capod.common.theming.ThemeStyle
|
||||
import eu.darken.capod.common.uix.ViewModel4
|
||||
import eu.darken.capod.main.core.GeneralSettings
|
||||
import eu.darken.capod.main.core.MonitorMode
|
||||
@@ -26,25 +30,38 @@ class GeneralSettingsViewModel @Inject constructor(
|
||||
val isOffloadedFilteringDisabled: Boolean,
|
||||
val isOffloadedBatchingDisabled: Boolean,
|
||||
val useIndirectScanResultCallback: Boolean,
|
||||
val themeState: ThemeState,
|
||||
)
|
||||
|
||||
val state = combine(
|
||||
generalSettings.monitorMode.flow,
|
||||
generalSettings.scannerMode.flow,
|
||||
generalSettings.useExtraMonitorNotification.flow,
|
||||
generalSettings.keepConnectedNotificationAfterDisconnect.flow,
|
||||
generalSettings.isOffloadedFilteringDisabled.flow,
|
||||
generalSettings.isOffloadedBatchingDisabled.flow,
|
||||
generalSettings.useIndirectScanResultCallback.flow,
|
||||
) { values ->
|
||||
combine(
|
||||
generalSettings.monitorMode.flow,
|
||||
generalSettings.scannerMode.flow,
|
||||
generalSettings.useExtraMonitorNotification.flow,
|
||||
generalSettings.keepConnectedNotificationAfterDisconnect.flow,
|
||||
) { monitorMode, scannerMode, showNotif, keepNotif ->
|
||||
@Suppress("USELESS_CAST")
|
||||
arrayOf<Any>(monitorMode as Any, scannerMode as Any, showNotif as Any, keepNotif as Any)
|
||||
},
|
||||
combine(
|
||||
generalSettings.isOffloadedFilteringDisabled.flow,
|
||||
generalSettings.isOffloadedBatchingDisabled.flow,
|
||||
generalSettings.useIndirectScanResultCallback.flow,
|
||||
) { filtering, batching, indirect ->
|
||||
@Suppress("USELESS_CAST")
|
||||
arrayOf<Any>(filtering as Any, batching as Any, indirect as Any)
|
||||
},
|
||||
generalSettings.themeState,
|
||||
) { general, compat, themeState ->
|
||||
State(
|
||||
monitorMode = values[0] as MonitorMode,
|
||||
scannerMode = values[1] as ScannerMode,
|
||||
showConnectedNotification = values[2] as Boolean,
|
||||
keepNotificationAfterDisconnect = values[3] as Boolean,
|
||||
isOffloadedFilteringDisabled = values[4] as Boolean,
|
||||
isOffloadedBatchingDisabled = values[5] as Boolean,
|
||||
useIndirectScanResultCallback = values[6] as Boolean,
|
||||
monitorMode = general[0] as MonitorMode,
|
||||
scannerMode = general[1] as ScannerMode,
|
||||
showConnectedNotification = general[2] as Boolean,
|
||||
keepNotificationAfterDisconnect = general[3] as Boolean,
|
||||
isOffloadedFilteringDisabled = compat[0] as Boolean,
|
||||
isOffloadedBatchingDisabled = compat[1] as Boolean,
|
||||
useIndirectScanResultCallback = compat[2] as Boolean,
|
||||
themeState = themeState,
|
||||
)
|
||||
}.shareLatest(scope = vmScope)
|
||||
|
||||
@@ -76,6 +93,18 @@ class GeneralSettingsViewModel @Inject constructor(
|
||||
generalSettings.useIndirectScanResultCallback.value = enabled
|
||||
}
|
||||
|
||||
fun setThemeMode(mode: ThemeMode) {
|
||||
generalSettings.themeMode.value = mode
|
||||
}
|
||||
|
||||
fun setThemeStyle(style: ThemeStyle) {
|
||||
generalSettings.themeStyle.value = style
|
||||
}
|
||||
|
||||
fun setThemeColor(color: ThemeColor) {
|
||||
generalSettings.themeColor.value = color
|
||||
}
|
||||
|
||||
fun goToDebugSettings() {
|
||||
navTo(Nav.Settings.Debug)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import android.os.Bundle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.enableEdgeToEdge
|
||||
import androidx.activity.viewModels
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
@@ -16,6 +17,7 @@ import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.theming.CapodTheme
|
||||
import eu.darken.capod.common.uix.Activity2
|
||||
import eu.darken.capod.common.upgrade.UpgradeRepo
|
||||
import eu.darken.capod.main.core.GeneralSettings
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
@@ -24,6 +26,7 @@ class WidgetConfigurationActivity : Activity2() {
|
||||
private val vm: WidgetConfigurationViewModel by viewModels()
|
||||
|
||||
@Inject lateinit var upgradeRepo: UpgradeRepo
|
||||
@Inject lateinit var generalSettings: GeneralSettings
|
||||
@ApplicationContext @Inject lateinit var appContext: Context
|
||||
|
||||
private var widgetId: Int = AppWidgetManager.INVALID_APPWIDGET_ID
|
||||
@@ -48,7 +51,8 @@ class WidgetConfigurationActivity : Activity2() {
|
||||
}
|
||||
|
||||
setContent {
|
||||
CapodTheme {
|
||||
val themeState by generalSettings.themeState.collectAsState(initial = generalSettings.currentThemeState)
|
||||
CapodTheme(state = themeState) {
|
||||
val state by waitForState(vm.state)
|
||||
state?.let { currentState ->
|
||||
WidgetConfigurationScreen(
|
||||
|
||||
@@ -19,6 +19,7 @@ import eu.darken.capod.common.debug.logging.asLog
|
||||
import eu.darken.capod.common.debug.logging.log
|
||||
import eu.darken.capod.common.debug.logging.logTag
|
||||
import eu.darken.capod.common.theming.CapodTheme
|
||||
import eu.darken.capod.main.core.GeneralSettings
|
||||
import eu.darken.capod.pods.core.PodDevice
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
@@ -26,6 +27,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class PopUpWindow @Inject constructor(
|
||||
@ApplicationContext private val appContext: Context,
|
||||
private val generalSettings: GeneralSettings,
|
||||
) {
|
||||
|
||||
private val windowManager = appContext.getSystemService(WINDOW_SERVICE) as WindowManager
|
||||
@@ -67,7 +69,7 @@ class PopUpWindow @Inject constructor(
|
||||
setViewTreeLifecycleOwner(owner)
|
||||
setViewTreeSavedStateRegistryOwner(owner)
|
||||
setContent {
|
||||
CapodTheme {
|
||||
CapodTheme(state = generalSettings.currentThemeState) {
|
||||
PopUpContent(device = device, onClose = { close() })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -271,6 +271,29 @@
|
||||
<string name="profiles_key_expected_format">Expected format: %1$s</string>
|
||||
<string name="profiles_priority_hint">Profile order determines priority. Drag profiles to reorder them - profiles higher in the list take precedence when multiple devices match.</string>
|
||||
|
||||
<!-- Theme Mode -->
|
||||
<string name="ui_theme_mode_label">Theme mode</string>
|
||||
<string name="ui_theme_mode_system_label">System</string>
|
||||
<string name="ui_theme_mode_dark_label">Dark</string>
|
||||
<string name="ui_theme_mode_light_label">Light</string>
|
||||
|
||||
<!-- Theme Style -->
|
||||
<string name="ui_theme_style_label">Theme style</string>
|
||||
<string name="ui_theme_style_default_label">Default</string>
|
||||
<string name="ui_theme_style_materialyou_label">Material You</string>
|
||||
<string name="ui_theme_style_medium_contrast_label">Medium contrast</string>
|
||||
<string name="ui_theme_style_high_contrast_label">High contrast</string>
|
||||
|
||||
<!-- Theme Color -->
|
||||
<string name="ui_theme_color_label">Theme color</string>
|
||||
<string name="ui_theme_color_blue_label">Blue</string>
|
||||
<string name="ui_theme_color_green_label">Green</string>
|
||||
<string name="ui_theme_color_amber_label">Amber</string>
|
||||
<string name="ui_theme_color_disabled_materialyou">Uses wallpaper colors</string>
|
||||
|
||||
<!-- Settings categories -->
|
||||
<string name="settings_category_appearance_label">Appearance</string>
|
||||
|
||||
<!-- Unsaved changes dialog -->
|
||||
<string name="general_unsaved_changes_title">Unsaved Changes</string>
|
||||
<string name="general_unsaved_changes_message">You have unsaved changes. What would you like to do?</string>
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
package eu.darken.capod.common.preferences
|
||||
|
||||
import com.squareup.moshi.JsonClass
|
||||
import com.squareup.moshi.JsonDataException
|
||||
import com.squareup.moshi.Moshi
|
||||
import eu.darken.capod.common.theming.ThemeMode
|
||||
import eu.darken.capod.main.core.MonitorMode
|
||||
import io.kotest.matchers.shouldBe
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -121,4 +124,66 @@ class FlowPreferenceMoshiTest : BaseTest() {
|
||||
monitorMode.update { MonitorMode.MANUAL }
|
||||
monitorMode.value shouldBe MonitorMode.MANUAL
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bad enum value throws without fallback`() = runTest {
|
||||
val moshi = Moshi.Builder().build()
|
||||
mockPreferences.edit().putString("theme.mode", "\"theme.mode.bogus\"").apply()
|
||||
|
||||
assertThrows<JsonDataException> {
|
||||
mockPreferences.createFlowPreference(
|
||||
key = "theme.mode",
|
||||
defaultValue = ThemeMode.SYSTEM,
|
||||
moshi = moshi,
|
||||
onErrorFallbackToDefault = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `bad enum value returns default with fallback`() = runTest {
|
||||
val moshi = Moshi.Builder().build()
|
||||
mockPreferences.edit().putString("theme.mode", "\"theme.mode.bogus\"").apply()
|
||||
|
||||
val pref = mockPreferences.createFlowPreference(
|
||||
key = "theme.mode",
|
||||
defaultValue = ThemeMode.SYSTEM,
|
||||
moshi = moshi,
|
||||
onErrorFallbackToDefault = true,
|
||||
)
|
||||
|
||||
pref.value shouldBe ThemeMode.SYSTEM
|
||||
pref.flow.first() shouldBe ThemeMode.SYSTEM
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `corrupt json returns default with fallback`() = runTest {
|
||||
val moshi = Moshi.Builder().build()
|
||||
mockPreferences.edit().putString("theme.mode", "not-json-at-all").apply()
|
||||
|
||||
val pref = mockPreferences.createFlowPreference(
|
||||
key = "theme.mode",
|
||||
defaultValue = ThemeMode.DARK,
|
||||
moshi = moshi,
|
||||
onErrorFallbackToDefault = true,
|
||||
)
|
||||
|
||||
pref.value shouldBe ThemeMode.DARK
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `valid enum roundtrips with fallback enabled`() = runTest {
|
||||
val moshi = Moshi.Builder().build()
|
||||
val pref = mockPreferences.createFlowPreference(
|
||||
key = "theme.mode",
|
||||
defaultValue = ThemeMode.SYSTEM,
|
||||
moshi = moshi,
|
||||
onErrorFallbackToDefault = true,
|
||||
)
|
||||
|
||||
pref.value shouldBe ThemeMode.SYSTEM
|
||||
pref.update { ThemeMode.DARK }
|
||||
pref.value shouldBe ThemeMode.DARK
|
||||
pref.flow.first() shouldBe ThemeMode.DARK
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user