mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-07 21:48:14 -08:00
fix: More native player UI fixes
This commit is contained in:
parent
edbd8d467c
commit
cfd4b4a5cc
12 changed files with 217 additions and 55 deletions
|
|
@ -47,7 +47,7 @@ fun VideoPlayerScreen(
|
|||
) {
|
||||
val leanBackEnabled = leanBackEnabled(LocalContext.current)
|
||||
ExoPlayer { player ->
|
||||
ScaledContent(if (leanBackEnabled) 0.75f else 1f) {
|
||||
ScaledContent(if (leanBackEnabled) 0.7f else 1f) {
|
||||
CustomVideoControls(player)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ internal fun CustomIconButton(
|
|||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CompositionLocalProvider(LocalContentColor provides currentContentColor) {
|
||||
Box(modifier = Modifier.padding(8.dp)) {
|
||||
Box(modifier = Modifier.padding(12.dp)) {
|
||||
icon()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import PlayableData
|
|||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.statusBarsPadding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
|
|
@ -13,19 +12,20 @@ import androidx.compose.ui.Alignment
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.compose.AsyncImage
|
||||
|
||||
@Composable
|
||||
fun ItemHeader(state: PlayableData?) {
|
||||
fun ItemHeader(
|
||||
modifier: Modifier = Modifier,
|
||||
state: PlayableData?
|
||||
) {
|
||||
val title = state?.title
|
||||
val logoUrl = state?.logoUrl
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.statusBarsPadding()
|
||||
.padding(16.dp),
|
||||
.statusBarsPadding(),
|
||||
contentAlignment = Alignment.CenterStart
|
||||
) {
|
||||
if (!logoUrl.isNullOrBlank()) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
|
|||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
|
|
@ -51,7 +52,6 @@ import androidx.compose.ui.input.key.Key.Companion.DirectionRight
|
|||
import androidx.compose.ui.input.key.Key.Companion.Enter
|
||||
import androidx.compose.ui.input.key.Key.Companion.Escape
|
||||
import androidx.compose.ui.input.key.Key.Companion.Spacebar
|
||||
import androidx.compose.ui.input.key.KeyEvent
|
||||
import androidx.compose.ui.input.key.KeyEventType
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.key.onKeyEvent
|
||||
|
|
@ -63,12 +63,14 @@ import androidx.compose.ui.text.font.FontWeight
|
|||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.util.fastCoerceIn
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import kotlinx.coroutines.delay
|
||||
import nl.jknaapen.fladder.objects.VideoPlayerObject
|
||||
import nl.jknaapen.fladder.utility.formatTime
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.toDuration
|
||||
|
||||
|
|
@ -259,7 +261,7 @@ internal fun RowScope.SimpleProgressBar(
|
|||
modifier = Modifier
|
||||
.focusable(enabled = false)
|
||||
.fillMaxWidth()
|
||||
.height(9.dp)
|
||||
.height(8.dp)
|
||||
.background(
|
||||
color = Color.White.copy(
|
||||
alpha = 0.15f
|
||||
|
|
@ -313,10 +315,10 @@ internal fun RowScope.SimpleProgressBar(
|
|||
.focusable(enabled = false)
|
||||
.graphicsLayer {
|
||||
translationX = startPx
|
||||
translationY = 13.dp.toPx()
|
||||
translationY = 14.dp.toPx()
|
||||
}
|
||||
.width(segDp)
|
||||
.height(7.dp)
|
||||
.height(6.dp)
|
||||
.background(
|
||||
color = segment.color.copy(alpha = 0.75f),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
|
|
@ -367,6 +369,30 @@ internal fun RowScope.SimpleProgressBar(
|
|||
)
|
||||
|
||||
|
||||
var direction by remember { mutableIntStateOf(0) }
|
||||
var speed by remember { mutableLongStateOf(1L) }
|
||||
val scrubSpeedDivider = 15L
|
||||
|
||||
val lastInteraction = remember { mutableLongStateOf(System.currentTimeMillis()) }
|
||||
|
||||
// Restart the multiplier
|
||||
LaunchedEffect(lastInteraction.longValue) {
|
||||
delay(500.milliseconds)
|
||||
speed = 1L
|
||||
}
|
||||
|
||||
fun updateLastInteraction() {
|
||||
lastInteraction.longValue = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
val scrubSpeed = playbackData?.trickPlayModel?.interval ?: 5.seconds.inWholeMilliseconds
|
||||
|
||||
fun scrubSpeedResult(): Long {
|
||||
return (scrubSpeed * (speed / scrubSpeedDivider).coerceIn(
|
||||
1L..60.seconds.inWholeMilliseconds
|
||||
))
|
||||
}
|
||||
|
||||
//Thumb
|
||||
Box(
|
||||
modifier = Modifier
|
||||
|
|
@ -379,7 +405,7 @@ internal fun RowScope.SimpleProgressBar(
|
|||
}
|
||||
}
|
||||
.focusable(enabled = true)
|
||||
.onKeyEvent { keyEvent: KeyEvent ->
|
||||
.onKeyEvent { keyEvent ->
|
||||
if (keyEvent.type != KeyEventType.KeyDown) return@onKeyEvent false
|
||||
|
||||
onUserInteraction()
|
||||
|
|
@ -392,25 +418,42 @@ internal fun RowScope.SimpleProgressBar(
|
|||
}
|
||||
|
||||
DirectionLeft -> {
|
||||
if (direction != -1) {
|
||||
direction = -1
|
||||
speed = 1L
|
||||
} else {
|
||||
speed++
|
||||
}
|
||||
if (!scrubbingTimeLine) {
|
||||
onTempPosChanged(position)
|
||||
onScrubbingChanged(true)
|
||||
player.pause()
|
||||
}
|
||||
val newPos = max(0L, tempPosition - 3000L)
|
||||
val newPos = max(
|
||||
0L,
|
||||
tempPosition - scrubSpeedResult()
|
||||
)
|
||||
onTempPosChanged(newPos)
|
||||
updateLastInteraction()
|
||||
true
|
||||
}
|
||||
|
||||
DirectionRight -> {
|
||||
if (direction != 1) {
|
||||
direction = 1
|
||||
speed = 1L
|
||||
} else {
|
||||
speed++
|
||||
}
|
||||
if (!scrubbingTimeLine) {
|
||||
onTempPosChanged(position)
|
||||
onScrubbingChanged(true)
|
||||
player.pause()
|
||||
}
|
||||
val newPos = min(player.duration.takeIf { it > 0 } ?: 1L,
|
||||
tempPosition + 3000L)
|
||||
tempPosition + scrubSpeedResult())
|
||||
onTempPosChanged(newPos)
|
||||
updateLastInteraction()
|
||||
true
|
||||
}
|
||||
|
||||
|
|
@ -448,6 +491,7 @@ internal fun RowScope.SimpleProgressBar(
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
val MediaSegment.color: Color
|
||||
get() = when (this.type) {
|
||||
MediaSegmentType.COMMERCIAL -> Color.Magenta
|
||||
|
|
|
|||
|
|
@ -88,15 +88,16 @@ internal fun BoxScope.SegmentSkipOverlay(
|
|||
AnimatedVisibility(
|
||||
activeSegment != null && skip == SegmentSkip.ASK,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.align(alignment = Alignment.CenterEnd)
|
||||
.padding(16.dp)
|
||||
.safeContentPadding()
|
||||
) {
|
||||
CustomIconButton(
|
||||
modifier = modifier
|
||||
.align(alignment = Alignment.CenterEnd)
|
||||
.focusRequester(focusRequester)
|
||||
.defaultSelected(true),
|
||||
backgroundColor = Color.Black.copy(alpha = 0.5f),
|
||||
enableScaledFocus = true,
|
||||
onClick = {
|
||||
activeSegment?.let {
|
||||
player.seekTo(it.end)
|
||||
|
|
|
|||
|
|
@ -48,8 +48,11 @@ import androidx.compose.ui.geometry.Offset
|
|||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.input.key.Key.Companion.DirectionLeft
|
||||
import androidx.compose.ui.input.key.Key.Companion.DirectionRight
|
||||
import androidx.compose.ui.input.key.KeyEvent
|
||||
import androidx.compose.ui.input.key.KeyEventType
|
||||
import androidx.compose.ui.input.key.key
|
||||
import androidx.compose.ui.input.key.onKeyEvent
|
||||
import androidx.compose.ui.input.key.type
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
|
|
@ -76,6 +79,7 @@ import nl.jknaapen.fladder.utility.ImmersiveSystemBars
|
|||
import nl.jknaapen.fladder.utility.defaultSelected
|
||||
import nl.jknaapen.fladder.utility.leanBackEnabled
|
||||
import nl.jknaapen.fladder.utility.visible
|
||||
import kotlin.math.absoluteValue
|
||||
import kotlin.time.Duration.Companion.seconds
|
||||
|
||||
|
||||
|
|
@ -98,7 +102,7 @@ fun CustomVideoControls(
|
|||
|
||||
val buffering by VideoPlayerObject.buffering.collectAsState(true)
|
||||
val playing by VideoPlayerObject.playing.collectAsState(false)
|
||||
val controlsPadding = 16.dp
|
||||
val controlsPadding = 32.dp
|
||||
|
||||
ImmersiveSystemBars(isImmersive = !showControls)
|
||||
|
||||
|
|
@ -128,6 +132,26 @@ fun CustomVideoControls(
|
|||
lastInteraction.longValue = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
val forwardSpeed by PlayerSettingsObject.forwardSpeed.collectAsState(30.seconds)
|
||||
val backwardSpeed by PlayerSettingsObject.backwardSpeed.collectAsState(15.seconds)
|
||||
|
||||
val position by VideoPlayerObject.position.collectAsState(0L)
|
||||
val player = VideoPlayerObject.implementation.player
|
||||
val lastSeekInteraction = remember { mutableLongStateOf(System.currentTimeMillis()) }
|
||||
|
||||
var currentSkipTime by remember { mutableLongStateOf(0L) }
|
||||
|
||||
// Restart the multiplier
|
||||
LaunchedEffect(lastSeekInteraction.longValue) {
|
||||
delay(2.seconds)
|
||||
player?.seekTo(position + currentSkipTime)
|
||||
currentSkipTime = 0L
|
||||
}
|
||||
|
||||
fun updateSeekInteraction() {
|
||||
lastSeekInteraction.longValue = System.currentTimeMillis()
|
||||
}
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
|
|
@ -139,7 +163,27 @@ fun CustomVideoControls(
|
|||
}
|
||||
.onKeyEvent { keyEvent: KeyEvent ->
|
||||
if (keyEvent.type != KeyEventType.KeyDown) return@onKeyEvent false
|
||||
|
||||
if (!showControls) {
|
||||
when (keyEvent.key) {
|
||||
DirectionLeft -> {
|
||||
if (currentSkipTime == 0L) {
|
||||
player?.seekTo(position - backwardSpeed.inWholeMilliseconds)
|
||||
}
|
||||
currentSkipTime -= backwardSpeed.inWholeMilliseconds
|
||||
updateSeekInteraction()
|
||||
return@onKeyEvent true
|
||||
}
|
||||
|
||||
DirectionRight -> {
|
||||
if (currentSkipTime.absoluteValue == 0L) {
|
||||
player?.seekTo(position + forwardSpeed.inWholeMilliseconds)
|
||||
}
|
||||
currentSkipTime += forwardSpeed.inWholeMilliseconds
|
||||
updateSeekInteraction()
|
||||
return@onKeyEvent true
|
||||
}
|
||||
}
|
||||
bottomControlFocusRequester.requestFocus()
|
||||
updateLastInteraction()
|
||||
return@onKeyEvent true
|
||||
|
|
@ -193,7 +237,10 @@ fun CustomVideoControls(
|
|||
null
|
||||
)
|
||||
state?.let {
|
||||
ItemHeader(it)
|
||||
ItemHeader(
|
||||
modifier = Modifier.padding(controlsPadding),
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
if (!leanBackEnabled(LocalContext.current)) {
|
||||
|
|
@ -217,7 +264,6 @@ fun CustomVideoControls(
|
|||
// Progress Bar
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(horizontal = controlsPadding)
|
||||
.background(
|
||||
brush = Brush.linearGradient(
|
||||
colors = listOf(
|
||||
|
|
@ -228,6 +274,7 @@ fun CustomVideoControls(
|
|||
end = Offset(0f, Float.POSITIVE_INFINITY)
|
||||
),
|
||||
)
|
||||
.padding(horizontal = controlsPadding)
|
||||
.displayCutoutPadding()
|
||||
.padding(top = 8.dp, bottom = controlsPadding)
|
||||
) {
|
||||
|
|
@ -250,10 +297,10 @@ fun CustomVideoControls(
|
|||
RightButtons(showAudioDialog, showSubDialog)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
SegmentSkipOverlay()
|
||||
SeekOverlay(value = currentSkipTime)
|
||||
if (buffering && !playing) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier
|
||||
|
|
|
|||
|
|
@ -10,13 +10,16 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import nl.jknaapen.fladder.objects.VideoPlayerObject
|
||||
import nl.jknaapen.fladder.utility.clearAudioTrack
|
||||
import nl.jknaapen.fladder.utility.defaultSelected
|
||||
import nl.jknaapen.fladder.utility.conditional
|
||||
import nl.jknaapen.fladder.utility.setInternalAudioTrack
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
|
|
@ -29,6 +32,8 @@ fun AudioPicker(
|
|||
val audioTracks by VideoPlayerObject.audioTracks.collectAsState(listOf())
|
||||
val internalAudioTracks by VideoPlayerObject.exoAudioTracks
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LaunchedEffect(selectedIndex) {
|
||||
|
|
@ -53,7 +58,9 @@ fun AudioPicker(
|
|||
TrackButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.defaultSelected(selectedOff),
|
||||
.conditional(selectedOff) {
|
||||
focusRequester(focusRequester)
|
||||
},
|
||||
onClick = {
|
||||
VideoPlayerObject.setAudioTrackIndex(-1)
|
||||
player.clearAudioTrack()
|
||||
|
|
@ -72,7 +79,9 @@ fun AudioPicker(
|
|||
TrackButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.defaultSelected(selected),
|
||||
.conditional(selected) {
|
||||
focusRequester(focusRequester)
|
||||
},
|
||||
onClick = {
|
||||
serverTrack?.index?.let {
|
||||
VideoPlayerObject.setAudioTrackIndex(it.toInt())
|
||||
|
|
|
|||
|
|
@ -27,12 +27,14 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.focus.onFocusChanged
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.compose.AsyncImage
|
||||
import nl.jknaapen.fladder.objects.VideoPlayerObject
|
||||
import nl.jknaapen.fladder.utility.defaultSelected
|
||||
import nl.jknaapen.fladder.utility.conditional
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
@ -44,6 +46,8 @@ internal fun ChapterSelectionSheet(
|
|||
val chapters = playbackData?.chapters ?: listOf()
|
||||
val currentPosition by VideoPlayerObject.position.collectAsState(0L)
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
if (chapters.isEmpty()) return
|
||||
|
||||
var currentChapter: Chapter? by remember {
|
||||
|
|
@ -56,10 +60,13 @@ internal fun ChapterSelectionSheet(
|
|||
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
LaunchedEffect(chapters) {
|
||||
LaunchedEffect(chapters, currentPosition) {
|
||||
val chapter = chapters.indexOfCurrent(currentPosition)
|
||||
lazyListState.animateScrollToItem(
|
||||
chapters.indexOfCurrent(currentPosition)
|
||||
chapter
|
||||
)
|
||||
currentChapter = chapters[chapter]
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
|
||||
CustomModalBottomSheet(
|
||||
|
|
@ -68,7 +75,7 @@ internal fun ChapterSelectionSheet(
|
|||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
.wrapContentHeight(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
|
|
@ -88,15 +95,19 @@ internal fun ChapterSelectionSheet(
|
|||
Column(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = Color.Black.copy(alpha = 0.75f),
|
||||
color = if (selectedChapter) Color.White.copy(alpha = 0.25f) else Color.Black.copy(
|
||||
alpha = 0.75f
|
||||
),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
.border(
|
||||
width = 2.dp,
|
||||
color = Color.White.copy(alpha = if (selectedChapter) 1f else 0f),
|
||||
color = Color.White.copy(alpha = if (selectedChapter) 0.45f else 0f),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
.defaultSelected(index == 0)
|
||||
.conditional(selectedChapter) {
|
||||
focusRequester(focusRequester)
|
||||
}
|
||||
.onFocusChanged {
|
||||
if (it.isFocused) {
|
||||
currentChapter = chapter
|
||||
|
|
|
|||
|
|
@ -11,13 +11,16 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.focus.FocusRequester
|
||||
import androidx.compose.ui.focus.focusRequester
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.media3.common.util.UnstableApi
|
||||
import androidx.media3.exoplayer.ExoPlayer
|
||||
import nl.jknaapen.fladder.objects.VideoPlayerObject
|
||||
import nl.jknaapen.fladder.utility.clearSubtitleTrack
|
||||
import nl.jknaapen.fladder.utility.defaultSelected
|
||||
import nl.jknaapen.fladder.utility.conditional
|
||||
import nl.jknaapen.fladder.utility.setInternalSubtitleTrack
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
|
|
@ -30,13 +33,16 @@ fun SubtitlePicker(
|
|||
val subTitles by VideoPlayerObject.subtitleTracks.collectAsState(listOf())
|
||||
val internalSubTracks by VideoPlayerObject.exoSubTracks
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LaunchedEffect(selectedIndex) {
|
||||
LaunchedEffect(selectedIndex, subTitles) {
|
||||
if (selectedIndex == -1) return@LaunchedEffect
|
||||
listState.scrollToItem(
|
||||
subTitles.indexOfFirst { it.index == selectedIndex.toLong() }
|
||||
)
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
|
||||
CustomModalBottomSheet(
|
||||
|
|
@ -54,7 +60,9 @@ fun SubtitlePicker(
|
|||
TrackButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.defaultSelected(selectedOff),
|
||||
.conditional(selectedOff) {
|
||||
focusRequester(focusRequester)
|
||||
},
|
||||
onClick = {
|
||||
VideoPlayerObject.setSubtitleTrackIndex(-1)
|
||||
player.clearSubtitleTrack()
|
||||
|
|
@ -73,7 +81,9 @@ fun SubtitlePicker(
|
|||
TrackButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.defaultSelected(selected),
|
||||
.conditional(selected) {
|
||||
focusRequester(focusRequester)
|
||||
},
|
||||
onClick = {
|
||||
serverSub?.index?.let {
|
||||
VideoPlayerObject.setSubtitleTrackIndex(it.toInt())
|
||||
|
|
|
|||
|
|
@ -11,9 +11,11 @@ fun ScaledContent(
|
|||
content: @Composable () -> Unit
|
||||
) {
|
||||
val density = LocalDensity.current
|
||||
val fontScale = 1f / scale
|
||||
CompositionLocalProvider(
|
||||
LocalDensity provides Density(
|
||||
density = density.density * scale,
|
||||
fontScale = fontScale
|
||||
)
|
||||
) {
|
||||
content()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue