Add dynamic color palette support

Use Material You dynamic colors derived from wallpaper on Android 12+.
Shows gradient indicator in settings, disabled on older Android versions.
This commit is contained in:
Pavel Baksy 2025-11-22 22:44:40 +01:00
parent 46fee06a44
commit 78fe928229
3 changed files with 47 additions and 11 deletions

View File

@ -5,6 +5,7 @@ enum class ThemeMode {
}
enum class ColorTheme(val displayName: String) {
DYNAMIC("Dynamic"),
DEFAULT("Default"),
BLUE("Blue"),
GREEN("Green"),

View File

@ -1,5 +1,6 @@
package cz.bugsy.karemote.ui.screens.settings
import android.os.Build
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
@ -44,6 +45,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight
@ -253,7 +255,38 @@ private fun ColorThemeOption(
isSelected: Boolean,
onClick: () -> Unit
) {
val isDynamicSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
val isEnabled = theme != ColorTheme.DYNAMIC || isDynamicSupported
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.clickable(enabled = isEnabled) { onClick() }
.then(if (!isEnabled) Modifier.background(Color.Transparent) else Modifier)
) {
Box(
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
.then(
if (theme == ColorTheme.DYNAMIC) {
Modifier.background(
brush = Brush.sweepGradient(
colors = listOf(
BluePrimaryLight,
PurplePrimaryLight,
RedPrimaryLight,
OrangePrimaryLight,
GreenPrimaryLight,
DefaultPrimaryLight,
BluePrimaryLight
)
),
alpha = if (isEnabled) 1f else 0.4f
)
} else {
val color = when (theme) {
ColorTheme.DYNAMIC -> Color.Transparent // won't reach here
ColorTheme.DEFAULT -> DefaultPrimaryLight
ColorTheme.BLUE -> BluePrimaryLight
ColorTheme.GREEN -> GreenPrimaryLight
@ -261,16 +294,9 @@ private fun ColorThemeOption(
ColorTheme.PURPLE -> PurplePrimaryLight
ColorTheme.RED -> RedPrimaryLight
}
Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.clickable { onClick() }
) {
Box(
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
.background(color)
Modifier.background(color)
}
)
.then(
if (isSelected) {
Modifier.border(3.dp, MaterialTheme.colorScheme.outline, CircleShape)
@ -292,7 +318,8 @@ private fun ColorThemeOption(
Spacer(modifier = Modifier.height(4.dp))
Text(
text = theme.displayName,
style = MaterialTheme.typography.labelSmall
style = MaterialTheme.typography.labelSmall,
color = if (isEnabled) Color.Unspecified else MaterialTheme.colorScheme.onSurface.copy(alpha = 0.38f)
)
}
}

View File

@ -254,7 +254,15 @@ fun KaRemoteTheme(
ThemeMode.SYSTEM -> isSystemInDarkTheme()
}
val context = LocalContext.current
val colorScheme = when (colorTheme) {
ColorTheme.DYNAMIC -> {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
} else {
if (darkTheme) DefaultDarkColorScheme else DefaultLightColorScheme
}
}
ColorTheme.DEFAULT -> if (darkTheme) DefaultDarkColorScheme else DefaultLightColorScheme
ColorTheme.BLUE -> if (darkTheme) BlueDarkColorScheme else BlueLightColorScheme
ColorTheme.GREEN -> if (darkTheme) GreenDarkColorScheme else GreenLightColorScheme