diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2ba0d5df..e3947311 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -26,8 +26,8 @@ android { applicationId = "dev.typetype.android" minSdk = 23 targetSdk = 37 - versionCode = 10707 - versionName = "1.7.0-beta.8" + versionCode = 10708 + versionName = "1.7.0-beta.9" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" resValue("string", "app_name", "TypeType") } diff --git a/app/src/main/java/dev/typetype/android/AppNavigation.kt b/app/src/main/java/dev/typetype/android/AppNavigation.kt index fa17289a..934de63e 100644 --- a/app/src/main/java/dev/typetype/android/AppNavigation.kt +++ b/app/src/main/java/dev/typetype/android/AppNavigation.kt @@ -142,8 +142,7 @@ internal fun AppTopBar( @Composable internal fun AppBottomBar( - currentDestination: NavDestination?, - fallbackTabRouteQualifiedName: String?, + selectedTabRouteQualifiedName: String?, onTabClick: (TopLevelTab) -> Unit, tabs: List = topLevelTabs, modifier: Modifier = Modifier, @@ -155,10 +154,10 @@ internal fun AppBottomBar( containerColor = MaterialTheme.colorScheme.surface, ) { tabs.forEach { tab: TopLevelTab -> - val selected = tab.isSelected(currentDestination, fallbackTabRouteQualifiedName, tabs) + val selected = tab.route::class.qualifiedName == selectedTabRouteQualifiedName NavigationBarItem( selected = selected, - onClick = { if (!currentDestination.matchesRoute(tab.route)) onTabClick(tab) }, + onClick = { onTabClick(tab) }, icon = { Icon(painterResource(tab.iconRes), contentDescription = null) }, label = { Text(stringResource(tab.labelRes)) }, ) @@ -169,8 +168,7 @@ internal fun AppBottomBar( @Composable internal fun AppNavigationRail( - currentDestination: NavDestination?, - fallbackTabRouteQualifiedName: String?, + selectedTabRouteQualifiedName: String?, onTabClick: (TopLevelTab) -> Unit, tabs: List = topLevelTabs, modifier: Modifier = Modifier, @@ -184,10 +182,10 @@ internal fun AppNavigationRail( ) { Spacer(Modifier.weight(1f)) tabs.forEach { tab: TopLevelTab -> - val selected = tab.isSelected(currentDestination, fallbackTabRouteQualifiedName, tabs) + val selected = tab.route::class.qualifiedName == selectedTabRouteQualifiedName NavigationRailItem( selected = selected, - onClick = { if (!currentDestination.matchesRoute(tab.route)) onTabClick(tab) }, + onClick = { onTabClick(tab) }, icon = { Icon(painterResource(tab.iconRes), contentDescription = null) }, label = { Text(stringResource(tab.labelRes)) }, ) @@ -220,16 +218,6 @@ private fun ProfileAvatarButton( } } -private fun TopLevelTab.isSelected( - destination: NavDestination?, - fallbackRouteName: String?, - tabs: List, -): Boolean { - val direct = destination.matchesRoute(route) - val anyDirect = tabs.any { destination.matchesRoute(it.route) } - return direct || (!anyDirect && route::class.qualifiedName == fallbackRouteName) -} - internal fun NavDestination?.matchesRoute(route: Any): Boolean { val destination = this ?: return false return when (route) { diff --git a/app/src/main/java/dev/typetype/android/AppShell.kt b/app/src/main/java/dev/typetype/android/AppShell.kt index 5c31247a..29e1ce4d 100644 --- a/app/src/main/java/dev/typetype/android/AppShell.kt +++ b/app/src/main/java/dev/typetype/android/AppShell.kt @@ -90,7 +90,7 @@ fun AppShell( currentDestination?.hasRoute() == true || currentDestination?.hasRoute() == true ) - var activeTabRoute by rememberSaveable { mutableStateOf(null) } + var selectedTabRoute by rememberSaveable { mutableStateOf(null) } var isPlayerFullscreen by remember { mutableStateOf(false) } var playerTransitionProgress by remember { mutableFloatStateOf(0f) } val playerHostState by playerHostController.state.collectAsStateWithLifecycle() @@ -103,9 +103,13 @@ fun AppShell( ) LaunchedEffect(currentDestination) { topLevelTabs.firstOrNull { currentDestination.matchesRoute(it.route) }?.let { - activeTabRoute = it.route::class.qualifiedName + selectedTabRoute = it.route::class.qualifiedName } } + val selectedTabRouteQualifiedName = selectedTabRoute + ?: topLevelTabs.firstOrNull { currentDestination.matchesRoute(it.route) } + ?.route + ?.let { it::class.qualifiedName } val mediaController = rememberMediaController().value val snackbarHostState = remember { SnackbarHostState() } @@ -119,10 +123,10 @@ fun AppShell( Row(modifier = Modifier.fillMaxSize()) { if (usesNavigationRail && showsNavigation && appChromeVisible) { AppNavigationRail( - currentDestination = currentDestination, - fallbackTabRouteQualifiedName = activeTabRoute, + selectedTabRouteQualifiedName = selectedTabRouteQualifiedName, onTabClick = { tab: TopLevelTab -> - navController.navigateTopLevel(tab.route, activeTabRoute) + selectedTabRoute = tab.route::class.qualifiedName + navController.navigateTopLevel(tab.route, selectedTabRoute) }, tabs = navigationTabs, ) @@ -163,10 +167,10 @@ fun AppShell( bottomBar = { if (!usesNavigationRail && showsNavigation) { AppBottomBar( - currentDestination = currentDestination, - fallbackTabRouteQualifiedName = activeTabRoute, + selectedTabRouteQualifiedName = selectedTabRouteQualifiedName, onTabClick = { tab: TopLevelTab -> - navController.navigateTopLevel(tab.route, activeTabRoute) + selectedTabRoute = tab.route::class.qualifiedName + navController.navigateTopLevel(tab.route, selectedTabRoute) }, tabs = navigationTabs, modifier = Modifier.playerChrome(phoneChromeAlpha), diff --git a/app/src/main/java/dev/typetype/android/feature/player/components/PlayerControls.kt b/app/src/main/java/dev/typetype/android/feature/player/components/PlayerControls.kt index 123a7b55..9ae73da0 100644 --- a/app/src/main/java/dev/typetype/android/feature/player/components/PlayerControls.kt +++ b/app/src/main/java/dev/typetype/android/feature/player/components/PlayerControls.kt @@ -8,7 +8,7 @@ import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.navigationBarsIgnoringVisibility +import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.statusBars @@ -56,6 +56,7 @@ fun PlayerControls( ) BottomScrim( compact = compactControls, + isFullscreen = isFullscreen, modifier = Modifier.align(Alignment.BottomCenter), ) PlayerTopBar( @@ -99,11 +100,17 @@ fun PlayerControls( .align(Alignment.BottomCenter) .fillMaxWidth() .testTag(PLAYER_BOTTOM_CONTROLS_TAG) - .windowInsetsPadding(WindowInsets.navigationBarsIgnoringVisibility) + .then( + if (isFullscreen) { + Modifier + } else { + Modifier.windowInsetsPadding(WindowInsets.navigationBars) + }, + ) .padding( start = if (isFullscreen) 12.dp else 4.dp, end = if (isFullscreen) 8.dp else 4.dp, - bottom = if (isFullscreen) 12.dp else 0.dp, + bottom = if (isFullscreen) 6.dp else 0.dp, ), ) } @@ -124,11 +131,21 @@ private fun TopScrim(compact: Boolean, modifier: Modifier = Modifier) { } @Composable -private fun BottomScrim(compact: Boolean, modifier: Modifier = Modifier) { +private fun BottomScrim( + compact: Boolean, + isFullscreen: Boolean, + modifier: Modifier = Modifier, +) { Box( modifier = modifier .fillMaxWidth() - .height(if (compact) 88.dp else 152.dp) + .height( + when { + isFullscreen -> 116.dp + compact -> 88.dp + else -> 152.dp + }, + ) .background( Brush.verticalGradient( colors = listOf(Color.Transparent, Color.Black.copy(alpha = 0.72f)), @@ -157,7 +174,7 @@ private fun BottomBar( }, ) .padding(start = if (isFullscreen) 8.dp else 2.dp, end = 2.dp), - verticalAlignment = Alignment.CenterVertically, + verticalAlignment = Alignment.Bottom, ) { PlayerTimeBar( player = player, diff --git a/app/src/main/java/dev/typetype/android/feature/player/components/PlayerSurfaceBox.kt b/app/src/main/java/dev/typetype/android/feature/player/components/PlayerSurfaceBox.kt index 3811d3bb..bf9e6f79 100644 --- a/app/src/main/java/dev/typetype/android/feature/player/components/PlayerSurfaceBox.kt +++ b/app/src/main/java/dev/typetype/android/feature/player/components/PlayerSurfaceBox.kt @@ -127,8 +127,15 @@ internal fun PlayerSurfaceBox( LaunchedEffect(accessibleControls) { if (accessibleControls) controlsVisible = true } - LaunchedEffect(controlsVisible, playbackStatus.isPlaying, accessibleControls) { + val isSeekDragging = gestureState.dragMode.value == DragMode.Seek + LaunchedEffect( + controlsVisible, + playbackStatus.isPlaying, + accessibleControls, + isSeekDragging, + ) { if (controlsVisible && playbackStatus.isPlaying && !accessibleControls) { + if (isSeekDragging) return@LaunchedEffect delay(AUTO_HIDE_DELAY_MS) controlsVisible = false }