default to the Material You dark scheme instead of black and white

This commit is contained in:
austinried
2026-10-04 00:30:05 +00:00
parent 9adb81aae7
commit dff2fe58e0
9 changed files with 81 additions and 61 deletions
@@ -95,7 +95,7 @@ fun HeroDetailScaffold(
content: LazyListScope.(rowModifier: Modifier) -> Unit,
modifier: Modifier = Modifier,
) {
val effectiveArtwork = artwork ?: baseArtworkColors
val effectiveArtwork = artwork ?: baseArtworkColors()
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) {
ArtworkTheme(effectiveArtwork) {
BoxWithConstraints(modifier.fillMaxSize().background(Color.Black)) {
@@ -272,7 +272,7 @@ fun ArtistDetailScreen(
)
val nameBusy = rememberOverlaidNameBusy(artThumbnail ?: art)
val nameScrimAlpha by animateFloatAsState(if (nameBusy) 1f else 0f, tween(SCRIM_FADE_MS), label = "artistNameScrim")
val effectiveArtwork = artwork ?: baseArtworkColors
val effectiveArtwork = artwork ?: baseArtworkColors()
CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) {
ArtworkTheme(effectiveArtwork) {
@@ -3,6 +3,7 @@ package com.subtracks.ui.theme
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.tween
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
@@ -16,29 +17,35 @@ const val ARTWORK_THEME_TRANSITION_MS = 600
private const val TRANSITION_STEPS = 24
internal val baseArtworkColors =
ArtworkColors(
scheme = SubtracksColorScheme,
gradientHigh = SubtracksColorScheme.surfaceContainerHigh,
gradientLow = SubtracksColorScheme.background,
accents = listOf(SubtracksColorScheme.surfaceContainerHighest, SubtracksColorScheme.surfaceContainerLow),
darkPrimary = SubtracksColorScheme.background,
blobSeed = 0,
)
@Composable
internal fun baseArtworkColors(): ArtworkColors {
val scheme = MaterialTheme.colorScheme
return remember(scheme) {
ArtworkColors(
scheme = scheme,
gradientHigh = scheme.surfaceContainerHigh,
gradientLow = scheme.background,
accents = listOf(scheme.surfaceContainerHighest, scheme.surfaceContainerLow),
darkPrimary = scheme.background,
blobSeed = 0,
)
}
}
@Composable
fun rememberAnimatedArtworkColors(
target: ArtworkColors?,
base: ArtworkColors,
durationMillis: Int = ARTWORK_THEME_TRANSITION_MS,
): ArtworkColors {
var from by remember { mutableStateOf(target ?: baseArtworkColors) }
var to by remember { mutableStateOf(target ?: baseArtworkColors) }
var from by remember { mutableStateOf(target ?: base) }
var to by remember { mutableStateOf(target ?: base) }
val progress = remember { Animatable(1f) }
LaunchedEffect(target, durationMillis) {
val next = target ?: baseArtworkColors
LaunchedEffect(target, base, durationMillis) {
val next = target ?: base
if (next !== to) {
if (to === baseArtworkColors) {
if (to === base) {
from = next
to = next
} else {
@@ -194,7 +194,8 @@ fun rememberArtworkColors(
}
}
val target = remember(effective) { effective?.let { (primary, secondary) -> artworkColorsFromSeeds(primary, secondary) } }
val colors = rememberAnimatedArtworkColors(target, durationMillis)
val base = baseArtworkColors()
val colors = rememberAnimatedArtworkColors(target, base, durationMillis)
return if (target == null) null else colors
}
@@ -1,10 +0,0 @@
package com.subtracks.ui.theme
import androidx.compose.ui.graphics.Color
val Black = Color(0xFF000000)
val NearBlack = Color(0xFF0A0A0A)
val DarkGrey = Color(0xFF1A1A1A)
val MidGrey = Color(0xFF2C2C2C)
val LightGrey = Color(0xFF9E9E9E)
val White = Color(0xFFFFFFFF)
@@ -1,40 +1,26 @@
package com.subtracks.ui.theme
import android.os.Build
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.runtime.Composable
internal val SubtracksColorScheme =
darkColorScheme(
primary = White,
onPrimary = Black,
primaryContainer = White,
onPrimaryContainer = Black,
secondary = White,
onSecondary = Black,
secondaryContainer = White,
onSecondaryContainer = Black,
tertiary = White,
onTertiary = Black,
background = Black,
onBackground = White,
surface = Black,
onSurface = White,
surfaceVariant = DarkGrey,
onSurfaceVariant = LightGrey,
surfaceContainerLowest = Black,
surfaceContainerLow = NearBlack,
surfaceContainer = NearBlack,
surfaceContainerHigh = DarkGrey,
surfaceContainerHighest = MidGrey,
outline = MidGrey,
outlineVariant = DarkGrey,
)
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
@Composable
fun SubtracksTheme(content: @Composable () -> Unit) {
val context = LocalContext.current
val colorScheme =
remember(context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
dynamicDarkColorScheme(context)
} else {
darkColorScheme()
}
}
MaterialTheme(
colorScheme = SubtracksColorScheme,
colorScheme = colorScheme,
typography = SubtracksTypography,
content = content,
)
@@ -68,7 +68,9 @@ class ArtworkExtractionTest {
val ref = CoverArtRef(url = "art", cacheKey = "art")
var colors: ArtworkColors? = null
composeRule.setContent {
colors = rememberArtworkColors(ref)
SubtracksTheme {
colors = rememberArtworkColors(ref)
}
}
composeRule.waitUntil(timeoutMillis = 5_000) { colors != null }
assertNotNull(colors)
@@ -99,15 +101,18 @@ class ArtworkExtractionTest {
val artwork = mutableStateOf<ArtworkColors?>(null)
val seen = mutableListOf<androidx.compose.ui.graphics.Color>()
composeRule.setContent {
val colors = rememberAnimatedArtworkColors(artwork.value)
SideEffect { seen += colors.scheme.background }
SubtracksTheme {
val colors = rememberAnimatedArtworkColors(artwork.value, baseArtworkColors())
SideEffect { seen += colors.scheme.background }
}
}
composeRule.waitForIdle()
val baseBackground = seen.first()
composeRule.runOnIdle { artwork.value = target }
composeRule.waitForIdle()
val firstPainting = seen.first { it != baseArtworkColors.scheme.background }
val firstPainting = seen.first { it != baseBackground }
assertEquals(target.scheme.background, firstPainting)
}
@@ -0,0 +1,31 @@
package com.subtracks.ui.theme
import androidx.activity.ComponentActivity
import androidx.compose.material3.ColorScheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Assert.assertNotEquals
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class ThemeTest {
@get:Rule
val composeRule = createAndroidComposeRule<ComponentActivity>()
@Test
fun defaultSchemeIsNotMonochrome() {
var scheme: ColorScheme? = null
composeRule.setContent {
SubtracksTheme {
scheme = MaterialTheme.colorScheme
}
}
val resolved = requireNotNull(scheme)
assertNotEquals(Color.White, resolved.primary)
assertNotEquals(Color.Black, resolved.background)
}
}