From ae6babb452bbeb9b27479e7d4f0b45547ae9e195 Mon Sep 17 00:00:00 2001 From: vfsfitvnm Date: Sat, 8 Oct 2022 18:13:11 +0200 Subject: [PATCH] Fix buggy BottomSheet nested scroll connection Thanks, Albert Chang! --- .../vimusic/ui/components/BottomSheet.kt | 120 ++++++++---------- .../vimusic/ui/screens/player/Lyrics.kt | 6 +- .../vimusic/ui/screens/player/Player.kt | 3 +- .../vimusic/ui/screens/player/Queue.kt | 4 +- .../vimusic/ui/screens/player/Thumbnail.kt | 3 - 5 files changed, 58 insertions(+), 78 deletions(-) diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/BottomSheet.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/BottomSheet.kt index 5f8f16f..c2df8b5 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/BottomSheet.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/BottomSheet.kt @@ -38,7 +38,6 @@ import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.Velocity import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.launch @Composable @@ -72,36 +71,7 @@ fun BottomSheet( onDragEnd = { val velocity = -velocityTracker.calculateVelocity().y velocityTracker.resetTracking() - - if (velocity > 250) { - state.expand() - } else if (velocity < -250) { - if (state.value < state.collapsedBound && onDismiss != null) { - state.dismiss() - onDismiss.invoke() - } else { - state.collapse() - } - } else { - val l0 = state.dismissedBound - val l1 = (state.collapsedBound - state.dismissedBound) / 2 - val l2 = (state.expandedBound - state.collapsedBound) / 2 - val l3 = state.expandedBound - - when (state.value) { - in l0..l1 -> { - if (onDismiss != null) { - state.dismiss() - onDismiss.invoke() - } else { - state.collapse() - } - } - in l1..l2 -> state.collapse() - in l2..l3 -> state.expand() - else -> Unit - } - } + state.performFling(velocity, onDismiss) } ) } @@ -173,11 +143,11 @@ class BottomSheetState( } } - fun collapse() { + private fun collapse() { collapse(SpringSpec()) } - fun expand() { + private fun expand() { expand(SpringSpec()) } @@ -202,21 +172,53 @@ class BottomSheetState( } } - fun nestedScrollConnection(initialIsTopReached: Boolean = true): NestedScrollConnection { - return object : NestedScrollConnection { - var isTopReached = initialIsTopReached + fun performFling(velocity: Float, onDismiss: (() -> Unit)?) { + if (velocity > 250) { + expand() + } else if (velocity < -250) { + if (value < collapsedBound && onDismiss != null) { + dismiss() + onDismiss.invoke() + } else { + collapse() + } + } else { + val l0 = dismissedBound + val l1 = (collapsedBound - dismissedBound) / 2 + val l2 = (expandedBound - collapsedBound) / 2 + val l3 = expandedBound + + when (value) { + in l0..l1 -> { + if (onDismiss != null) { + dismiss() + onDismiss.invoke() + } else { + collapse() + } + } + in l1..l2 -> collapse() + in l2..l3 -> expand() + else -> Unit + } + } + } + + val preUpPostDownNestedScrollConnection + get() = object : NestedScrollConnection { + var isTopReached = false override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { if (isExpanded && available.y < 0) { isTopReached = false } - if (isTopReached) { + return if (isTopReached && available.y < 0 && source == NestedScrollSource.Drag) { dispatchRawDelta(available.y) - return available + available + } else { + Offset.Zero } - - return Offset.Zero } override fun onPostScroll( @@ -228,44 +230,30 @@ class BottomSheetState( isTopReached = consumed.y == 0f && available.y > 0 } - return Offset.Zero + return if (isTopReached && source == NestedScrollSource.Drag) { + dispatchRawDelta(available.y) + available + } else { + Offset.Zero + } } override suspend fun onPreFling(available: Velocity): Velocity { - if (isTopReached) { + return if (isTopReached) { val velocity = -available.y - coroutineScope { - if (velocity > 250) { - expand() - } else if (velocity < -250) { - collapse() - } else { - val l0 = dismissedBound - val l1 = (collapsedBound - dismissedBound) / 2 - val l2 = (expandedBound - collapsedBound) / 2 - val l3 = expandedBound + performFling(velocity, null) - when (value) { - in l0..l1 -> collapse() - in l1..l2 -> collapse() - in l2..l3 -> expand() - else -> Unit - } - } - } - - return available + available + } else { + Velocity.Zero } - - return Velocity.Zero } override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { isTopReached = false - return super.onPostFling(consumed, available) + return Velocity.Zero } } - } } const val expandedAnchor = 2 diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Lyrics.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Lyrics.kt index e8c8789..ff2863c 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Lyrics.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Lyrics.kt @@ -37,8 +37,6 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalDensity @@ -53,9 +51,9 @@ import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.query import it.vfsfitvnm.vimusic.ui.components.LocalMenuState +import it.vfsfitvnm.vimusic.ui.components.ShimmerHost import it.vfsfitvnm.vimusic.ui.components.themed.Menu import it.vfsfitvnm.vimusic.ui.components.themed.MenuEntry -import it.vfsfitvnm.vimusic.ui.components.ShimmerHost import it.vfsfitvnm.vimusic.ui.components.themed.TextFieldDialog import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder import it.vfsfitvnm.vimusic.ui.styling.DefaultDarkColorPalette @@ -89,7 +87,6 @@ fun Lyrics( mediaMetadataProvider: () -> MediaMetadata, durationProvider: () -> Long, onLyricsUpdate: (Boolean, String, String) -> Unit, - nestedScrollConnectionProvider: () -> NestedScrollConnection, modifier: Modifier = Modifier ) { AnimatedVisibility( @@ -274,7 +271,6 @@ fun Lyrics( text = lyrics, style = typography.xs.center.medium.color(PureBlackColorPalette.text), modifier = Modifier - .nestedScroll(remember { nestedScrollConnectionProvider() }) .verticalFadingEdge() .verticalScroll(rememberScrollState()) .fillMaxWidth() diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Player.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Player.kt index 2955a0a..23cbcd3 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Player.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Player.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.style.TextOverflow @@ -239,8 +240,8 @@ fun Player( onShowLyrics = { isShowingLyrics = it }, isShowingStatsForNerds = isShowingStatsForNerds, onShowStatsForNerds = { isShowingStatsForNerds = it }, - nestedScrollConnectionProvider = layoutState::nestedScrollConnection, modifier = modifier + .nestedScroll(layoutState.preUpPostDownNestedScrollConnection) ) } diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Queue.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Queue.kt index dce39f2..19a6e68 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Queue.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Queue.kt @@ -140,9 +140,7 @@ fun Queue( .only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top).asPaddingValues(), horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier - .nestedScroll(remember { - layoutState.nestedScrollConnection(reorderingState.lazyListState.firstVisibleItemIndex == 0 && reorderingState.lazyListState.firstVisibleItemScrollOffset == 0) - }) + .nestedScroll(layoutState.preUpPostDownNestedScrollConnection) ) { items( diff --git a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Thumbnail.kt b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Thumbnail.kt index d394ecd..1d6d9c4 100644 --- a/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Thumbnail.kt +++ b/app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/screens/player/Thumbnail.kt @@ -21,7 +21,6 @@ import androidx.compose.runtime.remember import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.unit.dp @@ -48,7 +47,6 @@ fun Thumbnail( onShowLyrics: (Boolean) -> Unit, isShowingStatsForNerds: Boolean, onShowStatsForNerds: (Boolean) -> Unit, - nestedScrollConnectionProvider: () -> NestedScrollConnection, modifier: Modifier = Modifier ) { val binder = LocalPlayerServiceBinder.current @@ -145,7 +143,6 @@ fun Thumbnail( size = thumbnailSizeDp, mediaMetadataProvider = mediaItem::mediaMetadata, durationProvider = player::getDuration, - nestedScrollConnectionProvider = nestedScrollConnectionProvider, ) StatsForNerds(