diff --git a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/player/PlayerScreen.kt b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/player/PlayerScreen.kt index 2dbc177..d03b3a8 100644 --- a/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/player/PlayerScreen.kt +++ b/apps/vbox/client/shared/src/commonMain/kotlin/ru/shadowsparky/vbox/shared/presentation/player/PlayerScreen.kt @@ -59,7 +59,8 @@ fun PlayerScreen(component: PlayerComponent) { Box(modifier = Modifier.fillMaxSize()) { VideoPlayer( component.playerController, - Modifier.fillMaxSize() + Modifier.fillMaxSize(), + onBack = { component.onBack() } ) PlayerActionOverlay( effect is PlayerEffect.PlayNextEpisode, diff --git a/feature/video-player/src/commonMain/kotlin/ru/shadowsparky/videoplayer/ui/DefaultControls.kt b/feature/video-player/src/commonMain/kotlin/ru/shadowsparky/videoplayer/ui/DefaultControls.kt index 1a3d039..02b4ebd 100644 --- a/feature/video-player/src/commonMain/kotlin/ru/shadowsparky/videoplayer/ui/DefaultControls.kt +++ b/feature/video-player/src/commonMain/kotlin/ru/shadowsparky/videoplayer/ui/DefaultControls.kt @@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.safeDrawingPadding import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape @@ -58,6 +59,7 @@ import androidx.compose.ui.unit.dp import kotlinx.coroutines.delay import kotlinx.coroutines.launch import org.jetbrains.compose.resources.painterResource +import ru.shadowsparky.ui.components.BackButton import ru.shadowsparky.ui.components.IconButton import ru.shadowsparky.ui.components.isTv import ru.shadowsparky.ui.theme.focusBorder @@ -74,7 +76,8 @@ fun DefaultControls( state: PlayerState, controlsState: ControlsState, controller: PlayerController, - modifier: Modifier = Modifier + modifier: Modifier = Modifier, + onBack: () -> Unit ) { val isTv = isTv() val scope = rememberCoroutineScope() @@ -93,7 +96,11 @@ fun DefaultControls( modifier = modifier .fillMaxSize() .background(if (isUiLocked.value) Color.Transparent else Color.Black.copy(alpha = 0.4f)) + .safeDrawingPadding() ) { + Box(modifier = Modifier.align(Alignment.TopStart).padding(8.dp)) { + BackButton { onBack() } + } SideDoubleTapSeekZones(controller) Row(modifier = Modifier.align(Alignment.TopEnd).focusGroup().padding(8.dp)) { if (audioTracksState.isNotEmpty() && !isUiLocked.value) { diff --git a/feature/video-player/src/commonMain/kotlin/ru/shadowsparky/videoplayer/ui/VideoPlayer.kt b/feature/video-player/src/commonMain/kotlin/ru/shadowsparky/videoplayer/ui/VideoPlayer.kt index a6897fe..2c6147f 100644 --- a/feature/video-player/src/commonMain/kotlin/ru/shadowsparky/videoplayer/ui/VideoPlayer.kt +++ b/feature/video-player/src/commonMain/kotlin/ru/shadowsparky/videoplayer/ui/VideoPlayer.kt @@ -45,13 +45,20 @@ import ru.shadowsparky.videoplayer.ControlsState import ru.shadowsparky.videoplayer.PlayerController import ru.shadowsparky.videoplayer.PlayerEvent import ru.shadowsparky.videoplayer.PlayerState +import kotlin.time.Duration.Companion.seconds @Composable fun VideoPlayer( controller: PlayerController, modifier: Modifier = Modifier, + onBack: () -> Unit, overlayContent: @Composable (PlayerState, ControlsState) -> Unit = { player, controls -> - DefaultControls(player, controls, controller = controller) + DefaultControls( + player, + controls, + controller = controller, + onBack = onBack + ) } ) { val isTv = isTv() @@ -86,7 +93,7 @@ fun VideoPlayer( LaunchedEffect(areControlsVisible, state.isPlaying, lastErrorMessage, interactionCount) { if (areControlsVisible && state.isPlaying && lastErrorMessage == null) { - delay(5000) + delay(5.seconds) areControlsVisible = false } } diff --git a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/AppScaffold.kt b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/AppScaffold.kt index 547068b..d498d6b 100644 --- a/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/AppScaffold.kt +++ b/libs/ui/src/commonMain/kotlin/ru/shadowsparky/ui/AppScaffold.kt @@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.windowInsetsPadding -import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi @@ -49,7 +48,6 @@ import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.unit.dp import ru.shadowsparky.ui.nav.Route -import ru.shadowsparky.ui.theme.focusBorder @Immutable data class NavigationEntry( @@ -159,6 +157,7 @@ private fun DynamicNavigationScaffold( ) { val entries = navigationEntries.orEmpty() val railState = rememberWideNavigationRailState(WideNavigationRailValue.Collapsed) + val isExpanded = railState.targetValue == WideNavigationRailValue.Expanded var railHasFocus by remember { mutableStateOf(false) } LaunchedEffect(railHasFocus) { if (railHasFocus) railState.expand() else railState.collapse() @@ -168,26 +167,17 @@ private fun DynamicNavigationScaffold( navigationSuite = { WideNavigationRail( state = railState, - modifier = Modifier.onFocusChanged { railHasFocus = it.hasFocus } - .focusGroup(), + modifier = Modifier.onFocusChanged { railHasFocus = it.hasFocus }.focusGroup() ) { entries.forEach { entry -> WideNavigationRailItem( - railExpanded = - railState.targetValue == WideNavigationRailValue.Expanded, + railExpanded = isExpanded, selected = currentRoute == entry.route, - onClick = { - onSelectNavigation?.invoke(entry.route) - }, - icon = { - Icon(entry.painter, entry.title) - }, + onClick = { onSelectNavigation?.invoke(entry.route) }, + icon = { Icon(entry.painter, entry.title) }, label = { - if (railState.currentValue == WideNavigationRailValue.Expanded) { - Text(entry.title) - } - }, - modifier = Modifier.focusBorder(false, CircleShape) + if (isExpanded) Text(entry.title) + } ) } }