add back button to player

This commit is contained in:
2026-08-15 17:21:07 +03:00
parent b8234253d3
commit 3e980840b4
4 changed files with 26 additions and 21 deletions
@@ -59,7 +59,8 @@ fun PlayerScreen(component: PlayerComponent) {
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
VideoPlayer( VideoPlayer(
component.playerController, component.playerController,
Modifier.fillMaxSize() Modifier.fillMaxSize(),
onBack = { component.onBack() }
) )
PlayerActionOverlay( PlayerActionOverlay(
effect is PlayerEffect.PlayNextEpisode, effect is PlayerEffect.PlayNextEpisode,
@@ -21,6 +21,7 @@ import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
@@ -58,6 +59,7 @@ import androidx.compose.ui.unit.dp
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.painterResource import org.jetbrains.compose.resources.painterResource
import ru.shadowsparky.ui.components.BackButton
import ru.shadowsparky.ui.components.IconButton import ru.shadowsparky.ui.components.IconButton
import ru.shadowsparky.ui.components.isTv import ru.shadowsparky.ui.components.isTv
import ru.shadowsparky.ui.theme.focusBorder import ru.shadowsparky.ui.theme.focusBorder
@@ -74,7 +76,8 @@ fun DefaultControls(
state: PlayerState, state: PlayerState,
controlsState: ControlsState, controlsState: ControlsState,
controller: PlayerController, controller: PlayerController,
modifier: Modifier = Modifier modifier: Modifier = Modifier,
onBack: () -> Unit
) { ) {
val isTv = isTv() val isTv = isTv()
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
@@ -93,7 +96,11 @@ fun DefaultControls(
modifier = modifier modifier = modifier
.fillMaxSize() .fillMaxSize()
.background(if (isUiLocked.value) Color.Transparent else Color.Black.copy(alpha = 0.4f)) .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) SideDoubleTapSeekZones(controller)
Row(modifier = Modifier.align(Alignment.TopEnd).focusGroup().padding(8.dp)) { Row(modifier = Modifier.align(Alignment.TopEnd).focusGroup().padding(8.dp)) {
if (audioTracksState.isNotEmpty() && !isUiLocked.value) { if (audioTracksState.isNotEmpty() && !isUiLocked.value) {
@@ -45,13 +45,20 @@ import ru.shadowsparky.videoplayer.ControlsState
import ru.shadowsparky.videoplayer.PlayerController import ru.shadowsparky.videoplayer.PlayerController
import ru.shadowsparky.videoplayer.PlayerEvent import ru.shadowsparky.videoplayer.PlayerEvent
import ru.shadowsparky.videoplayer.PlayerState import ru.shadowsparky.videoplayer.PlayerState
import kotlin.time.Duration.Companion.seconds
@Composable @Composable
fun VideoPlayer( fun VideoPlayer(
controller: PlayerController, controller: PlayerController,
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onBack: () -> Unit,
overlayContent: @Composable (PlayerState, ControlsState) -> Unit = { player, controls -> overlayContent: @Composable (PlayerState, ControlsState) -> Unit = { player, controls ->
DefaultControls(player, controls, controller = controller) DefaultControls(
player,
controls,
controller = controller,
onBack = onBack
)
} }
) { ) {
val isTv = isTv() val isTv = isTv()
@@ -86,7 +93,7 @@ fun VideoPlayer(
LaunchedEffect(areControlsVisible, state.isPlaying, lastErrorMessage, interactionCount) { LaunchedEffect(areControlsVisible, state.isPlaying, lastErrorMessage, interactionCount) {
if (areControlsVisible && state.isPlaying && lastErrorMessage == null) { if (areControlsVisible && state.isPlaying && lastErrorMessage == null) {
delay(5000) delay(5.seconds)
areControlsVisible = false areControlsVisible = false
} }
} }
@@ -9,7 +9,6 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi 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.input.nestedscroll.nestedScroll
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import ru.shadowsparky.ui.nav.Route import ru.shadowsparky.ui.nav.Route
import ru.shadowsparky.ui.theme.focusBorder
@Immutable @Immutable
data class NavigationEntry<out T : Route>( data class NavigationEntry<out T : Route>(
@@ -159,6 +157,7 @@ private fun <T : Route> DynamicNavigationScaffold(
) { ) {
val entries = navigationEntries.orEmpty() val entries = navigationEntries.orEmpty()
val railState = rememberWideNavigationRailState(WideNavigationRailValue.Collapsed) val railState = rememberWideNavigationRailState(WideNavigationRailValue.Collapsed)
val isExpanded = railState.targetValue == WideNavigationRailValue.Expanded
var railHasFocus by remember { mutableStateOf(false) } var railHasFocus by remember { mutableStateOf(false) }
LaunchedEffect(railHasFocus) { LaunchedEffect(railHasFocus) {
if (railHasFocus) railState.expand() else railState.collapse() if (railHasFocus) railState.expand() else railState.collapse()
@@ -168,26 +167,17 @@ private fun <T : Route> DynamicNavigationScaffold(
navigationSuite = { navigationSuite = {
WideNavigationRail( WideNavigationRail(
state = railState, state = railState,
modifier = Modifier.onFocusChanged { railHasFocus = it.hasFocus } modifier = Modifier.onFocusChanged { railHasFocus = it.hasFocus }.focusGroup()
.focusGroup(),
) { ) {
entries.forEach { entry -> entries.forEach { entry ->
WideNavigationRailItem( WideNavigationRailItem(
railExpanded = railExpanded = isExpanded,
railState.targetValue == WideNavigationRailValue.Expanded,
selected = currentRoute == entry.route, selected = currentRoute == entry.route,
onClick = { onClick = { onSelectNavigation?.invoke(entry.route) },
onSelectNavigation?.invoke(entry.route) icon = { Icon(entry.painter, entry.title) },
},
icon = {
Icon(entry.painter, entry.title)
},
label = { label = {
if (railState.currentValue == WideNavigationRailValue.Expanded) { if (isExpanded) Text(entry.title)
Text(entry.title) }
}
},
modifier = Modifier.focusBorder(false, CircleShape)
) )
} }
} }