From 54c895ff0ffec1d1d148d3f38205e6a2fe011f7f Mon Sep 17 00:00:00 2001 From: Barnabas Balogh Date: Fri, 19 Jun 2026 12:00:16 +0000 Subject: [PATCH] feat(player): track skippable segment type to gate intro and outro UI Expose the active skippable segment type from the player state so the UI can show the skip intro button only for intro segments and trigger the next episode overlay when an outro segment is active. --- .../hu/bbara/purefin/ui/screen/player/TvPlayerScreen.kt | 4 +++- .../java/hu/bbara/purefin/ui/screen/player/PlayerScreen.kt | 7 ++++--- .../hu/bbara/purefin/core/player/model/PlayerUiModels.kt | 3 +++ .../bbara/purefin/core/player/viewmodel/PlayerViewModel.kt | 5 ++++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/app-tv/src/main/java/hu/bbara/purefin/ui/screen/player/TvPlayerScreen.kt b/app-tv/src/main/java/hu/bbara/purefin/ui/screen/player/TvPlayerScreen.kt index 97267cdc..28a51638 100644 --- a/app-tv/src/main/java/hu/bbara/purefin/ui/screen/player/TvPlayerScreen.kt +++ b/app-tv/src/main/java/hu/bbara/purefin/ui/screen/player/TvPlayerScreen.kt @@ -62,6 +62,7 @@ import androidx.media3.ui.PlayerView import androidx.media3.ui.SubtitleView import hu.bbara.purefin.core.player.model.TimedMarker import hu.bbara.purefin.core.player.viewmodel.PlayerViewModel +import hu.bbara.purefin.model.SegmentType import hu.bbara.purefin.ui.common.visual.ValueChangeTimedVisibility import hu.bbara.purefin.ui.screen.player.components.PlayerSeekBarTrack import hu.bbara.purefin.ui.screen.player.components.TvIconButton @@ -179,11 +180,12 @@ fun TvPlayerScreen( val showSkipIntroButton = !controlsVisible && uiState.activeSkippableSegmentEndMs != null + && uiState.activeSkippableSegmentType == SegmentType.INTRO && !uiState.isEnded val showNextEpisodeOverlay = !controlsVisible && uiState.nextEpisode != null && uiState.durationMs > 0L - && (uiState.durationMs - uiState.positionMs) <= 60_000L + && ((uiState.durationMs - uiState.positionMs) <= 30_000L || uiState.activeSkippableSegmentType == SegmentType.OUTRO) && !uiState.isEnded LaunchedEffect(showSkipIntroButton, showNextEpisodeOverlay) { rootFocusRequester = when { diff --git a/app/src/main/java/hu/bbara/purefin/ui/screen/player/PlayerScreen.kt b/app/src/main/java/hu/bbara/purefin/ui/screen/player/PlayerScreen.kt index db978a39..2bc2eee0 100644 --- a/app/src/main/java/hu/bbara/purefin/ui/screen/player/PlayerScreen.kt +++ b/app/src/main/java/hu/bbara/purefin/ui/screen/player/PlayerScreen.kt @@ -43,6 +43,7 @@ import androidx.media3.ui.PlayerView import androidx.media3.ui.SubtitleView import hu.bbara.purefin.core.player.model.TimedMarker import hu.bbara.purefin.core.player.viewmodel.PlayerViewModel +import hu.bbara.purefin.model.SegmentType import hu.bbara.purefin.ui.common.visual.EmptyValueTimedVisibility import hu.bbara.purefin.ui.common.visual.ValueChangeTimedVisibility import hu.bbara.purefin.ui.screen.player.components.NextEpisodeOverlay @@ -100,11 +101,11 @@ fun PlayerScreen( } - val showSkipIntroButton = !controlsVisible && uiState.activeSkippableSegmentEndMs != null + val showSkipIntroButton = !controlsVisible && uiState.activeSkippableSegmentEndMs != null && uiState.activeSkippableSegmentType == SegmentType.INTRO val showNextEpisodeOverlay = !controlsVisible && uiState.nextEpisode != null && uiState.durationMs > 0L - && (uiState.durationMs - uiState.positionMs) <= 60_000L + && ((uiState.durationMs - uiState.positionMs) <= 60_000L || uiState.activeSkippableSegmentType == SegmentType.OUTRO) && !uiState.isEnded @@ -301,7 +302,7 @@ fun PlayerScreen( .align(Alignment.BottomEnd) .padding( end = 24.dp, - bottom = 120.dp + bottom = 24.dp ) ) { uiState.nextEpisode?.let { nextEpisode -> diff --git a/core/src/main/java/hu/bbara/purefin/core/player/model/PlayerUiModels.kt b/core/src/main/java/hu/bbara/purefin/core/player/model/PlayerUiModels.kt index 48785522..4794521c 100644 --- a/core/src/main/java/hu/bbara/purefin/core/player/model/PlayerUiModels.kt +++ b/core/src/main/java/hu/bbara/purefin/core/player/model/PlayerUiModels.kt @@ -1,5 +1,7 @@ package hu.bbara.purefin.core.player.model +import hu.bbara.purefin.model.SegmentType + data class PlayerUiState( val isPlaying: Boolean = false, val isBuffering: Boolean = false, @@ -11,6 +13,7 @@ data class PlayerUiState( val positionMs: Long = 0L, val bufferedMs: Long = 0L, val activeSkippableSegmentEndMs: Long? = null, + val activeSkippableSegmentType: SegmentType? = null, val error: String? = null, val playbackSpeed: Float = 1f, val chapters: List = emptyList(), diff --git a/core/src/main/java/hu/bbara/purefin/core/player/viewmodel/PlayerViewModel.kt b/core/src/main/java/hu/bbara/purefin/core/player/viewmodel/PlayerViewModel.kt index 5ab8f258..618b990c 100644 --- a/core/src/main/java/hu/bbara/purefin/core/player/viewmodel/PlayerViewModel.kt +++ b/core/src/main/java/hu/bbara/purefin/core/player/viewmodel/PlayerViewModel.kt @@ -117,7 +117,10 @@ class PlayerViewModel @Inject constructor( viewModelScope.launch { playerManager.activeSkippableSegment.collect { mediaSegment -> _uiState.update { - it.copy(activeSkippableSegmentEndMs = mediaSegment?.endMs) + it.copy( + activeSkippableSegmentEndMs = mediaSegment?.endMs, + activeSkippableSegmentType = mediaSegment?.type + ) } } }