Implement #101
This commit is contained in:
parent
0e793c956c
commit
6474b52490
5 changed files with 261 additions and 241 deletions
|
@ -1,6 +1,5 @@
|
||||||
package it.vfsfitvnm.vimusic
|
package it.vfsfitvnm.vimusic
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.content.*
|
import android.content.*
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
@ -24,7 +23,8 @@ import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
import androidx.compose.ui.text.ExperimentalTextApi
|
import androidx.media3.common.MediaItem
|
||||||
|
import androidx.media3.common.Player
|
||||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import com.valentinilk.shimmer.LocalShimmerTheme
|
import com.valentinilk.shimmer.LocalShimmerTheme
|
||||||
import com.valentinilk.shimmer.defaultShimmerTheme
|
import com.valentinilk.shimmer.defaultShimmerTheme
|
||||||
|
@ -43,7 +43,6 @@ import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
import it.vfsfitvnm.vimusic.ui.views.PlayerView
|
import it.vfsfitvnm.vimusic.ui.views.PlayerView
|
||||||
import it.vfsfitvnm.vimusic.utils.*
|
import it.vfsfitvnm.vimusic.utils.*
|
||||||
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
private val serviceConnection = object : ServiceConnection {
|
private val serviceConnection = object : ServiceConnection {
|
||||||
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
||||||
|
@ -70,10 +69,7 @@ class MainActivity : ComponentActivity() {
|
||||||
super.onStop()
|
super.onStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("BatteryLife")
|
@OptIn(ExperimentalFoundationApi::class, ExperimentalAnimationApi::class)
|
||||||
@OptIn(ExperimentalFoundationApi::class, ExperimentalAnimationApi::class,
|
|
||||||
ExperimentalTextApi::class
|
|
||||||
)
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
@ -85,7 +81,8 @@ class MainActivity : ComponentActivity() {
|
||||||
var appearance by remember(isSystemInDarkTheme) {
|
var appearance by remember(isSystemInDarkTheme) {
|
||||||
with(preferences) {
|
with(preferences) {
|
||||||
val colorPaletteMode = getEnum(colorPaletteModeKey, ColorPaletteMode.System)
|
val colorPaletteMode = getEnum(colorPaletteModeKey, ColorPaletteMode.System)
|
||||||
val thumbnailRoundness = getEnum(thumbnailRoundnessKey, ThumbnailRoundness.Light)
|
val thumbnailRoundness =
|
||||||
|
getEnum(thumbnailRoundnessKey, ThumbnailRoundness.Light)
|
||||||
|
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
Appearance(
|
Appearance(
|
||||||
|
@ -102,7 +99,8 @@ class MainActivity : ComponentActivity() {
|
||||||
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
SharedPreferences.OnSharedPreferenceChangeListener { sharedPreferences, key ->
|
||||||
when (key) {
|
when (key) {
|
||||||
colorPaletteModeKey -> {
|
colorPaletteModeKey -> {
|
||||||
val colorPaletteMode = sharedPreferences.getEnum(key, ColorPaletteMode.System)
|
val colorPaletteMode =
|
||||||
|
sharedPreferences.getEnum(key, ColorPaletteMode.System)
|
||||||
|
|
||||||
appearance = appearance.copy(
|
appearance = appearance.copy(
|
||||||
colorPalette = colorPaletteMode.palette(isSystemInDarkTheme),
|
colorPalette = colorPaletteMode.palette(isSystemInDarkTheme),
|
||||||
|
@ -110,7 +108,8 @@ class MainActivity : ComponentActivity() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
thumbnailRoundnessKey -> {
|
thumbnailRoundnessKey -> {
|
||||||
val thumbnailRoundness = sharedPreferences.getEnum(key, ThumbnailRoundness.Light)
|
val thumbnailRoundness =
|
||||||
|
sharedPreferences.getEnum(key, ThumbnailRoundness.Light)
|
||||||
|
|
||||||
appearance = appearance.copy(
|
appearance = appearance.copy(
|
||||||
thumbnailShape = thumbnailRoundness.shape()
|
thumbnailShape = thumbnailRoundness.shape()
|
||||||
|
@ -130,7 +129,8 @@ class MainActivity : ComponentActivity() {
|
||||||
|
|
||||||
val systemUiController = rememberSystemUiController()
|
val systemUiController = rememberSystemUiController()
|
||||||
|
|
||||||
val rippleTheme = remember(appearance.colorPalette.text, appearance.colorPalette.isDark) {
|
val rippleTheme =
|
||||||
|
remember(appearance.colorPalette.text, appearance.colorPalette.isDark) {
|
||||||
object : RippleTheme {
|
object : RippleTheme {
|
||||||
@Composable
|
@Composable
|
||||||
override fun defaultColor(): Color = RippleTheme.defaultRippleColor(
|
override fun defaultColor(): Color = RippleTheme.defaultRippleColor(
|
||||||
|
@ -165,7 +165,10 @@ class MainActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
SideEffect {
|
SideEffect {
|
||||||
systemUiController.setSystemBarsColor(appearance.colorPalette.background, !appearance.colorPalette.isDark)
|
systemUiController.setSystemBarsColor(
|
||||||
|
appearance.colorPalette.background,
|
||||||
|
!appearance.colorPalette.isDark
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
CompositionLocalProvider(
|
CompositionLocalProvider(
|
||||||
|
@ -185,15 +188,26 @@ class MainActivity : ComponentActivity() {
|
||||||
) {
|
) {
|
||||||
when (val uri = uri) {
|
when (val uri = uri) {
|
||||||
null -> {
|
null -> {
|
||||||
|
val playerBottomSheetState = rememberBottomSheetState(
|
||||||
|
lowerBound = Dimensions.collapsedPlayer, upperBound = maxHeight
|
||||||
|
)
|
||||||
|
|
||||||
HomeScreen()
|
HomeScreen()
|
||||||
|
|
||||||
PlayerView(
|
PlayerView(
|
||||||
layoutState = rememberBottomSheetState(
|
layoutState = playerBottomSheetState,
|
||||||
lowerBound = Dimensions.collapsedPlayer, upperBound = maxHeight
|
|
||||||
),
|
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomCenter)
|
.align(Alignment.BottomCenter)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
binder?.player?.let { player ->
|
||||||
|
ExpandPlayerOnPlaylistChange(
|
||||||
|
player = player,
|
||||||
|
expand = {
|
||||||
|
playerBottomSheetState.expand(tween(500))
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else -> IntentUriScreen(uri = uri)
|
else -> IntentUriScreen(uri = uri)
|
||||||
}
|
}
|
||||||
|
@ -215,3 +229,16 @@ class MainActivity : ComponentActivity() {
|
||||||
}
|
}
|
||||||
|
|
||||||
val LocalPlayerServiceBinder = staticCompositionLocalOf<PlayerService.Binder?> { null }
|
val LocalPlayerServiceBinder = staticCompositionLocalOf<PlayerService.Binder?> { null }
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ExpandPlayerOnPlaylistChange(player: Player, expand: () -> Unit) {
|
||||||
|
DisposableEffect(player, expand) {
|
||||||
|
player.listener(object : Player.Listener {
|
||||||
|
override fun onMediaItemTransition(mediaItem: MediaItem?, reason: Int) {
|
||||||
|
if (reason == Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED) {
|
||||||
|
expand()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,22 +1,20 @@
|
||||||
package it.vfsfitvnm.vimusic.ui.components
|
package it.vfsfitvnm.vimusic.ui.components
|
||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.core.Animatable
|
import androidx.compose.animation.core.*
|
||||||
import androidx.compose.animation.core.VectorConverter
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.gestures.DraggableState
|
import androidx.compose.foundation.gestures.DraggableState
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.gestures.detectVerticalDragGestures
|
import androidx.compose.foundation.gestures.detectVerticalDragGestures
|
||||||
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material.ripple.rememberRipple
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.draw.drawBehind
|
|
||||||
import androidx.compose.ui.draw.shadow
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.graphics.RectangleShape
|
|
||||||
import androidx.compose.ui.graphics.Shape
|
|
||||||
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
|
||||||
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
|
||||||
import androidx.compose.ui.input.pointer.pointerInput
|
import androidx.compose.ui.input.pointer.pointerInput
|
||||||
|
@ -27,39 +25,20 @@ import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.IntOffset
|
import androidx.compose.ui.unit.IntOffset
|
||||||
import androidx.compose.ui.unit.Velocity
|
import androidx.compose.ui.unit.Velocity
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.coroutineScope
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun BottomSheet(
|
fun BottomSheet(
|
||||||
state: BottomSheetState,
|
state: BottomSheetState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
peekHeight: Dp = 0.dp,
|
peekHeight: Dp = 0.dp,
|
||||||
elevation: Dp = 8.dp,
|
elevation: Dp = 8.dp,
|
||||||
shape: Shape = RectangleShape,
|
|
||||||
handleOutsideInteractionsWhenExpanded: Boolean = false,
|
|
||||||
collapsedContent: @Composable BoxScope.() -> Unit,
|
collapsedContent: @Composable BoxScope.() -> Unit,
|
||||||
content: @Composable BoxScope.() -> Unit
|
content: @Composable BoxScope.() -> Unit
|
||||||
) {
|
) {
|
||||||
Box {
|
|
||||||
if (handleOutsideInteractionsWhenExpanded && !state.isCollapsed) {
|
|
||||||
Spacer(
|
|
||||||
modifier = Modifier
|
|
||||||
.pointerInput(state) {
|
|
||||||
detectTapGestures {
|
|
||||||
state.collapse()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.draggableBottomSheet(state)
|
|
||||||
.drawBehind {
|
|
||||||
drawRect(color = Color.Black.copy(alpha = 0.5f * state.progress))
|
|
||||||
}
|
|
||||||
.fillMaxSize()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.offset {
|
.offset {
|
||||||
|
@ -68,9 +47,39 @@ fun BottomSheet(
|
||||||
.coerceAtLeast(0)
|
.coerceAtLeast(0)
|
||||||
IntOffset(x = 0, y = y)
|
IntOffset(x = 0, y = y)
|
||||||
}
|
}
|
||||||
.shadow(elevation = elevation, shape = shape)
|
.shadow(elevation = elevation)
|
||||||
.clip(shape)
|
.pointerInput(state) {
|
||||||
.draggableBottomSheet(state)
|
var initialValue = 0.dp
|
||||||
|
val velocityTracker = VelocityTracker()
|
||||||
|
|
||||||
|
detectVerticalDragGestures(
|
||||||
|
onDragStart = {
|
||||||
|
initialValue = state.value
|
||||||
|
},
|
||||||
|
onVerticalDrag = { change, dragAmount ->
|
||||||
|
velocityTracker.addPointerInputChange(change)
|
||||||
|
state.dispatchRawDelta(dragAmount)
|
||||||
|
},
|
||||||
|
onDragEnd = {
|
||||||
|
val velocity = velocityTracker.calculateVelocity().y.absoluteValue
|
||||||
|
velocityTracker.resetTracking()
|
||||||
|
|
||||||
|
if (velocity.absoluteValue > 300 && initialValue != state.value) {
|
||||||
|
if (initialValue > state.value) {
|
||||||
|
state.collapse()
|
||||||
|
} else {
|
||||||
|
state.expand()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (state.upperBound - state.value > state.value - state.lowerBound) {
|
||||||
|
state.collapse()
|
||||||
|
} else {
|
||||||
|
state.expand()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
.pointerInput(state) {
|
.pointerInput(state) {
|
||||||
if (!state.isRunning && state.isCollapsed) {
|
if (!state.isRunning && state.isCollapsed) {
|
||||||
detectTapGestures {
|
detectTapGestures {
|
||||||
|
@ -81,11 +90,25 @@ fun BottomSheet(
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
if (!state.isCollapsed) {
|
if (!state.isCollapsed) {
|
||||||
BackHandler(onBack = state.collapse)
|
BackHandler(onBack = state::collapseSoft)
|
||||||
content()
|
content()
|
||||||
}
|
}
|
||||||
|
|
||||||
collapsedContent()
|
if (!state.isExpanded) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.graphicsLayer {
|
||||||
|
alpha = 1f - (state.progress * 16).coerceAtMost(1f)
|
||||||
|
}
|
||||||
|
.clickable(
|
||||||
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
|
indication = rememberRipple(bounded = true),
|
||||||
|
onClick = state::expandSoft
|
||||||
|
)
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(state.lowerBound),
|
||||||
|
content = collapsedContent
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,25 +116,63 @@ fun BottomSheet(
|
||||||
@Stable
|
@Stable
|
||||||
class BottomSheetState(
|
class BottomSheetState(
|
||||||
draggableState: DraggableState,
|
draggableState: DraggableState,
|
||||||
valueState: State<Dp>,
|
private val coroutineScope: CoroutineScope,
|
||||||
isRunningState: State<Boolean>,
|
private val animatable: Animatable<Dp, AnimationVector1D>,
|
||||||
isCollapsedState: State<Boolean>,
|
private val onWasExpandedChanged: (Boolean) -> Unit,
|
||||||
isExpandedState: State<Boolean>,
|
|
||||||
progressState: State<Float>,
|
|
||||||
val lowerBound: Dp,
|
|
||||||
val upperBound: Dp,
|
|
||||||
val collapse: () -> Unit,
|
|
||||||
val expand: () -> Unit,
|
|
||||||
) : DraggableState by draggableState {
|
) : DraggableState by draggableState {
|
||||||
val value by valueState
|
val lowerBound: Dp
|
||||||
|
get() = animatable.lowerBound!!
|
||||||
|
|
||||||
val isRunning by isRunningState
|
val upperBound: Dp
|
||||||
|
get() = animatable.upperBound!!
|
||||||
|
|
||||||
val isCollapsed by isCollapsedState
|
val value by animatable.asState()
|
||||||
|
|
||||||
val isExpanded by isExpandedState
|
val isRunning by derivedStateOf {
|
||||||
|
animatable.isRunning
|
||||||
|
}
|
||||||
|
|
||||||
val progress by progressState
|
val isCollapsed by derivedStateOf {
|
||||||
|
value == animatable.lowerBound
|
||||||
|
}
|
||||||
|
|
||||||
|
val isExpanded by derivedStateOf {
|
||||||
|
value == animatable.upperBound
|
||||||
|
}
|
||||||
|
|
||||||
|
val progress by derivedStateOf {
|
||||||
|
1f - (animatable.upperBound!! - animatable.value) / (animatable.upperBound!! - animatable.lowerBound!!)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun collapse(animationSpec: AnimationSpec<Dp>) {
|
||||||
|
onWasExpandedChanged(false)
|
||||||
|
coroutineScope.launch {
|
||||||
|
animatable.animateTo(animatable.lowerBound!!, animationSpec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expand(animationSpec: AnimationSpec<Dp>) {
|
||||||
|
onWasExpandedChanged(true)
|
||||||
|
coroutineScope.launch {
|
||||||
|
animatable.animateTo(animatable.upperBound!!, animationSpec)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun collapse() {
|
||||||
|
collapse(SpringSpec())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expand() {
|
||||||
|
expand(SpringSpec())
|
||||||
|
}
|
||||||
|
|
||||||
|
fun collapseSoft() {
|
||||||
|
collapse(tween(300))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun expandSoft() {
|
||||||
|
expand(tween(300))
|
||||||
|
}
|
||||||
|
|
||||||
fun nestedScrollConnection(initialIsTopReached: Boolean = true): NestedScrollConnection {
|
fun nestedScrollConnection(initialIsTopReached: Boolean = true): NestedScrollConnection {
|
||||||
return object : NestedScrollConnection {
|
return object : NestedScrollConnection {
|
||||||
|
@ -148,7 +209,7 @@ class BottomSheetState(
|
||||||
if (available.y.absoluteValue > 1000) {
|
if (available.y.absoluteValue > 1000) {
|
||||||
collapse()
|
collapse()
|
||||||
} else {
|
} else {
|
||||||
if (upperBound - value > value - lowerBound) {
|
if (animatable.upperBound!! - value > value - animatable.lowerBound!!) {
|
||||||
collapse()
|
collapse()
|
||||||
} else {
|
} else {
|
||||||
expand()
|
expand()
|
||||||
|
@ -179,79 +240,23 @@ fun rememberBottomSheetState(lowerBound: Dp, upperBound: Dp): BottomSheetState {
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
val animatable = remember(lowerBound, upperBound) {
|
return remember(lowerBound, upperBound, coroutineScope) {
|
||||||
|
val animatable =
|
||||||
Animatable(if (wasExpanded) upperBound else lowerBound, Dp.VectorConverter).also {
|
Animatable(if (wasExpanded) upperBound else lowerBound, Dp.VectorConverter).also {
|
||||||
it.updateBounds(lowerBound.coerceAtMost(upperBound), upperBound)
|
it.updateBounds(lowerBound.coerceAtMost(upperBound), upperBound)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return remember(animatable, coroutineScope) {
|
|
||||||
BottomSheetState(
|
BottomSheetState(
|
||||||
draggableState = DraggableState { delta ->
|
draggableState = DraggableState { delta ->
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
animatable.snapTo(animatable.value - with(density) { delta.toDp() })
|
animatable.snapTo(animatable.value - with(density) { delta.toDp() })
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
valueState = animatable.asState(),
|
onWasExpandedChanged = {
|
||||||
lowerBound = lowerBound,
|
wasExpanded = it
|
||||||
upperBound = upperBound,
|
|
||||||
isRunningState = derivedStateOf {
|
|
||||||
animatable.isRunning
|
|
||||||
},
|
},
|
||||||
isCollapsedState = derivedStateOf {
|
coroutineScope = coroutineScope,
|
||||||
animatable.value == lowerBound
|
animatable = animatable
|
||||||
},
|
|
||||||
isExpandedState = derivedStateOf {
|
|
||||||
animatable.value == upperBound
|
|
||||||
},
|
|
||||||
progressState = derivedStateOf {
|
|
||||||
1f - (upperBound - animatable.value) / (upperBound - lowerBound)
|
|
||||||
},
|
|
||||||
collapse = {
|
|
||||||
wasExpanded = false
|
|
||||||
coroutineScope.launch {
|
|
||||||
animatable.animateTo(animatable.lowerBound!!)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
expand = {
|
|
||||||
wasExpanded = true
|
|
||||||
coroutineScope.launch {
|
|
||||||
animatable.animateTo(animatable.upperBound!!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Modifier.draggableBottomSheet(state: BottomSheetState) = pointerInput(state) {
|
|
||||||
var initialValue = 0.dp
|
|
||||||
val velocityTracker = VelocityTracker()
|
|
||||||
|
|
||||||
detectVerticalDragGestures(
|
|
||||||
onDragStart = {
|
|
||||||
initialValue = state.value
|
|
||||||
},
|
|
||||||
onVerticalDrag = { change, dragAmount ->
|
|
||||||
velocityTracker.addPointerInputChange(change)
|
|
||||||
state.dispatchRawDelta(dragAmount)
|
|
||||||
},
|
|
||||||
onDragEnd = {
|
|
||||||
val velocity = velocityTracker.calculateVelocity().y.absoluteValue
|
|
||||||
velocityTracker.resetTracking()
|
|
||||||
|
|
||||||
if (velocity.absoluteValue > 300 && initialValue != state.value) {
|
|
||||||
if (initialValue > state.value) {
|
|
||||||
state.collapse()
|
|
||||||
} else {
|
|
||||||
state.expand()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (state.upperBound - state.value > state.value - state.lowerBound) {
|
|
||||||
state.collapse()
|
|
||||||
} else {
|
|
||||||
state.expand()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
|
@ -196,7 +196,7 @@ fun CurrentPlaylistView(
|
||||||
.clickable(
|
.clickable(
|
||||||
indication = rememberRipple(bounded = true),
|
indication = rememberRipple(bounded = true),
|
||||||
interactionSource = remember { MutableInteractionSource() },
|
interactionSource = remember { MutableInteractionSource() },
|
||||||
onClick = layoutState.collapse
|
onClick = layoutState::collapseSoft
|
||||||
)
|
)
|
||||||
.shadow(elevation = 8.dp)
|
.shadow(elevation = 8.dp)
|
||||||
.height(64.dp)
|
.height(64.dp)
|
||||||
|
|
|
@ -9,7 +9,6 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import it.vfsfitvnm.vimusic.R
|
import it.vfsfitvnm.vimusic.R
|
||||||
|
@ -17,7 +16,6 @@ import it.vfsfitvnm.vimusic.ui.components.BottomSheet
|
||||||
import it.vfsfitvnm.vimusic.ui.components.BottomSheetState
|
import it.vfsfitvnm.vimusic.ui.components.BottomSheetState
|
||||||
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
import it.vfsfitvnm.vimusic.ui.styling.LocalAppearance
|
||||||
|
|
||||||
|
|
||||||
@ExperimentalAnimationApi
|
@ExperimentalAnimationApi
|
||||||
@Composable
|
@Composable
|
||||||
fun PlayerBottomSheet(
|
fun PlayerBottomSheet(
|
||||||
|
@ -38,11 +36,7 @@ fun PlayerBottomSheet(
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.graphicsLayer {
|
.fillMaxSize()
|
||||||
alpha = 1f - (layoutState.progress * 16).coerceAtMost(1f)
|
|
||||||
}
|
|
||||||
.fillMaxWidth()
|
|
||||||
.height(layoutState.lowerBound)
|
|
||||||
.background(colorPalette.background)
|
.background(colorPalette.background)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
|
|
|
@ -91,17 +91,12 @@ fun PlayerView(
|
||||||
state = layoutState,
|
state = layoutState,
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
collapsedContent = {
|
collapsedContent = {
|
||||||
if (!layoutState.isExpanded) {
|
|
||||||
Row(
|
Row(
|
||||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.height(layoutState.lowerBound)
|
|
||||||
.fillMaxWidth()
|
|
||||||
.graphicsLayer {
|
|
||||||
alpha = 1f - (layoutState.progress * 16).coerceAtMost(1f)
|
|
||||||
}
|
|
||||||
.background(colorPalette.elevatedBackground)
|
.background(colorPalette.elevatedBackground)
|
||||||
|
.fillMaxSize()
|
||||||
.drawBehind {
|
.drawBehind {
|
||||||
val progress = positionAndDuration.first.toFloat() / positionAndDuration.second.absoluteValue
|
val progress = positionAndDuration.first.toFloat() / positionAndDuration.second.absoluteValue
|
||||||
val offset = Dimensions.thumbnails.player.songPreview.toPx()
|
val offset = Dimensions.thumbnails.player.songPreview.toPx()
|
||||||
|
@ -177,7 +172,6 @@ fun PlayerView(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
) {
|
) {
|
||||||
var isShowingLyrics by rememberSaveable {
|
var isShowingLyrics by rememberSaveable {
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
|
@ -322,7 +316,7 @@ fun PlayerView(
|
||||||
},
|
},
|
||||||
onSetSleepTimer = {},
|
onSetSleepTimer = {},
|
||||||
onDismiss = menuState::hide,
|
onDismiss = menuState::hide,
|
||||||
onGlobalRouteEmitted = layoutState.collapse,
|
onGlobalRouteEmitted = layoutState::collapseSoft,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -341,7 +335,7 @@ fun PlayerView(
|
||||||
isShowingLyrics = false
|
isShowingLyrics = false
|
||||||
isShowingStatsForNerds = !isShowingStatsForNerds
|
isShowingStatsForNerds = !isShowingStatsForNerds
|
||||||
},
|
},
|
||||||
onGlobalRouteEmitted = layoutState.collapse,
|
onGlobalRouteEmitted = layoutState::collapseSoft,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.align(Alignment.BottomCenter)
|
.align(Alignment.BottomCenter)
|
||||||
)
|
)
|
||||||
|
|
Loading…
Add table
Reference in a new issue