From c145fe51f070906159b94d47c1402686e6f9b899 Mon Sep 17 00:00:00 2001 From: austinried <4966622+austinried@users.noreply.github.com> Date: Thu, 24 Sep 2026 07:12:17 +0000 Subject: [PATCH] custom seek bar, don't play on seek, trim CJK line height, fix icon aspect --- .../subtracks/playback/PlaybackController.kt | 5 +- .../subtracks/ui/playback/NowPlayingScreen.kt | 77 +++++++++++++------ .../media3_notification_small_icon.xml | 42 +++++----- 3 files changed, 78 insertions(+), 46 deletions(-) diff --git a/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt b/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt index 1f4379a7..a0c01a09 100644 --- a/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt +++ b/app/src/main/kotlin/com/subtracks/playback/PlaybackController.kt @@ -376,10 +376,7 @@ class PlaybackController( fun previous() = skip(-1) fun seekTo(positionMs: Long) { - val player = player ?: return - player.ensurePrepared() - player.seekTo(positionMs) - player.play() + player?.seekTo(positionMs) } fun coverArt( diff --git a/app/src/main/kotlin/com/subtracks/ui/playback/NowPlayingScreen.kt b/app/src/main/kotlin/com/subtracks/ui/playback/NowPlayingScreen.kt index 6e2be62d..a46f0c30 100644 --- a/app/src/main/kotlin/com/subtracks/ui/playback/NowPlayingScreen.kt +++ b/app/src/main/kotlin/com/subtracks/ui/playback/NowPlayingScreen.kt @@ -3,6 +3,8 @@ package com.subtracks.ui.playback import android.content.Context import androidx.compose.foundation.background import androidx.compose.foundation.basicMarquee +import androidx.compose.foundation.gestures.detectHorizontalDragGestures +import androidx.compose.foundation.gestures.detectTapGestures import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column @@ -30,8 +32,6 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold -import androidx.compose.material3.Slider -import androidx.compose.material3.SliderDefaults import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.material3.TopAppBarDefaults @@ -40,6 +40,7 @@ import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @@ -47,8 +48,11 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.text.PlatformTextStyle +import androidx.compose.ui.text.style.LineHeightStyle import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -131,10 +135,12 @@ fun NowPlayingScreen( val titleStyle = MaterialTheme.typography.headlineSmall.copy( platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.Both), ) val subtitleStyle = MaterialTheme.typography.bodyMedium.copy( platformStyle = PlatformTextStyle(includeFontPadding = false), + lineHeightStyle = LineHeightStyle(LineHeightStyle.Alignment.Center, LineHeightStyle.Trim.Both), ) CompositionLocalProvider(LocalContentColor provides MaterialTheme.colorScheme.onBackground) { @@ -218,26 +224,9 @@ fun NowPlayingScreen( modifier = Modifier.padding(top = 6.dp).basicMarquee(), ) } - var dragging by remember { mutableStateOf(false) } - var dragPosition by remember { mutableFloatStateOf(0f) } - Slider( - value = if (dragging) dragPosition else state.positionMs.toFloat(), - onValueChange = { - dragging = true - dragPosition = it - }, - onValueChangeFinished = { - onSeek(dragPosition.toLong()) - dragging = false - }, - valueRange = 0f..state.durationMs.toFloat().coerceAtLeast(1f), - track = { sliderState -> - SliderDefaults.Track( - sliderState = sliderState, - thumbTrackGapSize = 0.dp, - drawStopIndicator = {}, - ) - }, + SeekBar( + progress = if (state.durationMs > 0) (state.positionMs.toFloat() / state.durationMs).coerceIn(0f, 1f) else 0f, + onSeek = { fraction -> onSeek((fraction * state.durationMs).toLong()) }, modifier = Modifier.padding(top = 28.dp), ) Row( @@ -317,6 +306,50 @@ fun NowPlayingScreen( } } +@Composable +private fun SeekBar( + progress: Float, + onSeek: (Float) -> Unit, + modifier: Modifier = Modifier, +) { + var widthPx by remember { mutableIntStateOf(0) } + var dragging by remember { mutableStateOf(false) } + var dragFraction by remember { mutableFloatStateOf(0f) } + val active = MaterialTheme.colorScheme.primary + val track = active.copy(alpha = 0.24f) + val shown = if (dragging) dragFraction else progress + Box( + modifier = + modifier + .fillMaxWidth() + .height(32.dp) + .onSizeChanged { widthPx = it.width } + .pointerInput(widthPx) { + detectHorizontalDragGestures( + onDragStart = { offset -> + dragging = true + dragFraction = if (widthPx > 0) (offset.x / widthPx).coerceIn(0f, 1f) else 0f + }, + onDragEnd = { + dragging = false + onSeek(dragFraction) + }, + onDragCancel = { dragging = false }, + ) { change, _ -> + dragFraction = if (widthPx > 0) (change.position.x / widthPx).coerceIn(0f, 1f) else 0f + } + }.pointerInput(widthPx) { + detectTapGestures { offset -> + if (widthPx > 0) onSeek((offset.x / widthPx).coerceIn(0f, 1f)) + } + }, + contentAlignment = Alignment.CenterStart, + ) { + Box(Modifier.fillMaxWidth().height(4.dp).background(track)) + Box(Modifier.fillMaxWidth(shown).height(4.dp).background(active)) + } +} + private fun formatTime(milliseconds: Long): String { val totalSeconds = (milliseconds / 1000).coerceAtLeast(0) val minutes = totalSeconds / 60 diff --git a/app/src/main/res/drawable/media3_notification_small_icon.xml b/app/src/main/res/drawable/media3_notification_small_icon.xml index cb1fb7ea..3fe84db5 100644 --- a/app/src/main/res/drawable/media3_notification_small_icon.xml +++ b/app/src/main/res/drawable/media3_notification_small_icon.xml @@ -1,24 +1,26 @@ - - - + android:viewportWidth="300" + android:viewportHeight="300"> + + + + +