feat(android): swipe-up-to-expand gesture on MiniPlayer (#34)
Ports player_bar.dart's onVerticalDragEnd — an upward flick anywhere on the bar expands into NowPlaying, alongside the existing tap-to-expand. A vertical draggable claims only vertical-dominant drags, so the inline seek slider keeps its horizontal scrub gesture. The 200 px/s Flutter threshold is expressed in dp and density-converted so the flick feels the same across screen densities. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,9 @@ import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.animation.ExperimentalSharedTransitionApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.draggable
|
||||
import androidx.compose.foundation.gestures.rememberDraggableState
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -27,6 +30,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.hilt.navigation.compose.hiltViewModel
|
||||
@@ -52,6 +56,11 @@ private const val MINI_BAR_TONAL_ELEVATION_DP = 4
|
||||
private const val COVER_SIZE_DP = 48
|
||||
private const val SCRUBBER_ROW_HEIGHT_DP = 4
|
||||
|
||||
// Upward flick speed (dp/s) that expands the bar into NowPlaying.
|
||||
// Mirrors player_bar.dart's 200 px/s threshold; expressed in dp and
|
||||
// converted via density so the gesture feels the same across screens.
|
||||
private const val SWIPE_UP_VELOCITY_DP = 200
|
||||
|
||||
@OptIn(ExperimentalSharedTransitionApi::class)
|
||||
@Composable
|
||||
private fun MiniCover(coverUrl: String, contentDescription: String) {
|
||||
@@ -126,10 +135,22 @@ fun MiniPlayer(
|
||||
val isLiked by trackActionsViewModel.isLikedFlow(track.id)
|
||||
.collectAsStateWithLifecycle(initialValue = false)
|
||||
|
||||
// Swipe up anywhere on the bar to expand into the full player —
|
||||
// mirrors player_bar.dart. We only act on a clear upward flick
|
||||
// (negative velocity past the threshold) so a slow tap-with-jitter
|
||||
// doesn't accidentally open the screen. The horizontal seek slider
|
||||
// keeps its own gestures; a vertical draggable only claims
|
||||
// vertical-dominant drags.
|
||||
val flickThresholdPx = with(LocalDensity.current) { SWIPE_UP_VELOCITY_DP.dp.toPx() }
|
||||
Surface(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.height(MINI_BAR_HEIGHT_DP.dp),
|
||||
.height(MINI_BAR_HEIGHT_DP.dp)
|
||||
.draggable(
|
||||
orientation = Orientation.Vertical,
|
||||
state = rememberDraggableState { },
|
||||
onDragStopped = { velocity -> if (velocity < -flickThresholdPx) onExpandClick() },
|
||||
),
|
||||
color = MaterialTheme.colorScheme.surface,
|
||||
tonalElevation = MINI_BAR_TONAL_ELEVATION_DP.dp,
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user