revert the library header colour and gradient

This commit is contained in:
austinried
2026-10-04 00:29:55 +00:00
parent e5416e1d33
commit ffd5f58aa9
4 changed files with 211 additions and 289 deletions
@@ -21,7 +21,6 @@ import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableFloatStateOf
@@ -56,7 +55,6 @@ import com.subtracks.ui.playback.NowPlayingRoute
import com.subtracks.ui.playback.QueueRoute import com.subtracks.ui.playback.QueueRoute
import com.subtracks.ui.settings.AddSourceRoute import com.subtracks.ui.settings.AddSourceRoute
import com.subtracks.ui.settings.SettingsRoute import com.subtracks.ui.settings.SettingsRoute
import com.subtracks.ui.theme.LocalPlayerArtwork
import com.subtracks.ui.theme.rememberArtworkColors import com.subtracks.ui.theme.rememberArtworkColors
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
@@ -145,13 +143,9 @@ private fun MainNavigation() {
nowPlayingProgress = 0f nowPlayingProgress = 0f
} }
} }
val miniArt = playbackController.coverArt(playback.item, thumbnail = true)
val miniArtwork = rememberArtworkColors(miniArt)
val libraryArtwork = miniArtwork.takeIf { miniArt != null }
val density = LocalDensity.current val density = LocalDensity.current
val navBarInset = with(density) { WindowInsets.navigationBars.getBottom(this).toDp() } val navBarInset = with(density) { WindowInsets.navigationBars.getBottom(this).toDp() }
CompositionLocalProvider(LocalPlayerArtwork provides libraryArtwork) {
Box(Modifier.fillMaxSize()) { Box(Modifier.fillMaxSize()) {
Column(Modifier.fillMaxSize()) { Column(Modifier.fillMaxSize()) {
NavHost( NavHost(
@@ -260,10 +254,11 @@ private fun MainNavigation() {
} }
if (playerVisible) { if (playerVisible) {
val miniArt = playbackController.coverArt(playback.item, thumbnail = true)
MiniPlayer( MiniPlayer(
state = playback, state = playback,
coverArt = miniArt, coverArt = miniArt,
artwork = miniArtwork, artwork = rememberArtworkColors(miniArt),
onExpand = { settleNowPlaying(true) }, onExpand = { settleNowPlaying(true) },
onPlayPause = playbackController::togglePlayPause, onPlayPause = playbackController::togglePlayPause,
onNext = playbackController::next, onNext = playbackController::next,
@@ -319,7 +314,6 @@ private fun MainNavigation() {
) )
} }
} }
}
BackHandler(enabled = showingQueue) { showingQueue = false } BackHandler(enabled = showingQueue) { showingQueue = false }
BackHandler(enabled = nowPlayingOpen && !showingQueue) { BackHandler(enabled = nowPlayingOpen && !showingQueue) {
@@ -4,7 +4,6 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
@@ -40,9 +39,7 @@ import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateMapOf import androidx.compose.runtime.mutableStateMapOf
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -53,16 +50,9 @@ 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.draw.clipToBounds import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Rect import androidx.compose.ui.geometry.Rect
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.lerp import androidx.compose.ui.graphics.lerp
import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.boundsInParent import androidx.compose.ui.layout.boundsInParent
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
@@ -78,9 +68,6 @@ import com.subtracks.data.model.Artist
import com.subtracks.data.model.CoverArtRef import com.subtracks.data.model.CoverArtRef
import com.subtracks.data.model.Playlist import com.subtracks.data.model.Playlist
import com.subtracks.data.model.SongListItem import com.subtracks.data.model.SongListItem
import com.subtracks.ui.theme.HeroGradient
import com.subtracks.ui.theme.LocalPlayerArtwork
import com.subtracks.ui.theme.heroBarColor
import org.koin.compose.viewmodel.koinViewModel import org.koin.compose.viewmodel.koinViewModel
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.roundToInt import kotlin.math.roundToInt
@@ -179,66 +166,19 @@ fun LibraryScreen(
SideEffect { scrollBehavior.state.heightOffsetLimit = -with(density) { titleHeight.toPx() } } SideEffect { scrollBehavior.state.heightOffsetLimit = -with(density) { titleHeight.toPx() } }
val titleFraction = scrollBehavior.state.collapsedFraction val titleFraction = scrollBehavior.state.collapsedFraction
var scrollPx by remember { mutableFloatStateOf(0f) } val statusBarTop = with(density) { WindowInsets.statusBars.getTop(this).toDp() }
val scrollTracker =
remember {
object : NestedScrollConnection {
override fun onPostScroll(
consumed: Offset,
available: Offset,
source: NestedScrollSource,
): Offset {
scrollPx = (scrollPx - consumed.y).coerceAtLeast(0f)
return Offset.Zero
}
}
}
val artwork = LocalPlayerArtwork.current Column(
val statusBarTopPx = WindowInsets.statusBars.getTop(density)
val statusBarTop = with(density) { statusBarTopPx.toDp() }
BoxWithConstraints(
modifier = modifier =
modifier modifier
.fillMaxSize() .fillMaxSize()
.background(MaterialTheme.colorScheme.background) .background(MaterialTheme.colorScheme.background)
.nestedScroll(scrollTracker)
.nestedScroll(scrollBehavior.nestedScrollConnection), .nestedScroll(scrollBehavior.nestedScrollConnection),
) { ) {
val screenHeightPx = with(density) { maxHeight.toPx() }
val barHeightPx = statusBarTopPx + with(density) { TopAppBarDefaults.TopAppBarExpandedHeight.toPx() }
val selectedColor = heroBarColor(artwork, 0f, barHeightPx, screenHeightPx)
val barColor by
remember(artwork, barHeightPx, screenHeightPx) {
derivedStateOf { heroBarColor(artwork, scrollPx, barHeightPx, screenHeightPx) }
}
HeroGradient(
colors = artwork,
scrollPx = { scrollPx },
modifier = Modifier.fillMaxSize(),
)
Box(
modifier =
Modifier
.align(Alignment.TopStart)
.fillMaxWidth()
.height(statusBarTop + 8.dp)
.background(
Brush.verticalGradient(
listOf(Color.Black.copy(alpha = 0.55f), Color.Transparent),
),
).graphicsLayer { alpha = 1f - titleFraction },
)
Column(Modifier.fillMaxSize()) {
Column( Column(
modifier = modifier =
Modifier Modifier
.fillMaxWidth() .fillMaxWidth()
.drawBehind { drawRect(barColor.copy(alpha = titleFraction)) }
.padding(top = statusBarTop), .padding(top = statusBarTop),
) { ) {
Box( Box(
@@ -266,7 +206,6 @@ fun LibraryScreen(
syncing = syncing, syncing = syncing,
onSync = onSync, onSync = onSync,
onOpenSettings = onOpenSettings, onOpenSettings = onOpenSettings,
selectedColor = selectedColor,
) )
} }
@@ -282,7 +221,6 @@ fun LibraryScreen(
} }
} }
} }
}
} }
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@@ -293,7 +231,6 @@ private fun LibraryTabs(
syncing: Boolean, syncing: Boolean,
onSync: () -> Unit, onSync: () -> Unit,
onOpenSettings: () -> Unit, onOpenSettings: () -> Unit,
selectedColor: Color,
) { ) {
val iconFadeThreshold = 0.65f val iconFadeThreshold = 0.65f
val indicatorStretch = 18.dp val indicatorStretch = 18.dp
@@ -337,7 +274,6 @@ private fun LibraryTabs(
((iconFadeThreshold - abs(position - index)) / iconFadeThreshold) ((iconFadeThreshold - abs(position - index)) / iconFadeThreshold)
.coerceIn(0f, 1f), .coerceIn(0f, 1f),
onClick = { onTabSelected(tab) }, onClick = { onTabSelected(tab) },
selectedColor = selectedColor,
modifier = modifier =
Modifier.onGloballyPositioned { coordinates -> Modifier.onGloballyPositioned { coordinates ->
bounds[index] = coordinates.boundsInParent() bounds[index] = coordinates.boundsInParent()
@@ -375,10 +311,9 @@ private fun TabButton(
tab: LibraryTab, tab: LibraryTab,
progress: Float, progress: Float,
onClick: () -> Unit, onClick: () -> Unit,
selectedColor: Color,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
) { ) {
val content = lerp(MaterialTheme.colorScheme.onBackground, selectedColor, progress) val content = lerp(MaterialTheme.colorScheme.onBackground, MaterialTheme.colorScheme.background, progress)
Box( Box(
modifier = modifier =
modifier modifier
@@ -7,7 +7,6 @@ import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable import androidx.compose.runtime.Immutable
import androidx.compose.runtime.State import androidx.compose.runtime.State
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.produceState import androidx.compose.runtime.produceState
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
@@ -206,8 +205,6 @@ fun ArtworkTheme(
} }
} }
val LocalPlayerArtwork = compositionLocalOf<ArtworkColors?> { null }
fun playerSurfaceColor(artwork: ArtworkColors): Color = lerp(artwork.scheme.surfaceContainerHigh, artwork.gradientHigh, 0.5f) fun playerSurfaceColor(artwork: ArtworkColors): Color = lerp(artwork.scheme.surfaceContainerHigh, artwork.gradientHigh, 0.5f)
fun heroBarColor( fun heroBarColor(
@@ -9,7 +9,6 @@ import androidx.activity.ComponentActivity
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule import androidx.compose.ui.test.junit4.v2.createAndroidComposeRule
@@ -50,7 +49,6 @@ import com.subtracks.ui.playback.QueueScreen
import com.subtracks.ui.settings.AddSourceScreen import com.subtracks.ui.settings.AddSourceScreen
import com.subtracks.ui.settings.AddSourceState import com.subtracks.ui.settings.AddSourceState
import com.subtracks.ui.settings.SettingsScreen import com.subtracks.ui.settings.SettingsScreen
import com.subtracks.ui.theme.LocalPlayerArtwork
import com.subtracks.ui.theme.SubtracksTheme import com.subtracks.ui.theme.SubtracksTheme
import com.subtracks.ui.theme.artworkColorsFromSeed import com.subtracks.ui.theme.artworkColorsFromSeed
import com.subtracks.ui.theme.artworkColorsFromSeeds import com.subtracks.ui.theme.artworkColorsFromSeeds
@@ -329,7 +327,6 @@ class AppScreenshotTest {
private fun setLibraryContent(tab: LibraryTab) { private fun setLibraryContent(tab: LibraryTab) {
composeRule.setContent { composeRule.setContent {
SubtracksTheme { SubtracksTheme {
CompositionLocalProvider(LocalPlayerArtwork provides artworkColorsFromSeed(Color.rgb(120, 80, 200))) {
LibraryScreen( LibraryScreen(
selectedTab = tab, selectedTab = tab,
onTabSelected = {}, onTabSelected = {},
@@ -348,7 +345,6 @@ class AppScreenshotTest {
} }
} }
} }
}
private fun awaitText(text: String) { private fun awaitText(text: String) {
composeRule.waitUntil(timeoutMillis = 5_000) { composeRule.waitUntil(timeoutMillis = 5_000) {