vfsfitvnm 2 anni fa
parent
commit
6474b52490

+ 54 - 27
app/src/main/kotlin/it/vfsfitvnm/vimusic/MainActivity.kt

@@ -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,
-        ExperimentalTextApi::class
-    )
+    @OptIn(ExperimentalFoundationApi::class, ExperimentalAnimationApi::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,21 +129,22 @@ class MainActivity : ComponentActivity() {
 
 
             val systemUiController = rememberSystemUiController()
             val systemUiController = rememberSystemUiController()
 
 
-            val rippleTheme = remember(appearance.colorPalette.text, appearance.colorPalette.isDark) {
-                object : RippleTheme {
-                    @Composable
-                    override fun defaultColor(): Color = RippleTheme.defaultRippleColor(
-                        contentColor = appearance.colorPalette.text,
-                        lightTheme = !appearance.colorPalette.isDark
-                    )
+            val rippleTheme =
+                remember(appearance.colorPalette.text, appearance.colorPalette.isDark) {
+                    object : RippleTheme {
+                        @Composable
+                        override fun defaultColor(): Color = RippleTheme.defaultRippleColor(
+                            contentColor = appearance.colorPalette.text,
+                            lightTheme = !appearance.colorPalette.isDark
+                        )
 
 
-                    @Composable
-                    override fun rippleAlpha(): RippleAlpha = RippleTheme.defaultRippleAlpha(
-                        contentColor = appearance.colorPalette.text,
-                        lightTheme = !appearance.colorPalette.isDark
-                    )
+                        @Composable
+                        override fun rippleAlpha(): RippleAlpha = RippleTheme.defaultRippleAlpha(
+                            contentColor = appearance.colorPalette.text,
+                            lightTheme = !appearance.colorPalette.isDark
+                        )
+                    }
                 }
                 }
-            }
 
 
             val shimmerTheme = remember {
             val shimmerTheme = remember {
                 defaultShimmerTheme.copy(
                 defaultShimmerTheme.copy(
@@ -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(
-                                    lowerBound = Dimensions.collapsedPlayer, upperBound = maxHeight
-                                ),
+                                layoutState = playerBottomSheetState,
                                 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()
+                }
+            }
+        })
+    }
+}

+ 132 - 127
app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/components/BottomSheet.kt

@@ -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.VectorConverter
+import androidx.compose.animation.core.*
+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.RectangleShape
-import androidx.compose.ui.graphics.Shape
+import androidx.compose.ui.graphics.graphicsLayer
 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,65 +25,90 @@ 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(
+        modifier = modifier
+            .offset {
+                val y = (state.upperBound - state.value + peekHeight)
+                    .roundToPx()
+                    .coerceAtLeast(0)
+                IntOffset(x = 0, y = y)
+            }
+            .shadow(elevation = elevation)
+            .pointerInput(state) {
+                var initialValue = 0.dp
+                val velocityTracker = VelocityTracker()
 
 
-        Box(
-            modifier = modifier
-                .offset {
-                    val y = (state.upperBound - state.value + peekHeight)
-                        .roundToPx()
-                        .coerceAtLeast(0)
-                    IntOffset(x = 0, y = y)
-                }
-                .shadow(elevation = elevation, shape = shape)
-                .clip(shape)
-                .draggableBottomSheet(state)
-                .pointerInput(state) {
-                    if (!state.isRunning && state.isCollapsed) {
-                        detectTapGestures {
-                            state.expand()
+                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) {
+                if (!state.isRunning && state.isCollapsed) {
+                    detectTapGestures {
+                        state.expand()
+                    }
                 }
                 }
-                .fillMaxSize()
-        ) {
-            if (!state.isCollapsed) {
-                BackHandler(onBack = state.collapse)
-                content()
             }
             }
+            .fillMaxSize()
+    ) {
+        if (!state.isCollapsed) {
+            BackHandler(onBack = state::collapseSoft)
+            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>,
-    isRunningState: State<Boolean>,
-    isCollapsedState: State<Boolean>,
-    isExpandedState: State<Boolean>,
-    progressState: State<Float>,
-    val lowerBound: Dp,
-    val upperBound: Dp,
-    val collapse: () -> Unit,
-    val expand: () -> Unit,
+    private val coroutineScope: CoroutineScope,
+    private val animatable:  Animatable<Dp, AnimationVector1D>,
+    private val onWasExpandedChanged: (Boolean) -> Unit,
 ) : DraggableState by draggableState {
 ) : DraggableState by draggableState {
-    val value by valueState
+    val lowerBound: Dp
+        get() = animatable.lowerBound!!
+
+    val upperBound: Dp
+        get() = animatable.upperBound!!
+
+    val value by animatable.asState()
+
+    val isRunning by derivedStateOf {
+        animatable.isRunning
+    }
+
+    val isCollapsed by derivedStateOf {
+        value == animatable.lowerBound
+    }
+
+    val isExpanded by derivedStateOf {
+        value == animatable.upperBound
+    }
 
 
-    val isRunning by isRunningState
+    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)
+        }
+    }
 
 
-    val isCollapsed by isCollapsedState
+    fun expand(animationSpec: AnimationSpec<Dp>) {
+        onWasExpandedChanged(true)
+        coroutineScope.launch {
+            animatable.animateTo(animatable.upperBound!!, animationSpec)
+        }
+    }
 
 
-    val isExpanded by isExpandedState
+    fun collapse() {
+        collapse(SpringSpec())
+    }
 
 
-    val progress by progressState
+    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) {
-        Animatable(if (wasExpanded) upperBound else lowerBound, Dp.VectorConverter).also {
-            it.updateBounds(lowerBound.coerceAtMost(upperBound), upperBound)
-        }
-    }
+    return remember(lowerBound, upperBound, coroutineScope) {
+        val animatable =
+            Animatable(if (wasExpanded) upperBound else lowerBound, Dp.VectorConverter).also {
+                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(),
-            lowerBound = lowerBound,
-            upperBound = upperBound,
-            isRunningState = derivedStateOf {
-                animatable.isRunning
+            onWasExpandedChanged = {
+                wasExpanded = it
             },
             },
-            isCollapsedState = derivedStateOf {
-                animatable.value == lowerBound
-            },
-            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!!)
-                }
-            }
+            coroutineScope = coroutineScope,
+            animatable = animatable
         )
         )
     }
     }
 }
 }
-
-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()
-                }
-            }
-        }
-    )
-}

+ 1 - 1
app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/CurrentPlaylistView.kt

@@ -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)

+ 1 - 7
app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerBottomSheet.kt

@@ -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 {
-                        alpha = 1f - (layoutState.progress * 16).coerceAtMost(1f)
-                    }
-                    .fillMaxWidth()
-                    .height(layoutState.lowerBound)
+                    .fillMaxSize()
                     .background(colorPalette.background)
                     .background(colorPalette.background)
             ) {
             ) {
                 Row(
                 Row(

+ 73 - 79
app/src/main/kotlin/it/vfsfitvnm/vimusic/ui/views/PlayerView.kt

@@ -91,90 +91,84 @@ fun PlayerView(
         state = layoutState,
         state = layoutState,
         modifier = modifier,
         modifier = modifier,
         collapsedContent = {
         collapsedContent = {
-            if (!layoutState.isExpanded) {
-                Row(
-                    horizontalArrangement = Arrangement.spacedBy(12.dp),
-                    verticalAlignment = Alignment.CenterVertically,
+            Row(
+                horizontalArrangement = Arrangement.spacedBy(12.dp),
+                verticalAlignment = Alignment.CenterVertically,
+                modifier = Modifier
+                    .background(colorPalette.elevatedBackground)
+                    .fillMaxSize()
+                    .drawBehind {
+                        val progress = positionAndDuration.first.toFloat() / positionAndDuration.second.absoluteValue
+                        val offset = Dimensions.thumbnails.player.songPreview.toPx()
+
+                        drawLine(
+                            color = colorPalette.text,
+                            start = Offset(
+                                x = offset,
+                                y = 1.dp.toPx()
+                            ),
+                            end = Offset(
+                                x = ((size.width - offset) * progress) + offset,
+                                y = 1.dp.toPx()
+                            ),
+                            strokeWidth = 2.dp.toPx()
+                        )
+                    }
+            ) {
+                AsyncImage(
+                    model = mediaItem.mediaMetadata.artworkUri.thumbnail(Dimensions.thumbnails.player.songPreview.px),
+                    contentDescription = null,
+                    contentScale = ContentScale.Crop,
                     modifier = Modifier
                     modifier = Modifier
-                        .height(layoutState.lowerBound)
-                        .fillMaxWidth()
-                        .graphicsLayer {
-                            alpha = 1f - (layoutState.progress * 16).coerceAtMost(1f)
-                        }
-                        .background(colorPalette.elevatedBackground)
-                        .drawBehind {
-                            val progress = positionAndDuration.first.toFloat() / positionAndDuration.second.absoluteValue
-                            val offset = Dimensions.thumbnails.player.songPreview.toPx()
-
-                            drawLine(
-                                color = colorPalette.text,
-                                start = Offset(
-                                    x = offset,
-                                    y = 1.dp.toPx()
-                                ),
-                                end = Offset(
-                                    x = ((size.width - offset) * progress) + offset,
-                                    y = 1.dp.toPx()
-                                ),
-                                strokeWidth = 2.dp.toPx()
-                            )
-                        }
+                        .size(Dimensions.thumbnails.player.songPreview)
+                )
+
+                Column(
+                    verticalArrangement = Arrangement.Center,
+                    modifier = Modifier
+                        .weight(1f)
                 ) {
                 ) {
-                    AsyncImage(
-                        model = mediaItem.mediaMetadata.artworkUri.thumbnail(Dimensions.thumbnails.player.songPreview.px),
+                    BasicText(
+                        text = mediaItem.mediaMetadata.title?.toString() ?: "",
+                        style = typography.xs.semiBold,
+                        maxLines = 1,
+                        overflow = TextOverflow.Ellipsis,
+                    )
+                    BasicText(
+                        text = mediaItem.mediaMetadata.artist?.toString() ?: "",
+                        style = typography.xs.semiBold.secondary,
+                        maxLines = 1,
+                        overflow = TextOverflow.Ellipsis,
+                    )
+                }
+
+                if (shouldBePlaying) {
+                    Image(
+                        painter = painterResource(R.drawable.pause),
                         contentDescription = null,
                         contentDescription = null,
-                        contentScale = ContentScale.Crop,
+                        colorFilter = ColorFilter.tint(colorPalette.text),
                         modifier = Modifier
                         modifier = Modifier
-                            .size(Dimensions.thumbnails.player.songPreview)
+                            .clickable(onClick = binder.player::pause)
+                            .padding(vertical = 8.dp)
+                            .padding(horizontal = 16.dp)
+                            .size(22.dp)
                     )
                     )
-
-                    Column(
-                        verticalArrangement = Arrangement.Center,
+                } else {
+                    Image(
+                        painter = painterResource(R.drawable.play),
+                        contentDescription = null,
+                        colorFilter = ColorFilter.tint(colorPalette.text),
                         modifier = Modifier
                         modifier = Modifier
-                            .weight(1f)
-                    ) {
-                        BasicText(
-                            text = mediaItem.mediaMetadata.title?.toString() ?: "",
-                            style = typography.xs.semiBold,
-                            maxLines = 1,
-                            overflow = TextOverflow.Ellipsis,
-                        )
-                        BasicText(
-                            text = mediaItem.mediaMetadata.artist?.toString() ?: "",
-                            style = typography.xs.semiBold.secondary,
-                            maxLines = 1,
-                            overflow = TextOverflow.Ellipsis,
-                        )
-                    }
-
-                    if (shouldBePlaying) {
-                        Image(
-                            painter = painterResource(R.drawable.pause),
-                            contentDescription = null,
-                            colorFilter = ColorFilter.tint(colorPalette.text),
-                            modifier = Modifier
-                                .clickable(onClick = binder.player::pause)
-                                .padding(vertical = 8.dp)
-                                .padding(horizontal = 16.dp)
-                                .size(22.dp)
-                        )
-                    } else {
-                        Image(
-                            painter = painterResource(R.drawable.play),
-                            contentDescription = null,
-                            colorFilter = ColorFilter.tint(colorPalette.text),
-                            modifier = Modifier
-                                .clickable {
-                                    if (binder.player.playbackState == Player.STATE_IDLE) {
-                                        binder.player.prepare()
-                                    }
-                                    binder.player.play()
+                            .clickable {
+                                if (binder.player.playbackState == Player.STATE_IDLE) {
+                                    binder.player.prepare()
                                 }
                                 }
-                                .padding(vertical = 8.dp)
-                                .padding(horizontal = 16.dp)
-                                .size(22.dp)
-                        )
-                    }
+                                binder.player.play()
+                            }
+                            .padding(vertical = 8.dp)
+                            .padding(horizontal = 16.dp)
+                            .size(22.dp)
+                    )
                 }
                 }
             }
             }
         }
         }
@@ -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)
         )
         )