Skip to content

Commit

Permalink
ui/fix: Bottom sheet player now showing correctly
Browse files Browse the repository at this point in the history
Signed-off-by: Gabriel Fontán <[email protected]>
  • Loading branch information
BobbyESP committed May 4, 2024
1 parent 0eb4a79 commit 1503669
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 120 deletions.
241 changes: 141 additions & 100 deletions app/src/main/java/com/bobbyesp/metadator/presentation/Navigation.kt
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package com.bobbyesp.metadator.presentation

import android.annotation.SuppressLint
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.add
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.systemBars
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Square
import androidx.compose.material3.Icon
Expand All @@ -19,13 +25,15 @@ import androidx.compose.material3.Snackbar
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
Expand All @@ -41,6 +49,7 @@ import com.bobbyesp.metadator.R
import com.bobbyesp.metadator.model.ParcelableSong
import com.bobbyesp.metadator.presentation.common.LocalDrawerState
import com.bobbyesp.metadator.presentation.common.LocalNavController
import com.bobbyesp.metadator.presentation.common.LocalPlayerAwareWindowInsets
import com.bobbyesp.metadator.presentation.common.LocalSnackbarHostState
import com.bobbyesp.metadator.presentation.common.NavArgs
import com.bobbyesp.metadator.presentation.common.Route
Expand Down Expand Up @@ -95,123 +104,155 @@ fun Navigator() {
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
) {
val density = LocalDensity.current
val windowsInsets = WindowInsets.systemBars

val bottomInset = with(density) { windowsInsets.getBottom(density).toDp() }
val mediaPlayerSheetState = rememberDraggableBottomSheetState(
dismissedBound = 0.dp,
collapsedBound = CollapsedPlayerHeight,
collapsedBound = bottomInset + CollapsedPlayerHeight,
expandedBound = maxHeight,
animationSpec = PlayerAnimationSpec,
)
ModalNavigationDrawer(
drawerState = drawerState,
gesturesEnabled = canOpenDrawer,
drawerContent = {
ModalDrawerSheet {
Text(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
text = stringResource(id = R.string.app_name).uppercase(),
style = MaterialTheme.typography.headlineMedium.copy(
letterSpacing = 4.sp,
),
fontWeight = FontWeight.Bold,
fontFamily = FontFamily.Monospace
)
routesToNavigate.forEachIndexed { _, route ->
val isSelected = currentRootRoute.value == route.route
NavigationDrawerItem(
label = {
Text(text = route.title?.let { stringResource(id = it) } ?: "")
},
selected = isSelected,
onClick = {
if (isSelected) {
scope.launch {
drawerState.close()
}
return@NavigationDrawerItem
} else {
navController.navigate(route.route) {
popUpTo(Route.MainHost.route) {
saveState = true

val targetBottom = if (!mediaPlayerSheetState.isDismissed) {
CollapsedPlayerHeight + bottomInset
} else {
bottomInset
}

val animatedBottom by animateDpAsState(
targetValue = targetBottom,
label = "Animated bottom insets for player sheet"
)

val playerAwareWindowInsets = remember(
bottomInset,
mediaPlayerSheetState.isDismissed,
animatedBottom
) {
val insetsBottom = animatedBottom
windowsInsets
.only(WindowInsetsSides.Horizontal + WindowInsetsSides.Top)
.add(WindowInsets(bottom = insetsBottom))
}


CompositionLocalProvider(
LocalPlayerAwareWindowInsets provides playerAwareWindowInsets
) {
ModalNavigationDrawer(
drawerState = drawerState,
gesturesEnabled = canOpenDrawer,
drawerContent = {
ModalDrawerSheet {
Text(
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp),
text = stringResource(id = R.string.app_name).uppercase(),
style = MaterialTheme.typography.headlineMedium.copy(
letterSpacing = 4.sp,
),
fontWeight = FontWeight.Bold,
fontFamily = FontFamily.Monospace
)
routesToNavigate.forEachIndexed { _, route ->
val isSelected = currentRootRoute.value == route.route
NavigationDrawerItem(
label = {
Text(text = route.title?.let { stringResource(id = it) } ?: "")
},
selected = isSelected,
onClick = {
if (isSelected) {
scope.launch {
drawerState.close()
}
return@NavigationDrawerItem
} else {
navController.navigate(route.route) {
popUpTo(Route.MainHost.route) {
saveState = true
}
launchSingleTop = true
restoreState = true
}
scope.launch {
drawerState.close()
}
launchSingleTop = true
restoreState = true
}
scope.launch {
drawerState.close()
}
}
},
icon = {
Icon(
imageVector = route.icon ?: Icons.Rounded.Square,
contentDescription = route.title?.let { stringResource(id = it) })
},
badge = {

},
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
},
icon = {
Icon(
imageVector = route.icon ?: Icons.Rounded.Square,
contentDescription = route.title?.let { stringResource(id = it) })
},
badge = {

},
modifier = Modifier.padding(NavigationDrawerItemDefaults.ItemPadding)
)
}
}
},
) {
Scaffold(snackbarHost = {
SnackbarHost(
hostState = snackbarHostState
) { dataReceived ->
Snackbar(
modifier = Modifier,
snackbarData = dataReceived,
containerColor = MaterialTheme.colorScheme.inverseSurface,
contentColor = MaterialTheme.colorScheme.inverseOnSurface,
)
}
}
},
) {
Scaffold(snackbarHost = {
SnackbarHost(
hostState = snackbarHostState
) { dataReceived ->
Snackbar(
modifier = Modifier,
snackbarData = dataReceived,
containerColor = MaterialTheme.colorScheme.inverseSurface,
contentColor = MaterialTheme.colorScheme.inverseOnSurface,
)
}
}) {
NavHost(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center),
navController = navController,
startDestination = Route.MetadatorNavigator.route,
route = Route.MainHost.route,
) {
navigation(
startDestination = Route.MetadatorNavigator.Home.route,
route = Route.MetadatorNavigator.route
}) {
NavHost(
modifier = Modifier
.fillMaxWidth()
.align(Alignment.Center),
navController = navController,
startDestination = Route.MetadatorNavigator.route,
route = Route.MainHost.route,
) {
animatedComposable(Route.MetadatorNavigator.Home.route) {
HomePage(viewModel = mediaStoreViewModel)
navigation(
startDestination = Route.MetadatorNavigator.Home.route,
route = Route.MetadatorNavigator.route
) {
animatedComposable(Route.MetadatorNavigator.Home.route) {
HomePage(viewModel = mediaStoreViewModel)
}
}
}

navigation(
startDestination = Route.MediaplayerNavigator.Mediaplayer.route,
route = Route.MediaplayerNavigator.route
) {
animatedComposable(Route.MediaplayerNavigator.Mediaplayer.route) {
MediaplayerPage(mediaplayerViewModel, mediaPlayerSheetState)
navigation(
startDestination = Route.MediaplayerNavigator.Mediaplayer.route,
route = Route.MediaplayerNavigator.route
) {
animatedComposable(Route.MediaplayerNavigator.Mediaplayer.route) {
MediaplayerPage(mediaplayerViewModel, mediaPlayerSheetState)
}
}
}

navigation(
startDestination = Route.UtilitiesNavigator.TagEditor.route,
route = Route.UtilitiesNavigator.route
) {
slideInVerticallyComposable(
route = Route.UtilitiesNavigator.TagEditor.route,
arguments = listOf(navArgument(NavArgs.TagEditorSelectedSong.key) {
type = TagEditorParcelableSongParamType
})
navigation(
startDestination = Route.UtilitiesNavigator.TagEditor.route,
route = Route.UtilitiesNavigator.route
) {
val parcelableSongParcelable =
it.getParcelable<ParcelableSong>(NavArgs.TagEditorSelectedSong.key)
slideInVerticallyComposable(
route = Route.UtilitiesNavigator.TagEditor.route,
arguments = listOf(navArgument(NavArgs.TagEditorSelectedSong.key) {
type = TagEditorParcelableSongParamType
})
) {
val parcelableSongParcelable =
it.getParcelable<ParcelableSong>(NavArgs.TagEditorSelectedSong.key)

val viewModel = hiltViewModel<ID3MetadataEditorPageViewModel>()
val viewModel = hiltViewModel<ID3MetadataEditorPageViewModel>()

ID3MetadataEditorPage(
viewModel = viewModel,
parcelableSong = parcelableSongParcelable!!
)
ID3MetadataEditorPage(
viewModel = viewModel,
parcelableSong = parcelableSongParcelable!!
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.bobbyesp.metadator.presentation.common

import android.os.Build
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.material3.DrawerState
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.SnackbarHostState
Expand Down Expand Up @@ -50,6 +51,8 @@ val LocalDrawerState =
compositionLocalOf<DrawerState> { error("No Drawer State has been provided") }
val LocalMediaplayerConnection =
compositionLocalOf<ConnectionHandler> { error("No Media Player Service Connection handler has been provided") }
val LocalPlayerAwareWindowInsets =
compositionLocalOf<WindowInsets> { error("No WindowInsets provided") }

@OptIn(ExperimentalMaterialNavigationApi::class)
@Composable
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
Expand Down Expand Up @@ -162,7 +163,13 @@ private fun MediaplayerExpandedContent(
}

Surface(
modifier = modifier.fillMaxSize(),
modifier = modifier
.fillMaxSize()
.padding(
WindowInsets.systemBars
.only(WindowInsetsSides.Horizontal)
.asPaddingValues()
),
color = MaterialTheme.colorScheme.surfaceContainer,
) {
when (config.orientation) {
Expand Down Expand Up @@ -448,7 +455,6 @@ fun MiniplayerContent(
playingSong: MediaMetadata,
isPlaying: Boolean = false,
songProgress: Float = 0f,
imageModifier: Modifier = Modifier,
onPlayPause: () -> Unit = {}
) {
val transitionState = remember { MutableTransitionState(playingSong) }
Expand Down Expand Up @@ -479,7 +485,7 @@ fun MiniplayerContent(
horizontalArrangement = Arrangement.spacedBy(4.dp)
) {
ArtworkAsyncImage(
modifier = imageModifier
modifier = Modifier
.size(52.dp)
.clip(MaterialTheme.shapes.extraSmall),
artworkPath = songCardArtworkUri
Expand Down
Loading

0 comments on commit 1503669

Please sign in to comment.