fix back popping the route under now playing; disc header spacing; dial back the fab glyph

- register the now playing and queue back callback from a LaunchedEffect instead of BackHandler: the NavHost registers its own callback from a LaunchedEffect, so a BackHandler composed before it loses the back and pops the route underneath
- NowPlayingBackTest registers the overlay handler before the NavHost (the losing order) and asserts back closes the overlay without popping the route
- more top padding on disc headers, except the first which sits flush
- LIST_OPTIONS_WEIGHT 0.15 -> 0.11
This commit is contained in:
austinried
2026-10-04 00:29:58 +00:00
parent d43dd51b15
commit fd37c3ad86
4 changed files with 85 additions and 16 deletions
@@ -1,7 +1,8 @@
package com.subtracks.ui
import android.net.Uri
import androidx.activity.compose.BackHandler
import androidx.activity.OnBackPressedCallback
import androidx.activity.compose.LocalOnBackPressedDispatcherOwner
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.animate
import androidx.compose.animation.core.animateDpAsState
@@ -32,6 +33,7 @@ import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.statusBars
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.mutableStateOf
@@ -72,6 +74,7 @@ import com.subtracks.ui.settings.SettingsRoute
import com.subtracks.ui.theme.ArtworkTheme
import com.subtracks.ui.theme.rememberArtworkColors
import kotlinx.coroutines.Job
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
@@ -157,6 +160,30 @@ private fun MainNavigation() {
if (!open) nowPlayingOpen = false
}
}
// The NavHost registers its back callback from a LaunchedEffect, so a BackHandler composed
// before it can lose the back to the NavHost (which would pop the route under the overlay).
// Register ours from a LaunchedEffect after the NavHost so it lands later and wins.
val backDispatcher = LocalOnBackPressedDispatcherOwner.current?.onBackPressedDispatcher
val overlayBack =
remember {
object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
when {
showingQueue -> showingQueue = false
nowPlayingOpen -> settleNowPlaying(false)
}
}
}
}
SideEffect { overlayBack.isEnabled = showingQueue || nowPlayingOpen }
LaunchedEffect(backDispatcher, overlayBack) {
backDispatcher?.addCallback(overlayBack)
try {
awaitCancellation()
} finally {
overlayBack.remove()
}
}
LaunchedEffect(Unit) { playbackController.connect() }
LaunchedEffect(Unit) { if (nowPlayingOpen) nowPlayingProgress = 1f }
@@ -370,9 +397,4 @@ private fun MainNavigation() {
}
}
}
BackHandler(enabled = showingQueue) { showingQueue = false }
BackHandler(enabled = nowPlayingOpen && !showingQueue) {
settleNowPlaying(false)
}
}
@@ -128,7 +128,7 @@ fun AlbumDetailScreen(
val disc = song.disc ?: 1L
val discLabel = discLabels[disc]
if (disc != lastDisc && (multiDisc || discLabel != null)) {
item(key = "disc:$disc") { DiscHeader(discLabel ?: "Disc $disc") }
item(key = "disc:$disc") { DiscHeader(discLabel ?: "Disc $disc", first = lastDisc == null) }
}
lastDisc = disc
item(key = song.id) {
@@ -146,10 +146,13 @@ fun AlbumDetailScreen(
}
@Composable
private fun DiscHeader(text: String) {
private fun DiscHeader(
text: String,
first: Boolean,
) {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = 28.dp, bottom = 4.dp),
modifier = Modifier.padding(start = 16.dp, end = 16.dp, top = if (first) 0.dp else 40.dp, bottom = 4.dp),
) {
Icon(
imageVector = Icons.Outlined.FiberSmartRecord,
@@ -653,7 +653,7 @@ private val TAB_BAR_CONTENT_HEIGHT = 52.dp
private val TAB_ICON_SIZE = 24.dp
private val TAB_VERTICAL_PADDING = 6.dp
private val TOUCH_TARGET = 48.dp
private const val LIST_OPTIONS_WEIGHT = 0.15f
private const val LIST_OPTIONS_WEIGHT = 0.11f
private val LIST_OPTIONS_BAR_WIDTHS = listOf(1f, 0.62f, 0.34f)
@Composable