diff --git a/new-player/src/main/java/net/newpipe/newplayer/MediaRepository.kt b/new-player/src/main/java/net/newpipe/newplayer/MediaRepository.kt index 123b00c..26013b8 100644 --- a/new-player/src/main/java/net/newpipe/newplayer/MediaRepository.kt +++ b/new-player/src/main/java/net/newpipe/newplayer/MediaRepository.kt @@ -24,7 +24,7 @@ import android.net.Uri import androidx.media3.common.PlaybackException import net.newpipe.newplayer.utils.Thumbnail -data class Chapter(val chapterStartInMs: Long, val chapterTitle: String?) +data class Chapter(val chapterStartInMs: Long, val chapterTitle: String?, val thumbnail: Thumbnail?) data class MetaInfo( val title: String, @@ -46,7 +46,6 @@ interface MediaRepository { suspend fun getPreviewThumbnails(item: String): HashMap? suspend fun getChapters(item: String): List - suspend fun getChapterThumbnail(item: String, chapter: Long): Thumbnail? suspend fun getTimestampLink(item: String, timestampInSeconds: Long): String 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 16a2398..6625bc1 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 @@ -24,6 +24,7 @@ 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.ui.ContentScale import net.newpipe.newplayer.utils.Thumbnail @@ -58,4 +59,6 @@ interface VideoPlayerViewModel { fun volumeChange(changeRate: Float) fun openStreamSelection(selectChapter: Boolean, embeddedUiConfig: EmbeddedUiConfig) fun closeStreamSelection() + fun chapterSelected(chapter: Chapter) + fun streamSelected(streamId: Int) } \ No newline at end of file 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 7508ac1..e1927fe 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 @@ -43,6 +43,7 @@ import javax.inject.Inject import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch +import net.newpipe.newplayer.Chapter import net.newpipe.newplayer.utils.VideoSize import net.newpipe.newplayer.NewPlayer import net.newpipe.newplayer.playerInternals.getPlaylistItemsFromItemList @@ -399,6 +400,14 @@ class VideoPlayerViewModelImpl @Inject constructor( updateUiMode(UIModeState.FULLSCREEN_VIDEO) } + override fun chapterSelected(chapter: Chapter) { + println("gurken chapter selectd: $chapter") + } + + override fun streamSelected(streamId: Int) { + println("stream selected: $streamId") + } + private fun updateUiMode(newState: UIModeState) { val newPlayMode = newState.toPlayMode() val currentPlayMode = mutableUiState.value.uiMode.toPlayMode() 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 92959a1..2acc865 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 @@ -6,6 +6,7 @@ import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharedFlow import kotlinx.coroutines.flow.asSharedFlow +import net.newpipe.newplayer.Chapter import net.newpipe.newplayer.NewPlayer import net.newpipe.newplayer.ui.ContentScale @@ -83,6 +84,14 @@ open class VideoPlayerViewModelDummy : VideoPlayerViewModel { println("dummy impl") } + override fun chapterSelected(chapter: Chapter) { + println("dummp impl chapter selected: $chapter") + } + + override fun streamSelected(streamId: Int) { + println("dummy impl stream selected: $streamId") + } + override fun pause() { println("dummy pause") } 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 4b23d02..4d611f2 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 @@ -21,29 +21,32 @@ package net.newpipe.newplayer.ui.videoplayer import android.view.MotionEvent +import android.view.Surface import androidx.compose.foundation.Image import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.aspectRatio 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.foundation.layout.size -import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.DragHandle +import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar +import androidx.compose.material3.TopAppBarDefaults.topAppBarColors import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.ExperimentalComposeUiApi @@ -65,6 +68,7 @@ import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.utils.getLocale import net.newpipe.newplayer.utils.getTimeStringFromMs import coil.compose.AsyncImage +import net.newpipe.newplayer.Chapter import net.newpipe.newplayer.utils.BitmapThumbnail import net.newpipe.newplayer.utils.OnlineThumbnail import net.newpipe.newplayer.utils.Thumbnail @@ -79,12 +83,14 @@ fun StreamSelectUI( ) { val insets = getInsets() Surface( - modifier = Modifier - .fillMaxSize() - .windowInsetsPadding(insets), + modifier = Modifier.fillMaxSize(), color = CONTROLLER_UI_BACKGROUND_COLOR ) { Scaffold( + modifier = Modifier + .fillMaxSize() + .windowInsetsPadding(insets), + containerColor = Color.Transparent, topBar = { if (isChapterSelect) { ChapterSelectTopBar(onClose = { @@ -95,37 +101,50 @@ fun StreamSelectUI( } } ) { innerPadding -> - Surface( + LazyColumn( modifier = Modifier .padding(innerPadding) - .fillMaxSize(), color = Color.Red + .fillMaxSize(), ) { + if (isChapterSelect) { + items(uiState.chapters.size) { chapterIndex -> + val chapter = uiState.chapters[chapterIndex] + ChapterItem( + id = chapterIndex, + chapterTitle = chapter.chapterTitle ?: "", + chapterStartInMs = chapter.chapterStartInMs, + thumbnail = chapter.thumbnail, + onClicked = { + viewModel.chapterSelected(chapter) + } + ) + } + } else { + } } } } } +@OptIn(ExperimentalMaterial3Api::class) @Composable private fun ChapterSelectTopBar(modifier: Modifier = Modifier, onClose: () -> Unit) { - Row( - modifier = modifier, - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.SpaceBetween - ) { - Spacer(modifier = Modifier.width(20.dp)) - //Text(stringResource(R.string.chapter)) - Text("Chapter TODO") - Spacer(modifier = Modifier.weight(1F)) - IconButton( - onClick = onClose - ) { - Icon( - imageVector = Icons.Filled.Close, - contentDescription = stringResource(R.string.close_chapter_selection) - ) - } - } + TopAppBar(modifier = modifier, + colors = topAppBarColors(containerColor = Color.Transparent), + title = { + Text("Chapter TODO") + //Text(stringResource(R.string.chapter)) + }, actions = { + IconButton( + onClick = onClose + ) { + Icon( + imageVector = Icons.Filled.Close, + contentDescription = stringResource(R.string.close_chapter_selection) + ) + } + }) } @Composable @@ -150,7 +169,7 @@ private fun ChapterItem( top = 4.dp, bottom = 4.dp, end = 4.dp - ) + ).height(80.dp) .clickable { onClicked(id) } ) { val contentDescription = stringResource(R.string.chapter) @@ -348,11 +367,29 @@ fun ChapterTopBarPreview() { @Composable fun VideoPlayerStreamSelectUIPreview() { VideoPlayerTheme { - Surface(modifier = Modifier.fillMaxSize(), color = Color.Green) { + Surface(modifier = Modifier.fillMaxSize(), color = Color.Red) { StreamSelectUI( isChapterSelect = true, viewModel = VideoPlayerViewModelDummy(), - uiState = VideoPlayerUIState.DEFAULT + uiState = VideoPlayerUIState.DEFAULT.copy( + chapters = arrayListOf( + Chapter( + chapterStartInMs = 5000, + chapterTitle = "First Chapter", + thumbnail = null + ), + Chapter( + chapterStartInMs = 10000, + chapterTitle = "Second Chapter", + thumbnail = null + ), + Chapter( + chapterStartInMs = 20000, + chapterTitle = "Third Chapter", + thumbnail = null + ), + ) + ) ) } } diff --git a/test-app/src/main/java/net/newpipe/newplayer/testapp/TestMediaRepository.kt b/test-app/src/main/java/net/newpipe/newplayer/testapp/TestMediaRepository.kt index 1175b87..285f062 100644 --- a/test-app/src/main/java/net/newpipe/newplayer/testapp/TestMediaRepository.kt +++ b/test-app/src/main/java/net/newpipe/newplayer/testapp/TestMediaRepository.kt @@ -30,12 +30,14 @@ class TestMediaRepository(val context: Context) : MediaRepository { thumbnail = OnlineThumbnail(context.getString(R.string.ccc_6502_thumbnail)), lengthInS = context.resources.getInteger(R.integer.ccc_6502_length) ) + "imu" -> MetaInfo( title = context.getString(R.string.ccc_imu_title), channelName = context.getString(R.string.ccc_imu_channel), thumbnail = OnlineThumbnail(context.getString(R.string.ccc_imu_thumbnail)), lengthInS = context.resources.getInteger(R.integer.ccc_imu_length) ) + "portrait" -> MetaInfo( title = context.getString(R.string.portrait_title), channelName = context.getString(R.string.portrait_channel), @@ -113,29 +115,29 @@ class TestMediaRepository(val context: Context) : MediaRepository { override suspend fun getChapters(item: String) = when (item) { "6502" -> context.resources.getIntArray(R.array.ccc_6502_chapters) - "imu" -> TODO() + "imu" -> context.resources.getIntArray(R.array.ccc_imu_chapters) else -> intArrayOf() }.map { - Chapter(it.toLong(), "Dummy Chapter at timestamp $it") - } + Chapter( + it.toLong(), chapterTitle = "Dummy Chapter at timestamp $it", + thumbnail = when (item) { + "6502" -> OnlineThumbnail( + String.format( + context.getString(R.string.ccc_6502_preview_thumbnails), + it / (10 * 1000) + ) + ) - override suspend fun getChapterThumbnail(item: String, chapter: Long) = - when (item) { - "6502" -> OnlineThumbnail( - String.format( - context.getString(R.string.ccc_6502_preview_thumbnails), - chapter / (10 * 1000) - ) + "imu" -> OnlineThumbnail( + String.format( + context.getString(R.string.ccc_imu_preview_thumbnails), + it / (10 * 1000) + ) + ) + + else -> null + } ) - - "imu" -> OnlineThumbnail( - String.format( - context.getString(R.string.ccc_imu_preview_thumbnails), - chapter / (10 * 1000) - ) - ) - - else -> null } override suspend fun getTimestampLink(item: String, timestampInSeconds: Long) =