From 79f8719ac3ec91a97451ac212ee36eefb8e96093 Mon Sep 17 00:00:00 2001 From: Christian Schabesberger Date: Fri, 6 Sep 2024 13:58:52 +0200 Subject: [PATCH] highlight active chapter --- .../newplayer/model/VideoPlayerViewModel.kt | 5 +- .../model/VideoPlayerViewModelImpl.kt | 9 +- .../model/ViewoPlayerViewModelDummy.kt | 2 +- .../ui/videoplayer/StreamSelectUI.kt | 10 ++- .../videoplayer/streamselect/ChapterItem.kt | 86 +++++++++++++------ .../streamselect/StreamSelectTopBar.kt | 5 +- 6 files changed, 78 insertions(+), 39 deletions(-) diff --git a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt index b4c81f8..dea75bc 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModel.kt @@ -21,14 +21,11 @@ package net.newpipe.newplayer.model import android.os.Bundle -import androidx.media3.common.Player import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.StateFlow import net.newpipe.newplayer.Chapter import net.newpipe.newplayer.NewPlayer -import net.newpipe.newplayer.RepeatMode import net.newpipe.newplayer.ui.ContentScale -import net.newpipe.newplayer.utils.Thumbnail interface VideoPlayerViewModel { @@ -61,7 +58,7 @@ interface VideoPlayerViewModel { fun closeStreamSelection() fun chapterSelected(chapter: Chapter) fun streamSelected(streamId: Int) - fun cycleRepeatmode() + fun cycleRepeatMode() fun toggleShuffle() fun onStorePlaylist() fun movePlaylistItem(from: Int, to: Int) diff --git a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModelImpl.kt b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModelImpl.kt index 9ef945b..a4ca9f2 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModelImpl.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/model/VideoPlayerViewModelImpl.kt @@ -419,7 +419,6 @@ class VideoPlayerViewModelImpl @Inject constructor( } override fun openStreamSelection(selectChapter: Boolean, embeddedUiConfig: EmbeddedUiConfig) { - resetPlaylistProgressUpdaterJob() uiVisibilityJob?.cancel() if (!uiState.value.uiMode.fullscreen) { this.embeddedUiConfig = embeddedUiConfig @@ -428,10 +427,16 @@ class VideoPlayerViewModelImpl @Inject constructor( if (selectChapter) uiState.value.uiMode.getChapterSelectUiState() else uiState.value.uiMode.getStreamSelectUiState() ) + if(selectChapter) { + resetProgressUpdatePeriodicallyJob() + } else { + resetPlaylistProgressUpdaterJob() + } } override fun closeStreamSelection() { playlistProgressUpdatrJob?.cancel() + progressUpdaterJob?.cancel() updateUiMode(uiState.value.uiMode.getUiHiddenState()) } @@ -466,7 +471,7 @@ class VideoPlayerViewModelImpl @Inject constructor( println("stream selected: $streamId") } - override fun cycleRepeatmode() { + override fun cycleRepeatMode() { newPlayer?.let { it.repeatMode = when (it.repeatMode) { RepeatMode.DONT_REPEAT -> RepeatMode.REPEAT_ALL diff --git a/new-player/src/main/java/net/newpipe/newplayer/model/ViewoPlayerViewModelDummy.kt b/new-player/src/main/java/net/newpipe/newplayer/model/ViewoPlayerViewModelDummy.kt index 8dc7072..fe4cb61 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/model/ViewoPlayerViewModelDummy.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/model/ViewoPlayerViewModelDummy.kt @@ -92,7 +92,7 @@ open class VideoPlayerViewModelDummy : VideoPlayerViewModel { println("dummy impl stream selected: $streamId") } - override fun cycleRepeatmode() { + override fun cycleRepeatMode() { println("dummy impl") } diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/StreamSelectUI.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/StreamSelectUI.kt index 1f43244..cf2dbe1 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/StreamSelectUI.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/StreamSelectUI.kt @@ -48,6 +48,7 @@ import net.newpipe.newplayer.ui.videoplayer.streamselect.ChapterItem import net.newpipe.newplayer.ui.videoplayer.streamselect.ChapterSelectTopBar import net.newpipe.newplayer.ui.videoplayer.streamselect.StreamItem import net.newpipe.newplayer.ui.videoplayer.streamselect.StreamSelectTopBar +import net.newpipe.newplayer.ui.videoplayer.streamselect.isActiveChapter import net.newpipe.newplayer.utils.ReorderHapticFeedbackType import net.newpipe.newplayer.utils.getInsets import net.newpipe.newplayer.utils.rememberReorderHapticFeedback @@ -100,7 +101,12 @@ fun StreamSelectUI( thumbnail = chapter.thumbnail, onClicked = { viewModel.chapterSelected(chapter) - } + }, + isCurrentChapter = isActiveChapter( + chapterIndex, + uiState.chapters, + uiState.playbackPositionInMs + ) ) } @@ -139,7 +145,7 @@ fun ReorderableStreamItemsList( verticalArrangement = Arrangement.spacedBy(5.dp), state = lazyListState ) { - itemsIndexed(uiState.playList, key = {_, item -> item.uniqueId}) { index, playlistItem -> + itemsIndexed(uiState.playList, key = { _, item -> item.uniqueId }) { index, playlistItem -> ReorderableItem( state = reorderableLazyListState, key = playlistItem.uniqueId diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/streamselect/ChapterItem.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/streamselect/ChapterItem.kt index 626d946..4a22fb4 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/streamselect/ChapterItem.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/streamselect/ChapterItem.kt @@ -21,14 +21,20 @@ package net.newpipe.newplayer.ui.videoplayer.streamselect +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut import androidx.compose.foundation.Image import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable @@ -44,6 +50,8 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import coil.compose.AsyncImage +import net.newpipe.newplayer.Chapter +import net.newpipe.newplayer.NewPlayerException import net.newpipe.newplayer.R import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.ui.videoplayer.ITEM_CORNER_SHAPE @@ -54,6 +62,16 @@ import net.newpipe.newplayer.utils.VectorThumbnail import net.newpipe.newplayer.utils.getLocale import net.newpipe.newplayer.utils.getTimeStringFromMs +fun isActiveChapter(chapterId: Int, chapters: List, playbackPosition: Long) : Boolean { + assert(0 <= chapterId && chapterId < chapters.size) { + throw NewPlayerException("Chapter Id out of bounds: id: $chapterId, chapters.size: ${chapters.size}") + } + val chapterStart = chapters[chapterId].chapterStartInMs + val chapterEnd = + if (chapterId + 1 < chapters.size) chapters[chapterId + 1].chapterStartInMs + else Long.MAX_VALUE + return playbackPosition in chapterStart.. Unit + onClicked: (Int) -> Unit, + isCurrentChapter: Boolean ) { val locale = getLocale()!! - Row( + Box( modifier = modifier .height(80.dp) .clip(ITEM_CORNER_SHAPE) .clickable { onClicked(id) } ) { - val contentDescription = stringResource(R.string.chapter_thumbnail) - Thumbnail( - thumbnail = thumbnail, - contentDescription = contentDescription, - shape = ITEM_CORNER_SHAPE - ) - Column( - modifier = Modifier - .padding(start = 8.dp, top = 5.dp, bottom = 5.dp) - .weight(1f), - horizontalAlignment = Alignment.Start, + AnimatedVisibility( + isCurrentChapter, + enter = fadeIn(animationSpec = tween(200)), + exit = fadeOut(animationSpec = tween(400)) ) { - Text( - text = chapterTitle, - fontSize = 18.sp, - fontWeight = FontWeight.Bold, - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) - Text( - getTimeStringFromMs(chapterStartInMs, locale), - maxLines = 1, - overflow = TextOverflow.Ellipsis - ) + Surface( + modifier = Modifier.fillMaxSize(), + color = Color.White.copy(alpha = 0.2f), + ) {} } + + + Row { + val contentDescription = stringResource(R.string.chapter_thumbnail) + Thumbnail( + thumbnail = thumbnail, + contentDescription = contentDescription, + shape = ITEM_CORNER_SHAPE + ) + Column( + modifier = Modifier + .padding(start = 8.dp, top = 5.dp, bottom = 5.dp) + .weight(1f), + horizontalAlignment = Alignment.Start, + ) { + Text( + text = chapterTitle, + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + Text( + getTimeStringFromMs(chapterStartInMs, locale), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } } } @@ -111,7 +144,8 @@ fun ChapterItemPreview() { modifier = Modifier.fillMaxSize(), chapterTitle = "Chapter Title", chapterStartInMs = (4 * 60 + 32) * 1000, - onClicked = {} + onClicked = {}, + isCurrentChapter = false ) } } diff --git a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/streamselect/StreamSelectTopBar.kt b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/streamselect/StreamSelectTopBar.kt index 8ca33c9..add428e 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/streamselect/StreamSelectTopBar.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/ui/videoplayer/streamselect/StreamSelectTopBar.kt @@ -24,7 +24,6 @@ import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.filled.PlaylistAdd import androidx.compose.material.icons.filled.Close -import androidx.compose.material.icons.filled.PlaylistAdd import androidx.compose.material.icons.filled.Repeat import androidx.compose.material.icons.filled.RepeatOn import androidx.compose.material.icons.filled.RepeatOneOn @@ -43,8 +42,6 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview -import androidx.media3.common.Player -import net.newpipe.newplayer.NewPlayerException import net.newpipe.newplayer.R import net.newpipe.newplayer.RepeatMode import net.newpipe.newplayer.model.VideoPlayerUIState @@ -79,7 +76,7 @@ fun StreamSelectTopBar( ) }, actions = { IconButton( - onClick = viewModel::cycleRepeatmode + onClick = viewModel::cycleRepeatMode ) { when (uiState.repeatMode) { RepeatMode.DONT_REPEAT -> Icon(