Skip to content

Commit

Permalink
Fix bottom sheet not behaving properly (closes #309)
Browse files Browse the repository at this point in the history
  • Loading branch information
25huizengek1 committed May 29, 2024
1 parent 29c8dd4 commit a20a4b9
Show file tree
Hide file tree
Showing 11 changed files with 144 additions and 157 deletions.
4 changes: 2 additions & 2 deletions app/src/main/kotlin/app/vitune/android/Database.kt
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ import app.vitune.android.models.SongPlaylistMap
import app.vitune.android.models.SongWithContentLength
import app.vitune.android.models.SortedSongPlaylistMap
import app.vitune.android.service.LOCAL_KEY_PREFIX
import app.vitune.android.utils.SongBundleAccessor
import app.vitune.android.utils.songBundle
import app.vitune.core.data.enums.AlbumSortBy
import app.vitune.core.data.enums.ArtistSortBy
import app.vitune.core.data.enums.PlaylistSortBy
Expand Down Expand Up @@ -553,7 +553,7 @@ interface Database {

@Transaction
fun insert(mediaItem: MediaItem, block: (Song) -> Song = { it }) {
val extras = mediaItem.mediaMetadata.extras?.let { SongBundleAccessor(it) }
val extras = mediaItem.mediaMetadata.extras?.songBundle
val song = Song(
id = mediaItem.mediaId,
title = mediaItem.mediaMetadata.title?.toString().orEmpty(),
Expand Down
197 changes: 90 additions & 107 deletions app/src/main/kotlin/app/vitune/android/MainApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import androidx.compose.material.ripple.LocalRippleTheme
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.compositionLocalOf
import androidx.compose.runtime.getValue
Expand Down Expand Up @@ -72,13 +71,13 @@ import app.vitune.android.ui.screens.player.Player
import app.vitune.android.ui.screens.playlistRoute
import app.vitune.android.utils.DisposableListener
import app.vitune.android.utils.LocalMonetCompat
import app.vitune.android.utils.SongBundleAccessor
import app.vitune.android.utils.asMediaItem
import app.vitune.android.utils.collectProvidedBitmapAsState
import app.vitune.android.utils.forcePlay
import app.vitune.android.utils.intent
import app.vitune.android.utils.invokeOnReady
import app.vitune.android.utils.setDefaultPalette
import app.vitune.android.utils.songBundle
import app.vitune.android.utils.toast
import app.vitune.compose.persist.LocalPersistMap
import app.vitune.compose.persist.PersistMap
Expand Down Expand Up @@ -190,125 +189,109 @@ class MainActivity : ComponentActivity(), MonetColorsChangedListener {

@Suppress("CyclomaticComplexMethod")
@OptIn(ExperimentalLayoutApi::class)
fun setContent() {
val fromNotification = intent?.extras?.getBoolean("fromNotification") == true

setContent {
AppWrapper {
val density = LocalDensity.current
val windowsInsets = WindowInsets.systemBars
val bottomDp = with(density) { windowsInsets.getBottom(density).toDp() }

val imeVisible = WindowInsets.isImeVisible
val imeBottomDp = with(density) { WindowInsets.ime.getBottom(density).toDp() }
val animatedBottomDp by animateDpAsState(
targetValue = if (imeVisible) 0.dp else bottomDp,
label = ""
)
fun setContent() = setContent {
AppWrapper {
val density = LocalDensity.current
val windowsInsets = WindowInsets.systemBars
val bottomDp = with(density) { windowsInsets.getBottom(density).toDp() }

val imeVisible = WindowInsets.isImeVisible
val imeBottomDp = with(density) { WindowInsets.ime.getBottom(density).toDp() }
val animatedBottomDp by animateDpAsState(
targetValue = if (imeVisible) 0.dp else bottomDp,
label = ""
)

val playerBottomSheetState = rememberBottomSheetState(
dismissedBound = 0.dp,
collapsedBound = Dimensions.items.collapsedPlayerHeight + bottomDp,
expandedBound = maxHeight
)

val playerAwareWindowInsets = remember(
bottomDp,
animatedBottomDp,
playerBottomSheetState.value,
imeVisible,
imeBottomDp
) {
val bottom =
if (imeVisible) imeBottomDp.coerceAtLeast(playerBottomSheetState.value)
else playerBottomSheetState.value.coerceIn(
animatedBottomDp..playerBottomSheetState.collapsedBound
)

val playerBottomSheetState = rememberBottomSheetState(
dismissedBound = 0.dp,
collapsedBound = Dimensions.items.collapsedPlayerHeight + bottomDp,
expandedBound = maxHeight
)
windowsInsets
.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top)
.add(WindowInsets(bottom = bottom))
}

val playerAwareWindowInsets = remember(
bottomDp,
animatedBottomDp,
playerBottomSheetState.value,
imeVisible,
imeBottomDp
) {
val bottom =
if (imeVisible) imeBottomDp.coerceAtLeast(playerBottomSheetState.value)
else playerBottomSheetState.value.coerceIn(
animatedBottomDp..playerBottomSheetState.collapsedBound
)

windowsInsets
.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top)
.add(WindowInsets(bottom = bottom))
CompositionLocalProvider(
LocalIndication provides rememberRipple(),
LocalRippleTheme provides rippleTheme(),
LocalShimmerTheme provides shimmerTheme(),
LocalPlayerServiceBinder provides binder,
LocalPlayerAwareWindowInsets provides playerAwareWindowInsets,
LocalLayoutDirection provides LayoutDirection.Ltr,
LocalPersistMap provides Dependencies.application.persistMap,
LocalMonetCompat provides monet
) {
val isDownloading by downloadState.collectAsState()

Box {
HomeScreen(
onPlaylistUrl = { url ->
onNewIntent(Intent.parseUri(url, 0))
}
)
}

CompositionLocalProvider(
LocalIndication provides rememberRipple(),
LocalRippleTheme provides rippleTheme(),
LocalShimmerTheme provides shimmerTheme(),
LocalPlayerServiceBinder provides binder,
LocalPlayerAwareWindowInsets provides playerAwareWindowInsets,
LocalLayoutDirection provides LayoutDirection.Ltr,
LocalPersistMap provides Dependencies.application.persistMap,
LocalMonetCompat provides monet
AnimatedVisibility(
visible = isDownloading,
modifier = Modifier.padding(playerAwareWindowInsets.asPaddingValues())
) {
val isDownloading by downloadState.collectAsState()

Box {
HomeScreen(
onPlaylistUrl = { url ->
onNewIntent(Intent.parseUri(url, 0))
}
)
}

AnimatedVisibility(
visible = isDownloading,
modifier = Modifier.padding(playerAwareWindowInsets.asPaddingValues())
) {
LinearProgressIndicator(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.TopCenter)
)
}

CompositionLocalProvider(
LocalAppearance provides LocalAppearance.current.let {
if (it.colorPalette.isDark && AppearancePreferences.darkness == Darkness.AMOLED) {
it.copy(colorPalette = it.colorPalette.amoled())
} else it
}
) {
Player(
layoutState = playerBottomSheetState,
modifier = Modifier.align(Alignment.BottomCenter)
)
}

BottomSheetMenu(
LinearProgressIndicator(
modifier = Modifier
.align(Alignment.BottomCenter)
.imePadding()
.fillMaxWidth()
.align(Alignment.TopCenter)
)
}

LaunchedEffect(binder?.player) {
val player = binder?.player ?: return@LaunchedEffect

when {
player.currentMediaItem == null ->
if (!playerBottomSheetState.isDismissed) playerBottomSheetState.dismiss()

playerBottomSheetState.isDismissed -> if (fromNotification) {
intent.replaceExtras(null)
playerBottomSheetState.expandSoft()
} else playerBottomSheetState.collapseSoft()
CompositionLocalProvider(
LocalAppearance provides LocalAppearance.current.let {
if (it.colorPalette.isDark && AppearancePreferences.darkness == Darkness.AMOLED) {
it.copy(colorPalette = it.colorPalette.amoled())
} else it
}
) {
Player(
layoutState = playerBottomSheetState,
modifier = Modifier.align(Alignment.BottomCenter)
)
}

binder?.player?.DisposableListener {
object : Player.Listener {
override fun onMediaItemTransition(
mediaItem: MediaItem?,
reason: Int
) = when {
reason != Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED || mediaItem == null -> Unit
BottomSheetMenu(
modifier = Modifier
.align(Alignment.BottomCenter)
.imePadding()
)
}

binder?.player?.DisposableListener {
object : Player.Listener {
override fun onMediaItemTransition(
mediaItem: MediaItem?,
reason: Int
) = when {
mediaItem == null -> playerBottomSheetState.dismissSoft()

mediaItem.mediaMetadata.extras
?.let { SongBundleAccessor(it) }
?.isFromPersistentQueue == true -> playerBottomSheetState.collapseSoft()
reason == Player.MEDIA_ITEM_TRANSITION_REASON_PLAYLIST_CHANGED &&
mediaItem.mediaMetadata.extras?.songBundle?.isFromPersistentQueue != true
-> playerBottomSheetState.expandSoft()

else -> playerBottomSheetState.expandSoft()
}
playerBottomSheetState.dismissed -> playerBottomSheetState.collapseSoft()

else -> Unit
}
}
}
Expand Down
17 changes: 6 additions & 11 deletions app/src/main/kotlin/app/vitune/android/service/PlayerService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ import app.vitune.android.transaction
import app.vitune.android.utils.ActionReceiver
import app.vitune.android.utils.ConditionalCacheDataSourceFactory
import app.vitune.android.utils.InvincibleService
import app.vitune.android.utils.SongBundleAccessor
import app.vitune.android.utils.TimerJob
import app.vitune.android.utils.YouTubeRadio
import app.vitune.android.utils.activityPendingIntent
Expand All @@ -95,6 +94,7 @@ import app.vitune.android.utils.intent
import app.vitune.android.utils.mediaItems
import app.vitune.android.utils.setPlaybackPitch
import app.vitune.android.utils.shouldBePlaying
import app.vitune.android.utils.songBundle
import app.vitune.android.utils.thumbnail
import app.vitune.android.utils.timer
import app.vitune.android.utils.toast
Expand Down Expand Up @@ -602,11 +602,9 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
.setCustomCacheKey(item.mediaItem.mediaId)
.build()
.apply {
mediaMetadata.extras
?.let { SongBundleAccessor(it) }
?.apply {
isFromPersistentQueue = true
}
mediaMetadata.extras?.songBundle?.apply {
isFromPersistentQueue = true
}
}
},
/* startIndex = */ index,
Expand Down Expand Up @@ -866,9 +864,7 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
)
.setOngoing(false)
.setContentIntent(
activityPendingIntent<MainActivity>(flags = PendingIntent.FLAG_UPDATE_CURRENT) {
putExtra("fromNotification", true)
}
activityPendingIntent<MainActivity>(flags = PendingIntent.FLAG_UPDATE_CURRENT)
)
.setDeleteIntent(broadcastPendingIntent<NotificationDismissReceiver>())
.setVisibility(Notification.VISIBILITY_PUBLIC)
Expand Down Expand Up @@ -1245,8 +1241,7 @@ class PlayerService : InvincibleService(), Player.Listener, PlaybackStatsListene
val url = when (val status = body.playabilityStatus?.status) {
"OK" -> format?.let { _ ->
val mediaItem = runCatching { findMediaItem(videoId) }.getOrNull()
val extras = mediaItem?.mediaMetadata?.extras
?.let { SongBundleAccessor(it) }
val extras = mediaItem?.mediaMetadata?.extras?.songBundle

if (extras?.durationText == null) format.approxDurationMs
?.div(1000)
Expand Down
Loading

0 comments on commit a20a4b9

Please sign in to comment.