make chapters visible in the seekbar

This commit is contained in:
Christian Schabesberger 2024-09-06 17:33:13 +02:00
parent 8a3cc12bd7
commit 430bed20b6
11 changed files with 157 additions and 107 deletions

View File

@ -81,7 +81,7 @@ data class VideoPlayerUIState(
seekerPosition = 0.3f, seekerPosition = 0.3f,
bufferedPercentage = 0.5f, bufferedPercentage = 0.5f,
isLoading = false, isLoading = false,
durationInMs = 420, durationInMs = 12000,
playbackPositionInPlaylistS = 5039, playbackPositionInPlaylistS = 5039,
playbackPositionInMs = 69, playbackPositionInMs = 69,
fastSeekSeconds = 10, fastSeekSeconds = 10,
@ -92,6 +92,11 @@ data class VideoPlayerUIState(
currentlyPlaying = PlaylistItem.DUMMY, currentlyPlaying = PlaylistItem.DUMMY,
currentPlaylistItemIndex = 1, currentPlaylistItemIndex = 1,
chapters = arrayListOf( chapters = arrayListOf(
Chapter(
chapterStartInMs = 0,
chapterTitle = "Intro",
thumbnail = null
),
Chapter( Chapter(
chapterStartInMs = 5000, chapterStartInMs = 5000,
chapterTitle = "First Chapter", chapterTitle = "First Chapter",
@ -102,11 +107,6 @@ data class VideoPlayerUIState(
chapterTitle = "Second Chapter", chapterTitle = "Second Chapter",
thumbnail = null thumbnail = null
), ),
Chapter(
chapterStartInMs = 20000,
chapterTitle = "Third Chapter",
thumbnail = null
)
), ),
playList = arrayListOf( playList = arrayListOf(
PlaylistItem( PlaylistItem(

View File

@ -42,7 +42,7 @@ interface VideoPlayerViewModel {
fun pause() fun pause()
fun prevStream() fun prevStream()
fun nextStream() fun nextStream()
fun switchToFullscreen(embeddedUiConfig: EmbeddedUiConfig) fun switchToFullscreen()
fun switchToEmbeddedView() fun switchToEmbeddedView()
fun onBackPressed() fun onBackPressed()
fun showUi() fun showUi()
@ -54,7 +54,7 @@ interface VideoPlayerViewModel {
fun finishFastSeek() fun finishFastSeek()
fun brightnessChange(changeRate: Float, systemBrightness: Float) fun brightnessChange(changeRate: Float, systemBrightness: Float)
fun volumeChange(changeRate: Float) fun volumeChange(changeRate: Float)
fun openStreamSelection(selectChapter: Boolean, embeddedUiConfig: EmbeddedUiConfig) fun openStreamSelection(selectChapter: Boolean)
fun closeStreamSelection() fun closeStreamSelection()
fun chapterSelected(chapterId: Int) fun chapterSelected(chapterId: Int)
fun streamSelected(streamId: Int) fun streamSelected(streamId: Int)

View File

@ -432,11 +432,8 @@ class VideoPlayerViewModelImpl @Inject constructor(
} }
} }
override fun openStreamSelection(selectChapter: Boolean, embeddedUiConfig: EmbeddedUiConfig) { override fun openStreamSelection(selectChapter: Boolean) {
uiVisibilityJob?.cancel() uiVisibilityJob?.cancel()
if (!uiState.value.uiMode.fullscreen) {
this.embeddedUiConfig = embeddedUiConfig
}
updateUiMode( updateUiMode(
if (selectChapter) uiState.value.uiMode.getChapterSelectUiState() if (selectChapter) uiState.value.uiMode.getChapterSelectUiState()
else uiState.value.uiMode.getStreamSelectUiState() else uiState.value.uiMode.getStreamSelectUiState()
@ -469,11 +466,10 @@ class VideoPlayerViewModelImpl @Inject constructor(
} }
} }
override fun switchToFullscreen(embeddedUiConfig: EmbeddedUiConfig) { override fun switchToFullscreen() {
uiVisibilityJob?.cancel() uiVisibilityJob?.cancel()
finishFastSeek() finishFastSeek()
this.embeddedUiConfig = embeddedUiConfig
updateUiMode(UIModeState.FULLSCREEN_VIDEO) updateUiMode(UIModeState.FULLSCREEN_VIDEO)
} }

View File

@ -36,7 +36,7 @@ open class VideoPlayerViewModelDummy : VideoPlayerViewModel {
println("dummy impl") println("dummy impl")
} }
override fun switchToFullscreen(embeddedUiConfig: EmbeddedUiConfig) { override fun switchToFullscreen() {
println("dummy impl") println("dummy impl")
} }
@ -61,7 +61,7 @@ open class VideoPlayerViewModelDummy : VideoPlayerViewModel {
} }
override fun fastSeek(steps: Int) { override fun fastSeek(steps: Int) {
println("dummy impl") println("dummy impl, steps: $steps")
} }
override fun finishFastSeek() { override fun finishFastSeek() {
@ -76,7 +76,7 @@ open class VideoPlayerViewModelDummy : VideoPlayerViewModel {
println("dummy impl") println("dummy impl")
} }
override fun openStreamSelection(selectChapter: Boolean, embeddedUiConfig: EmbeddedUiConfig) { override fun openStreamSelection(selectChapter: Boolean) {
println("dummy impl") println("dummy impl")
} }

View File

@ -36,11 +36,8 @@ import androidx.compose.foundation.interaction.Interaction
import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.offset import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.requiredSizeIn import androidx.compose.foundation.layout.requiredSizeIn
@ -49,8 +46,6 @@ import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.progressSemantics import androidx.compose.foundation.progressSemantics
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.ripple.rememberRipple import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -75,7 +70,6 @@ import androidx.compose.ui.graphics.drawscope.rotateRad
import androidx.compose.ui.graphics.drawscope.translate import androidx.compose.ui.graphics.drawscope.translate
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.VerticalAlignmentLine
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.semantics.disabled import androidx.compose.ui.semantics.disabled
@ -135,18 +129,25 @@ fun Seeker(
onValueChange: (Float) -> Unit, onValueChange: (Float) -> Unit,
onValueChangeFinished: (() -> Unit)? = null, onValueChangeFinished: (() -> Unit)? = null,
segments: List<Segment> = emptyList(), segments: List<Segment> = emptyList(),
chapterSegments: List<ChapterSegment> = emptyList(),
enabled: Boolean = true, enabled: Boolean = true,
colors: SeekerColors = SeekerDefaults.seekerColors(), colors: SeekerColors = SeekerDefaults.seekerColors(),
dimensions: SeekerDimensions = SeekerDefaults.seekerDimensions(), dimensions: SeekerDimensions = SeekerDefaults.seekerDimensions(),
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) { ) {
if (segments.isNotEmpty()) { if (segments.isNotEmpty()) {
require(segments.first().start == range.start) {
"the first segment should start from range start value"
}
segments.forEach { segments.forEach {
require(it.start in range && it.end in range) {
"segment must lie withing the range: segment: ${it.name} start: ${it.start}, end: ${it.end}, range: ${range}"
}
}
}
if (chapterSegments.isNotEmpty()) {
chapterSegments.forEach {
require(it.start in range) { require(it.start in range) {
"segment must start from withing the range." "chapter segment must lie withing the range: segment: ${it.name} start: ${it.start} range: ${range}"
} }
} }
} }
@ -178,6 +179,10 @@ fun Seeker(
segmentToPxValues(segments, range, widthPx) segmentToPxValues(segments, range, widthPx)
} }
val chapterSegmentsPx = remember(chapterSegments, range, widthPx) {
chapterSegmentToPxValues(chapterSegments, range, widthPx)
}
LaunchedEffect(thumbValue, segments) { LaunchedEffect(thumbValue, segments) {
state.currentSegment(thumbValue, segments) state.currentSegment(thumbValue, segments)
} }
@ -259,6 +264,7 @@ fun Seeker(
readAheadValuePx = readAheadValuePx, readAheadValuePx = readAheadValuePx,
enabled = enabled, enabled = enabled,
segments = segmentStarts, segments = segmentStarts,
chapterSegments = chapterSegmentsPx,
colors = colors, colors = colors,
dimensions = dimensions, dimensions = dimensions,
interactionSource = interactionSource interactionSource = interactionSource
@ -276,6 +282,7 @@ private fun Seeker(
readAheadValuePx: Float, readAheadValuePx: Float,
enabled: Boolean, enabled: Boolean,
segments: List<SegmentPxs>, segments: List<SegmentPxs>,
chapterSegments: List<SegmentPxs>,
colors: SeekerColors, colors: SeekerColors,
dimensions: SeekerDimensions, dimensions: SeekerDimensions,
interactionSource: MutableInteractionSource interactionSource: MutableInteractionSource
@ -288,6 +295,7 @@ private fun Seeker(
modifier = Modifier.fillMaxSize(), modifier = Modifier.fillMaxSize(),
enabled = enabled, enabled = enabled,
segments = segments, segments = segments,
chapterSegments = chapterSegments,
colors = colors, colors = colors,
widthPx = widthPx, widthPx = widthPx,
valuePx = valuePx, valuePx = valuePx,
@ -310,6 +318,7 @@ private fun Track(
modifier: Modifier, modifier: Modifier,
enabled: Boolean, enabled: Boolean,
segments: List<SegmentPxs>, segments: List<SegmentPxs>,
chapterSegments: List<SegmentPxs>,
colors: SeekerColors, colors: SeekerColors,
widthPx: Float, widthPx: Float,
valuePx: Float, valuePx: Float,
@ -334,35 +343,16 @@ private fun Track(
val left = thumbRadius.toPx() val left = thumbRadius.toPx()
translate(left = left) { translate(left = left) {
if (segments.isEmpty()) {
// draw the track with a single line. // draw the track with a single line.
drawLine( drawLine(
start = Offset(rtlAware(0f, widthPx, isRtl), center.y), start = Offset(rtlAware(0f, widthPx, isRtl), center.y),
end = Offset(rtlAware(widthPx, widthPx, isRtl), center.y), end = Offset(rtlAware(widthPx, widthPx, isRtl), center.y),
color = trackColor, color = trackColor,
strokeWidth = trackHeight.toPx(), strokeWidth = trackHeight.toPx(),
cap = StrokeCap.Round cap = StrokeCap.Round
) )
} else {
// draw segments in their respective color,
// excluding gaps (which will be cleared out later)
for (index in segments.indices) {
val segment = segments[index]
val segmentColor = when (segment.color) {
Color.Unspecified -> trackColor
else -> segment.color
}
drawSegment(
startPx = rtlAware(segment.startPx, widthPx, isRtl),
endPx = rtlAware(segment.endPx, widthPx, isRtl),
trackColor = segmentColor,
trackHeight = trackHeight.toPx(),
blendMode = BlendMode.SrcOver,
startCap = if (index == 0) StrokeCap.Round else null,
endCap = if (index == segments.lastIndex) StrokeCap.Round else null
)
}
}
// readAhead indicator // readAhead indicator
drawLine( drawLine(
@ -382,14 +372,28 @@ private fun Track(
cap = StrokeCap.Round cap = StrokeCap.Round
) )
// clear segment gaps // draw segments in their respective color,
for (index in segments.indices) { for (index in segments.indices) {
if (index == segments.lastIndex) break // skip "gap" after last segment
val segment = segments[index] val segment = segments[index]
drawGap( val segmentColor = when (segment.color) {
startPx = rtlAware(segment.endPx - segmentGap.toPx(), widthPx, isRtl), Color.Unspecified -> trackColor
else -> segment.color
}
drawSegment(
startPx = rtlAware(segment.startPx, widthPx, isRtl),
endPx = rtlAware(segment.endPx, widthPx, isRtl), endPx = rtlAware(segment.endPx, widthPx, isRtl),
trackColor = segmentColor,
trackHeight = trackHeight.toPx(), trackHeight = trackHeight.toPx(),
blendMode = BlendMode.SrcOver,
)
}
// clear segment gaps
for (index in chapterSegments.indices) {
val segment = chapterSegments[index]
drawDot(
x = rtlAware(segment.startPx, widthPx, isRtl),
trackHeight = trackHeight.toPx()
) )
} }
} }
@ -485,8 +489,6 @@ private fun DrawScope.drawSegment(
trackColor: Color, trackColor: Color,
trackHeight: Float, trackHeight: Float,
blendMode: BlendMode, blendMode: BlendMode,
startCap: StrokeCap? = null,
endCap: StrokeCap? = null
) { ) {
drawLine( drawLine(
start = Offset(startPx, center.y), start = Offset(startPx, center.y),
@ -494,8 +496,7 @@ private fun DrawScope.drawSegment(
color = trackColor, color = trackColor,
strokeWidth = trackHeight, strokeWidth = trackHeight,
blendMode = blendMode, blendMode = blendMode,
endCap = endCap, cap = StrokeCap.Round
startCap = startCap
) )
} }
@ -513,6 +514,18 @@ private fun DrawScope.drawGap(
) )
} }
private fun DrawScope.drawDot(
x: Float,
trackHeight: Float
) {
drawCircle(
radius = (trackHeight / 2f) * 0.8f,
center = Offset(x = x, y = center.y),
color = Color.Gray.copy(alpha = 0.9f),
blendMode = BlendMode.SrcOver
)
}
@Composable @Composable
private fun Thumb( private fun Thumb(
valuePx: () -> Float, valuePx: () -> Float,
@ -580,14 +593,21 @@ private fun Modifier.progressSemantics(
@Composable @Composable
fun SeekerPreview() { fun SeekerPreview() {
val segments = listOf( val segments = listOf(
Segment(name = "Intro", start = 0f), Segment(name = "Intro", start = 0.1f, end = 0.3f, color = Color.Green),
Segment(name = "Talk 1", start = 0.5f), Segment(name = "Talk 1", start = 0.5f, end = 0.6f, color = Color.Cyan),
Segment(name = "Talk 2", start = 0.8f), Segment(name = "Talk 2", start = 0.8f, end = 0.85f, color = Color.Blue),
)
val chapterSegments = listOf(
ChapterSegment(name = "Intro", start = 0.0f, color = Color.Green),
ChapterSegment(name = "Talk 1", start = 0.55f, color = Color.Cyan),
ChapterSegment(name = "Talk 2", start = 0.9f, color = Color.Blue),
) )
Seeker( Seeker(
value = 0.7f, value = 0.7f,
range = 0f..1f, range = 0f..1f,
segments = segments, segments = segments,
chapterSegments = chapterSegments,
onValueChange = { }, onValueChange = { },
) )
} }

View File

@ -74,13 +74,25 @@ fun rememberSeekerState(): SeekerState = remember {
data class Segment( data class Segment(
val name: String, val name: String,
val start: Float, val start: Float,
val end: Float,
val color: Color = Color.Unspecified val color: Color = Color.Unspecified
) { ) {
companion object { companion object {
val Unspecified = Segment(name = "", start = 0f) val Unspecified = Segment(name = "", start = 0f, end = 0f)
} }
} }
@Immutable
data class ChapterSegment(
val name: String,
val start: Float,
val color: Color = Color.Unspecified
) {
companion object {
val Unspecified = ChapterSegment(name = "", start = 0f)
}
}
@Immutable @Immutable
internal data class SegmentPxs( internal data class SegmentPxs(
val name: String, val name: String,

View File

@ -57,6 +57,37 @@ internal fun segmentToPxValues(
val rangeSize = range.endInclusive - range.start val rangeSize = range.endInclusive - range.start
val sortedSegments = segments.distinct().sortedBy { it.start } val sortedSegments = segments.distinct().sortedBy { it.start }
val segmentRangesPxs = sortedSegments.map { segment ->
// percent of the start of this segment in the range size
val percentStart = (segment.start - range.start) * 100 / rangeSize
val percentEnd = (segment.end - range.start) * 100 / rangeSize
val startPx = percentStart * widthPx / 100
val endPx = percentEnd * widthPx / 100
Pair(startPx, endPx)
}
return sortedSegments.mapIndexed { index, segment ->
SegmentPxs(
name = segment.name,
color = segment.color,
startPx = segmentRangesPxs[index].first,
endPx = segmentRangesPxs[index].second
)
}
}
internal fun chapterSegmentToPxValues(
segments: List<ChapterSegment>,
range: ClosedFloatingPointRange<Float>,
widthPx: Float,
): List<SegmentPxs> {
val rangeSize = range.endInclusive - range.start
val sortedSegments = ArrayList(segments.distinct().sortedBy { it.start })
if(sortedSegments.isNotEmpty()) {
sortedSegments.removeAt(0)
}
val segmentStartPxs = sortedSegments.map { segment -> val segmentStartPxs = sortedSegments.map { segment ->
// percent of the start of this segment in the range size // percent of the start of this segment in the range size

View File

@ -23,6 +23,7 @@ package net.newpipe.newplayer.ui.videoplayer
import android.app.Activity import android.app.Activity
import android.app.LocaleConfig import android.app.LocaleConfig
import android.icu.text.DecimalFormat import android.icu.text.DecimalFormat
import android.util.Log
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
@ -44,21 +45,23 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.core.os.ConfigurationCompat import androidx.core.os.ConfigurationCompat
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import net.newpipe.newplayer.Chapter
import net.newpipe.newplayer.R import net.newpipe.newplayer.R
import net.newpipe.newplayer.model.UIModeState import net.newpipe.newplayer.model.UIModeState
import net.newpipe.newplayer.model.VideoPlayerUIState import net.newpipe.newplayer.model.VideoPlayerUIState
import net.newpipe.newplayer.model.VideoPlayerViewModel import net.newpipe.newplayer.model.VideoPlayerViewModel
import net.newpipe.newplayer.model.VideoPlayerViewModelDummy import net.newpipe.newplayer.model.VideoPlayerViewModelDummy
import net.newpipe.newplayer.ui.seeker.ChapterSegment
import net.newpipe.newplayer.ui.seeker.DefaultSeekerColor import net.newpipe.newplayer.ui.seeker.DefaultSeekerColor
import net.newpipe.newplayer.ui.seeker.Seeker import net.newpipe.newplayer.ui.seeker.Seeker
import net.newpipe.newplayer.ui.seeker.SeekerColors import net.newpipe.newplayer.ui.seeker.SeekerColors
import net.newpipe.newplayer.ui.seeker.SeekerDefaults import net.newpipe.newplayer.ui.seeker.Segment
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.utils.getEmbeddedUiConfig
import net.newpipe.newplayer.utils.getLocale import net.newpipe.newplayer.utils.getLocale
import net.newpipe.newplayer.utils.getTimeStringFromMs import net.newpipe.newplayer.utils.getTimeStringFromMs
import java.util.Locale
import kotlin.math.min
private const val TAG = "BottomUI"
@Composable @Composable
fun BottomUI( fun BottomUI(
@ -77,17 +80,17 @@ fun BottomUI(
onValueChange = viewModel::seekPositionChanged, onValueChange = viewModel::seekPositionChanged,
onValueChangeFinished = viewModel::seekingFinished, onValueChangeFinished = viewModel::seekingFinished,
readAheadValue = uiState.bufferedPercentage, readAheadValue = uiState.bufferedPercentage,
colors = customizedSeekerColors() colors = customizedSeekerColors(),
chapterSegments = getSeekerSegmentsFromChapters(uiState.chapters, uiState.durationInMs)
) )
Text(getTimeStringFromMs(uiState.durationInMs, getLocale() ?: locale)) Text(getTimeStringFromMs(uiState.durationInMs, getLocale() ?: locale))
val embeddedUiConfig = getEmbeddedUiConfig(LocalContext.current as Activity)
IconButton( IconButton(
onClick = if (uiState.uiMode.fullscreen) viewModel::switchToEmbeddedView onClick = if (uiState.uiMode.fullscreen) viewModel::switchToEmbeddedView
else { else {
{ // <- head of lambda ... yea kotlin is weird { // <- head of lambda ... yea kotlin is weird
viewModel.switchToFullscreen(embeddedUiConfig) viewModel.switchToFullscreen()
} }
} }
) { ) {
@ -114,6 +117,21 @@ private fun customizedSeekerColors(): SeekerColors {
return colors return colors
} }
private fun getSeekerSegmentsFromChapters(chapters: List<Chapter>, duration: Long) =
chapters.map { chapter ->
val markPosition = chapter.chapterStartInMs.toFloat() / duration.toFloat()
if (markPosition < 0f || 1f < markPosition) {
Log.e(
TAG,
"Chapter mark outside of stream duration range: chapter: ${chapter.chapterTitle}, mark in ms: ${chapter.chapterStartInMs}, vidoe duration in ms: ${duration}"
)
ChapterSegment(name = chapter.chapterTitle ?: "", start = 0f)
} else {
ChapterSegment(name = chapter.chapterTitle ?: "", start = markPosition)
}
}
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////
// Preview // Preview
/////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////

View File

@ -51,16 +51,14 @@ import net.newpipe.newplayer.R
import net.newpipe.newplayer.model.VideoPlayerUIState import net.newpipe.newplayer.model.VideoPlayerUIState
import net.newpipe.newplayer.model.VideoPlayerViewModel import net.newpipe.newplayer.model.VideoPlayerViewModel
import net.newpipe.newplayer.model.VideoPlayerViewModelDummy import net.newpipe.newplayer.model.VideoPlayerViewModelDummy
import net.newpipe.newplayer.playerInternals.PlaylistItem
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.ui.theme.video_player_onSurface import net.newpipe.newplayer.ui.theme.video_player_onSurface
import net.newpipe.newplayer.utils.getEmbeddedUiConfig
@Composable @Composable
fun TopUI( fun TopUI(
modifier: Modifier, viewModel: VideoPlayerViewModel, uiState: VideoPlayerUIState modifier: Modifier, viewModel: VideoPlayerViewModel, uiState: VideoPlayerUIState
) { ) {
val embeddedUiConfig = getEmbeddedUiConfig(activity = LocalContext.current as Activity)
Row( Row(
modifier = modifier, modifier = modifier,
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
@ -101,7 +99,7 @@ fun TopUI(
} }
AnimatedVisibility(visible = uiState.chapters.isNotEmpty()) { AnimatedVisibility(visible = uiState.chapters.isNotEmpty()) {
IconButton( IconButton(
onClick = { viewModel.openStreamSelection(selectChapter = true, embeddedUiConfig) }, onClick = { viewModel.openStreamSelection(selectChapter = true) },
) { ) {
Icon( Icon(
imageVector = Icons.AutoMirrored.Filled.MenuBook, imageVector = Icons.AutoMirrored.Filled.MenuBook,
@ -113,8 +111,7 @@ fun TopUI(
IconButton( IconButton(
onClick = { onClick = {
viewModel.openStreamSelection( viewModel.openStreamSelection(
selectChapter = false, selectChapter = false
embeddedUiConfig
) )
}, },
) { ) {

View File

@ -21,8 +21,7 @@
package net.newpipe.newplayer.ui.videoplayer.gesture_ui package net.newpipe.newplayer.ui.videoplayer.gesture_ui
import android.app.Activity import android.util.Log
import android.util.Log
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
@ -36,13 +35,11 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import net.newpipe.newplayer.model.VideoPlayerUIState import net.newpipe.newplayer.model.VideoPlayerUIState
import net.newpipe.newplayer.model.VideoPlayerViewModel import net.newpipe.newplayer.model.VideoPlayerViewModel
import net.newpipe.newplayer.model.VideoPlayerViewModelDummy import net.newpipe.newplayer.model.VideoPlayerViewModelDummy
import net.newpipe.newplayer.ui.theme.VideoPlayerTheme import net.newpipe.newplayer.ui.theme.VideoPlayerTheme
import net.newpipe.newplayer.utils.getEmbeddedUiConfig
private const val TAG = "EmbeddedGestureUI" private const val TAG = "EmbeddedGestureUI"
@ -55,8 +52,6 @@ fun EmbeddedGestureUI(
mutableStateOf(false) mutableStateOf(false)
} }
val embeddedUiConfig = getEmbeddedUiConfig(LocalContext.current as Activity)
val handleMovement = { movement: TouchedPosition -> val handleMovement = { movement: TouchedPosition ->
Log.d(TAG, "${movement.x}:${movement.y}") Log.d(TAG, "${movement.x}:${movement.y}")
if (0 < movement.y) { if (0 < movement.y) {
@ -66,7 +61,7 @@ fun EmbeddedGestureUI(
// this check is there to allow a temporary move up in the downward gesture // this check is there to allow a temporary move up in the downward gesture
if (downwardMovementMode == false) { if (downwardMovementMode == false) {
viewModel.switchToFullscreen(embeddedUiConfig) viewModel.switchToFullscreen()
} else { } else {
viewModel.embeddedDraggedDown(movement.y) viewModel.embeddedDraggedDown(movement.y)
} }

View File

@ -89,25 +89,6 @@ fun getLocale(): Locale? {
return ConfigurationCompat.getLocales(configuration).get(0) return ConfigurationCompat.getLocales(configuration).get(0)
} }
@Composable
@ReadOnlyComposable
fun getEmbeddedUiConfig(activity: Activity): EmbeddedUiConfig {
val window = activity.window
val view = LocalView.current
val isLightStatusBar = WindowCompat.getInsetsController(
window,
view
).isAppearanceLightStatusBars
val screenOrientation = activity.requestedOrientation
val defaultBrightness = getDefaultBrightness(activity)
return EmbeddedUiConfig(
systemBarInLightMode = isLightStatusBar,
brightness = defaultBrightness,
screenOrientation = screenOrientation
)
}
@Composable @Composable
fun getInsets() = fun getInsets() =
WindowInsets.systemBars.union(WindowInsets.displayCutout).union(WindowInsets.waterfall) WindowInsets.systemBars.union(WindowInsets.displayCutout).union(WindowInsets.waterfall)