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 414b900012
commit 14c901dd10
3 changed files with 47 additions and 11 deletions

View File

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

View File

@ -1,5 +1,6 @@
package cz.bugsy.karemote.ui.screens.settings package cz.bugsy.karemote.ui.screens.settings
import android.os.Build
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
@ -44,6 +45,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@ -253,24 +255,48 @@ private fun ColorThemeOption(
isSelected: Boolean, isSelected: Boolean,
onClick: () -> Unit onClick: () -> Unit
) { ) {
val color = when (theme) { val isDynamicSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
ColorTheme.DEFAULT -> DefaultPrimaryLight val isEnabled = theme != ColorTheme.DYNAMIC || isDynamicSupported
ColorTheme.BLUE -> BluePrimaryLight
ColorTheme.GREEN -> GreenPrimaryLight
ColorTheme.ORANGE -> OrangePrimaryLight
ColorTheme.PURPLE -> PurplePrimaryLight
ColorTheme.RED -> RedPrimaryLight
}
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier.clickable { onClick() } modifier = Modifier
.clickable(enabled = isEnabled) { onClick() }
.then(if (!isEnabled) Modifier.background(Color.Transparent) else Modifier)
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
.size(48.dp) .size(48.dp)
.clip(CircleShape) .clip(CircleShape)
.background(color) .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
ColorTheme.ORANGE -> OrangePrimaryLight
ColorTheme.PURPLE -> PurplePrimaryLight
ColorTheme.RED -> RedPrimaryLight
}
Modifier.background(color)
}
)
.then( .then(
if (isSelected) { if (isSelected) {
Modifier.border(3.dp, MaterialTheme.colorScheme.outline, CircleShape) Modifier.border(3.dp, MaterialTheme.colorScheme.outline, CircleShape)
@ -292,7 +318,8 @@ private fun ColorThemeOption(
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
Text( Text(
text = theme.displayName, 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() ThemeMode.SYSTEM -> isSystemInDarkTheme()
} }
val context = LocalContext.current
val colorScheme = when (colorTheme) { 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.DEFAULT -> if (darkTheme) DefaultDarkColorScheme else DefaultLightColorScheme
ColorTheme.BLUE -> if (darkTheme) BlueDarkColorScheme else BlueLightColorScheme ColorTheme.BLUE -> if (darkTheme) BlueDarkColorScheme else BlueLightColorScheme
ColorTheme.GREEN -> if (darkTheme) GreenDarkColorScheme else GreenLightColorScheme ColorTheme.GREEN -> if (darkTheme) GreenDarkColorScheme else GreenLightColorScheme