highlight active chapter

This commit is contained in:
Christian Schabesberger 2024-09-06 13:58:52 +02:00
parent 48f5e159d9
commit 79f8719ac3
6 changed files with 78 additions and 39 deletions

View File

@ -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)

View File

@ -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

View File

@ -92,7 +92,7 @@ open class VideoPlayerViewModelDummy : VideoPlayerViewModel {
println("dummy impl stream selected: $streamId")
}
override fun cycleRepeatmode() {
override fun cycleRepeatMode() {
println("dummy impl")
}

View File

@ -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

View File

@ -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<Chapter>, 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..<chapterEnd
}
@Composable
fun ChapterItem(
@ -62,15 +80,30 @@ fun ChapterItem(
thumbnail: Thumbnail?,
chapterTitle: String,
chapterStartInMs: Long,
onClicked: (Int) -> Unit
onClicked: (Int) -> Unit,
isCurrentChapter: Boolean
) {
val locale = getLocale()!!
Row(
Box(
modifier = modifier
.height(80.dp)
.clip(ITEM_CORNER_SHAPE)
.clickable { onClicked(id) }
) {
AnimatedVisibility(
isCurrentChapter,
enter = fadeIn(animationSpec = tween(200)),
exit = fadeOut(animationSpec = tween(400))
) {
Surface(
modifier = Modifier.fillMaxSize(),
color = Color.White.copy(alpha = 0.2f),
) {}
}
Row {
val contentDescription = stringResource(R.string.chapter_thumbnail)
Thumbnail(
thumbnail = thumbnail,
@ -96,7 +129,7 @@ fun ChapterItem(
overflow = TextOverflow.Ellipsis
)
}
}
}
}
@ -111,7 +144,8 @@ fun ChapterItemPreview() {
modifier = Modifier.fillMaxSize(),
chapterTitle = "Chapter Title",
chapterStartInMs = (4 * 60 + 32) * 1000,
onClicked = {}
onClicked = {},
isCurrentChapter = false
)
}
}

View File

@ -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(