diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarHomeScreen.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarHomeScreen.kt index 16efc54..5ad6543 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarHomeScreen.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarHomeScreen.kt @@ -275,31 +275,15 @@ private fun DockedPlayer(now: NowPlaying, modifier: Modifier) { // Sized from the column's width rather than fixed: fixed sizes filled a narrow // column edge to edge, so the buttons sat squeezed against each other. Each row // keeps a clear gap between buttons, and none grows past a comfortable size. + // Like, shuffle, repeat and lyrics are in the nav bar's pill while the player is + // here (NavNowPlaying), which leaves the cover more room. BoxWithConstraints(Modifier.fillMaxWidth()) { - val w = maxWidth // Two skips, play at 1.25 times a skip, and two gaps of at least 20dp. - val skip = ((w - 40.dp) / 3.25f).coerceIn(36.dp, 48.dp) - // Four buttons and three gaps of at least 12dp. - val mode = ((w - 36.dp) / 4).coerceIn(34.dp, 42.dp) - Column(Modifier.fillMaxWidth(), horizontalAlignment = Alignment.CenterHorizontally) { - TransportRow(now, media, skip = skip, modifier = Modifier.fillMaxWidth()) - Spacer(Modifier.height(10.dp)) - PlayModeRow(now, media, size = mode, modifier = Modifier.fillMaxWidth()) { - GsIconButton( - ImageVector.vectorResource(R.drawable.lyrics_24), stringResource(R.string.storage_lyrics), navigator::showSideLyrics, - size = mode, iconSize = mode * 0.52f, - ) - } - } + val skip = ((maxWidth - 40.dp) / 3.25f).coerceIn(36.dp, 48.dp) + TransportRow(now, media, skip = skip, modifier = Modifier.fillMaxWidth().padding(bottom = 4.dp)) } } - // Round like the player's other buttons, and tucked into the card's corner so it - // covers as little of the cover as it can. - GsIconButton( - Icons.Filled.Close, stringResource(R.string.home_back_nav), navigator::closeSidePanel, - Modifier.align(Alignment.TopEnd).offset(x = 6.dp, y = (-6).dp), size = 32.dp, iconSize = 18.dp, - shape = CircleShape, - ) + // No close button here: the nav bar's dock button closes the player (NavNowPlaying). } } } diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarNavBar.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarNavBar.kt index 66346fe..75c1645 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarNavBar.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/CarNavBar.kt @@ -556,8 +556,13 @@ private fun NavNowPlaying(navigator: CarNavigator, selected: Boolean, modifier: val phase by media.phase.collectAsState() val now by media.now.collectAsState() if (phase != CarMedia.Phase.READY || !now.isActive) return - // Docked beside the map, the player is already on screen - the pill steps aside for it. - if (navigator.sidePanel == SidePanel.CONTROLS && navigator.current == CarScreen.Home) return + // Docked beside the map, the player is already on screen, so the pill carries what the + // player leaves out - like, shuffle, repeat and lyrics - and its dock button, filled in, in + // the same place: tapped again, it closes the player. + if (navigator.sidePanel == SidePanel.CONTROLS && navigator.current == CarScreen.Home) { + DockedPill(navigator, now, selected, modifier) + return + } // The media screen is the full player already; the pill would only repeat its controls. if (navigator.current == CarScreen.Media) return val context = LocalContext.current @@ -670,6 +675,42 @@ private fun NavNowPlaying(navigator: CarNavigator, selected: Boolean, modifier: } } +/** + * The pill while the player is docked: like, shuffle, repeat and lyrics, then the dock button, + * shown open, which closes it. Sits on the player's side of the bar, under the player. + */ +@Composable +private fun DockedPill(navigator: CarNavigator, now: app.seb3thehacker.gearslip.media.NowPlaying, selected: Boolean, modifier: Modifier) { + val scheme = MaterialTheme.colorScheme + val playerLeft by CarSettings.playerLeft.collectAsState() + Box(modifier.fillMaxWidth(), contentAlignment = if (playerLeft) Alignment.CenterStart else Alignment.CenterEnd) { + Surface( + modifier = Modifier.height(44.dp), + shape = RoundedCornerShape(14.dp), + color = if (selected) scheme.primaryContainer else scheme.secondaryContainer, + contentColor = if (selected) scheme.onPrimaryContainer else scheme.onSecondaryContainer, + ) { + PlayModeRow( + now, CarServices.media, size = 36.dp, + modifier = Modifier.fillMaxHeight().padding(horizontal = 4.dp), + arrangement = Arrangement.spacedBy(6.dp), + ) { + GsIconButton( + androidx.compose.ui.graphics.vector.ImageVector.vectorResource(R.drawable.lyrics_24), + stringResource(R.string.storage_lyrics), navigator::showSideLyrics, + size = 36.dp, iconSize = 20.dp, + ) + GsIconButton( + if (playerLeft) MediaIcons.DockLeftOpen else MediaIcons.DockRightOpen, + stringResource(R.string.navbar_close_panel), + navigator::togglePlayerDock, + size = 36.dp, iconSize = 22.dp, + ) + } + } + } +} + /** * A filled lightning bolt, next to the battery percentage instead of the word "charging" - the * core Material icon set this project ships (no extended pack, to keep the app small) has no diff --git a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/PlayerParts.kt b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/PlayerParts.kt index e0627d5..61fe9f5 100644 --- a/gearslip/src/main/java/app/seb3thehacker/gearslip/car/PlayerParts.kt +++ b/gearslip/src/main/java/app/seb3thehacker/gearslip/car/PlayerParts.kt @@ -130,6 +130,7 @@ internal fun PlayModeRow( size: Dp, modifier: Modifier = Modifier, maxOther: Int = 0, + arrangement: Arrangement.Horizontal = Arrangement.SpaceEvenly, trailing: @Composable RowScope.() -> Unit = {}, ) { val modes by media.modes.collectAsState() @@ -139,7 +140,7 @@ internal fun PlayModeRow( val shuffleOn = shuffleKnown && modes.shuffle != PlaybackStateCompat.SHUFFLE_MODE_NONE val repeatOn = repeatKnown && modes.repeat != PlaybackStateCompat.REPEAT_MODE_NONE val icon = size * 0.52f - Row(modifier, horizontalArrangement = Arrangement.SpaceEvenly, verticalAlignment = Alignment.CenterVertically) { + Row(modifier, horizontalArrangement = arrangement, verticalAlignment = Alignment.CenterVertically) { // An app's own like, shuffle or repeat button shows whether it's on only through its icon, // so that icon is drawn, as Android Auto does, and lit when its name says it's on. // Gearslip's own stand in for apps that report a heart rating or shuffle and repeat modes.