mirror of
https://github.com/gabehf/Fladder.git
synced 2026-03-07 21:48:14 -08:00
fix: Lots of small adjustments and changes to native player
This commit is contained in:
parent
360b87dacb
commit
b4e68c9e15
14 changed files with 197 additions and 156 deletions
|
|
@ -62,6 +62,7 @@
|
|||
android:hardwareAccelerated="true"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:windowSoftInputMode="adjustResize"
|
||||
android:launchMode="singleTop"
|
||||
android:exported="true" />
|
||||
|
||||
<service
|
||||
|
|
|
|||
|
|
@ -61,7 +61,11 @@ class MainActivity : AudioServiceFragmentActivity(), NativeVideoActivity {
|
|||
override fun launchActivity(callback: (Result<StartResult>) -> Unit) {
|
||||
try {
|
||||
videoPlayerCallback = callback
|
||||
val intent = Intent(this, VideoPlayerActivity::class.java)
|
||||
|
||||
val intent = Intent(this, VideoPlayerActivity::class.java).apply {
|
||||
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_REORDER_TO_FRONT)
|
||||
}
|
||||
|
||||
videoPlayerLauncher.launch(intent)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
|
|
|
|||
|
|
@ -38,6 +38,11 @@ class VideoPlayerActivity : ComponentActivity() {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
VideoPlayerObject.implementation.pause()
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
|
|
|
|||
|
|
@ -79,7 +79,6 @@ 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
|
||||
|
||||
|
||||
|
|
@ -143,7 +142,8 @@ fun CustomVideoControls(
|
|||
|
||||
// Restart the multiplier
|
||||
LaunchedEffect(lastSeekInteraction.longValue) {
|
||||
delay(2.seconds)
|
||||
delay(1.seconds)
|
||||
if (currentSkipTime == 0L) return@LaunchedEffect
|
||||
player?.seekTo(position + currentSkipTime)
|
||||
currentSkipTime = 0L
|
||||
}
|
||||
|
|
@ -167,18 +167,12 @@ fun CustomVideoControls(
|
|||
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
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ 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.conditional
|
||||
import nl.jknaapen.fladder.utility.setInternalAudioTrack
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
|
|
@ -32,15 +31,31 @@ fun AudioPicker(
|
|||
val audioTracks by VideoPlayerObject.audioTracks.collectAsState(listOf())
|
||||
val internalAudioTracks by VideoPlayerObject.exoAudioTracks
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val focusOffTrack = remember { FocusRequester() }
|
||||
val focusRequesters = remember(internalAudioTracks) {
|
||||
internalAudioTracks.associateWith { FocusRequester() }
|
||||
}
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LaunchedEffect(selectedIndex) {
|
||||
if (selectedIndex == -1) return@LaunchedEffect
|
||||
listState.scrollToItem(
|
||||
audioTracks.indexOfFirst { it.index == selectedIndex.toLong() }
|
||||
)
|
||||
LaunchedEffect(selectedIndex, audioTracks, internalAudioTracks) {
|
||||
if (selectedIndex == -1) {
|
||||
focusOffTrack.requestFocus()
|
||||
return@LaunchedEffect
|
||||
}
|
||||
|
||||
val serverTrackIndex = audioTracks.indexOfFirst { it.index == selectedIndex.toLong() }
|
||||
|
||||
if (serverTrackIndex <= 0) {
|
||||
focusOffTrack.requestFocus()
|
||||
return@LaunchedEffect
|
||||
}
|
||||
|
||||
val internalIndex = serverTrackIndex - 1
|
||||
val lazyColumnIndex = internalIndex + 1
|
||||
|
||||
listState.scrollToItem(lazyColumnIndex)
|
||||
focusRequesters[internalAudioTracks[internalIndex]]?.requestFocus()
|
||||
}
|
||||
|
||||
CustomModalBottomSheet(
|
||||
|
|
@ -54,45 +69,37 @@ fun AudioPicker(
|
|||
.padding(horizontal = 8.dp, vertical = 16.dp),
|
||||
) {
|
||||
item {
|
||||
val selectedOff = -1 == selectedIndex
|
||||
val selectedOff = selectedIndex == -1
|
||||
TrackButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.conditional(selectedOff) {
|
||||
focusRequester(focusRequester)
|
||||
},
|
||||
.focusRequester(focusOffTrack),
|
||||
onClick = {
|
||||
VideoPlayerObject.setAudioTrackIndex(-1)
|
||||
player.clearAudioTrack()
|
||||
},
|
||||
selected = selectedOff
|
||||
) {
|
||||
Text(
|
||||
text = "Off",
|
||||
)
|
||||
Text("Off")
|
||||
}
|
||||
}
|
||||
|
||||
internalAudioTracks.forEachIndexed { index, track ->
|
||||
val serverTrack = audioTracks.elementAtOrNull(index + 1)
|
||||
val selected = serverTrack?.index == selectedIndex.toLong()
|
||||
val selected = serverTrack?.index?.toInt() == selectedIndex
|
||||
|
||||
item {
|
||||
TrackButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.conditional(selected) {
|
||||
focusRequester(focusRequester)
|
||||
},
|
||||
.focusRequester(focusRequesters[track]!!),
|
||||
onClick = {
|
||||
serverTrack?.index?.let {
|
||||
VideoPlayerObject.setAudioTrackIndex(it.toInt())
|
||||
}
|
||||
serverTrack?.index?.let { VideoPlayerObject.setAudioTrackIndex(it.toInt()) }
|
||||
player.setInternalAudioTrack(track)
|
||||
},
|
||||
selected = selected
|
||||
) {
|
||||
Text(
|
||||
text = serverTrack?.name ?: "",
|
||||
)
|
||||
Text(serverTrack?.name ?: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,18 +2,20 @@ package nl.jknaapen.fladder.composables.dialogs
|
|||
|
||||
import Chapter
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
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.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.lazy.LazyRow
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
|
|
@ -22,20 +24,18 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
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.layout.ContentScale
|
||||
import androidx.compose.ui.unit.dp
|
||||
import coil3.compose.AsyncImage
|
||||
import nl.jknaapen.fladder.objects.VideoPlayerObject
|
||||
import nl.jknaapen.fladder.utility.conditional
|
||||
import nl.jknaapen.fladder.utility.highlightOnFocus
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
|
@ -47,27 +47,23 @@ internal fun ChapterSelectionSheet(
|
|||
val chapters = playbackData?.chapters ?: listOf()
|
||||
val currentPosition by VideoPlayerObject.position.collectAsState(0L)
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val focusRequesters = remember(chapters) {
|
||||
chapters.associateWith { FocusRequester() }
|
||||
}
|
||||
|
||||
if (chapters.isEmpty()) return
|
||||
|
||||
var currentChapter: Chapter? by remember {
|
||||
mutableStateOf(
|
||||
chapters[chapters.indexOfCurrent(
|
||||
currentPosition
|
||||
)]
|
||||
)
|
||||
}
|
||||
|
||||
val lazyListState = rememberLazyListState()
|
||||
|
||||
LaunchedEffect(chapters, currentPosition) {
|
||||
val chapter = chapters.indexOfCurrent(currentPosition)
|
||||
lazyListState.animateScrollToItem(
|
||||
chapter
|
||||
)
|
||||
currentChapter = chapters[chapter]
|
||||
focusRequester.requestFocus()
|
||||
val currentChapterIndex = remember(currentPosition) {
|
||||
chapters.indexOfCurrent(currentPosition)
|
||||
}
|
||||
|
||||
val currentChapter = chapters.getOrNull(currentChapterIndex)
|
||||
|
||||
LaunchedEffect(currentChapter) {
|
||||
lazyListState.animateScrollToItem(chapters.indexOf(currentChapter))
|
||||
focusRequesters[currentChapter]?.requestFocus()
|
||||
}
|
||||
|
||||
CustomModalBottomSheet(
|
||||
|
|
@ -76,6 +72,8 @@ internal fun ChapterSelectionSheet(
|
|||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.fillMaxHeight(0.55f)
|
||||
.wrapContentHeight()
|
||||
.padding(horizontal = 16.dp, vertical = 16.dp)
|
||||
.wrapContentHeight(),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
|
|
@ -90,36 +88,11 @@ internal fun ChapterSelectionSheet(
|
|||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
chapters.forEachIndexed { index, chapter ->
|
||||
val selectedChapter = currentChapter == chapter
|
||||
val isCurrentChapter = chapters.indexOfCurrent(currentPosition) == index
|
||||
item {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(
|
||||
color = if (selectedChapter) Color.White.copy(alpha = 0.25f) else Color.Black.copy(
|
||||
alpha = 0.75f
|
||||
),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
.aspectRatio(1.67f)
|
||||
.border(
|
||||
width = 2.dp,
|
||||
color = Color.White.copy(alpha = if (selectedChapter) 0.45f else 0f),
|
||||
shape = RoundedCornerShape(8.dp)
|
||||
)
|
||||
.conditional(selectedChapter) {
|
||||
focusRequester(focusRequester)
|
||||
}
|
||||
.onFocusChanged {
|
||||
if (it.isFocused) {
|
||||
currentChapter = chapter
|
||||
}
|
||||
}
|
||||
.clickable(
|
||||
onClick = {
|
||||
onSelected(chapter)
|
||||
}
|
||||
)
|
||||
|
||||
.padding(horizontal = 8.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(
|
||||
|
|
@ -127,33 +100,48 @@ internal fun ChapterSelectionSheet(
|
|||
alignment = Alignment.CenterVertically
|
||||
),
|
||||
) {
|
||||
AsyncImage(
|
||||
model = chapter.url,
|
||||
modifier = Modifier
|
||||
.focusRequester(focusRequesters[chapter]!!)
|
||||
.highlightOnFocus(
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
width = 3.dp,
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
)
|
||||
.clickable(
|
||||
onClick = {
|
||||
onSelected(chapter)
|
||||
}
|
||||
)
|
||||
.aspectRatio(1.67f)
|
||||
.clip(shape = RoundedCornerShape(24.dp))
|
||||
.weight(1f),
|
||||
contentDescription = "",
|
||||
contentScale = ContentScale.FillBounds
|
||||
)
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(
|
||||
8.dp,
|
||||
alignment = Alignment.CenterHorizontally
|
||||
alignment = Alignment.Start
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
if (isCurrentChapter)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(16.dp)
|
||||
.background(
|
||||
color = MaterialTheme.colorScheme.primary,
|
||||
shape = CircleShape
|
||||
)
|
||||
)
|
||||
Text(
|
||||
chapter.name,
|
||||
style = MaterialTheme.typography.bodyLarge,
|
||||
color = Color.White
|
||||
)
|
||||
}
|
||||
AsyncImage(
|
||||
model = chapter.url,
|
||||
modifier = Modifier
|
||||
.clip(
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
)
|
||||
.heightIn(min = 125.dp, max = 150.dp)
|
||||
.border(
|
||||
width = 2.dp,
|
||||
color = Color.White.copy(alpha = if (isCurrentChapter) 1f else 0f),
|
||||
shape = RoundedCornerShape(24.dp)
|
||||
),
|
||||
contentDescription = ""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -163,9 +151,13 @@ internal fun ChapterSelectionSheet(
|
|||
}
|
||||
|
||||
private fun List<Chapter>.indexOfCurrent(currentPosition: Long): Int {
|
||||
return this.indexOfFirst { chapter ->
|
||||
val nextChapterTime =
|
||||
this.getOrNull(this.indexOf(chapter) + 1)?.time ?: Long.MAX_VALUE
|
||||
currentPosition >= chapter.time && currentPosition < nextChapterTime
|
||||
if (isEmpty()) return 0
|
||||
|
||||
for (i in indices) {
|
||||
val chapter = this[i]
|
||||
val nextTime = getOrNull(i + 1)?.time ?: Long.MAX_VALUE
|
||||
if (currentPosition in chapter.time until nextTime) return i
|
||||
}
|
||||
|
||||
return if (currentPosition < first().time) 0 else lastIndex
|
||||
}
|
||||
|
|
@ -6,10 +6,10 @@ import androidx.compose.foundation.layout.WindowInsets
|
|||
import androidx.compose.foundation.layout.displayCutoutPadding
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.wrapContentHeight
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.SheetValue
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
|
@ -29,7 +29,11 @@ internal fun CustomModalBottomSheet(
|
|||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val modalBottomSheetState = rememberModalBottomSheetState(
|
||||
skipPartiallyExpanded = true
|
||||
skipPartiallyExpanded = true,
|
||||
confirmValueChange = { newValue ->
|
||||
newValue == SheetValue.Expanded ||
|
||||
newValue == SheetValue.Hidden
|
||||
}
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
|
|
@ -47,7 +51,6 @@ internal fun CustomModalBottomSheet(
|
|||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.displayCutoutPadding()
|
||||
.background(
|
||||
shape = RoundedCornerShape(16.dp),
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ 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.conditional
|
||||
import nl.jknaapen.fladder.utility.setInternalSubtitleTrack
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
|
|
@ -33,16 +32,27 @@ fun SubtitlePicker(
|
|||
val subTitles by VideoPlayerObject.subtitleTracks.collectAsState(listOf())
|
||||
val internalSubTracks by VideoPlayerObject.exoSubTracks
|
||||
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val focusOffTrack = remember { FocusRequester() }
|
||||
|
||||
val focusRequesters = remember(internalSubTracks) {
|
||||
internalSubTracks.associateWith { FocusRequester() }
|
||||
}
|
||||
|
||||
val listState = rememberLazyListState()
|
||||
|
||||
LaunchedEffect(selectedIndex, subTitles) {
|
||||
if (selectedIndex == -1) return@LaunchedEffect
|
||||
listState.scrollToItem(
|
||||
subTitles.indexOfFirst { it.index == selectedIndex.toLong() }
|
||||
)
|
||||
focusRequester.requestFocus()
|
||||
LaunchedEffect(selectedIndex, subTitles, internalSubTracks) {
|
||||
val serverSubIndex = subTitles.indexOfFirst { it.index == selectedIndex.toLong() }
|
||||
|
||||
if (serverSubIndex <= 0) {
|
||||
focusOffTrack.requestFocus()
|
||||
return@LaunchedEffect
|
||||
}
|
||||
|
||||
val internalIndex = serverSubIndex - 1
|
||||
val lazyColumnIndex = internalIndex + 1
|
||||
|
||||
listState.scrollToItem(lazyColumnIndex)
|
||||
focusRequesters[internalSubTracks[internalIndex]]?.requestFocus()
|
||||
}
|
||||
|
||||
CustomModalBottomSheet(
|
||||
|
|
@ -60,9 +70,7 @@ fun SubtitlePicker(
|
|||
TrackButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.conditional(selectedOff) {
|
||||
focusRequester(focusRequester)
|
||||
},
|
||||
.focusRequester(focusOffTrack),
|
||||
onClick = {
|
||||
VideoPlayerObject.setSubtitleTrackIndex(-1)
|
||||
player.clearSubtitleTrack()
|
||||
|
|
@ -81,9 +89,7 @@ fun SubtitlePicker(
|
|||
TrackButton(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.conditional(selected) {
|
||||
focusRequester(focusRequester)
|
||||
},
|
||||
.focusRequester(focusRequesters[subtitle]!!),
|
||||
onClick = {
|
||||
serverSub?.index?.let {
|
||||
VideoPlayerObject.setSubtitleTrackIndex(it.toInt())
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ import androidx.compose.ui.unit.dp
|
|||
import io.github.rabehx.iconsax.Iconsax
|
||||
import io.github.rabehx.iconsax.filled.TickSquare
|
||||
import nl.jknaapen.fladder.composables.controls.CustomButton
|
||||
import nl.jknaapen.fladder.utility.defaultSelected
|
||||
|
||||
@Composable
|
||||
internal fun TrackButton(
|
||||
|
|
@ -33,8 +32,7 @@ internal fun TrackButton(
|
|||
backgroundColor = Color.White.copy(alpha = 0.25f),
|
||||
modifier = modifier
|
||||
.padding(vertical = 6.dp, horizontal = 12.dp)
|
||||
.defaultMinSize(minHeight = 40.dp)
|
||||
.defaultSelected(selected),
|
||||
.defaultMinSize(minHeight = 40.dp),
|
||||
onClick = onClick,
|
||||
) {
|
||||
Row(
|
||||
|
|
|
|||
|
|
@ -164,10 +164,16 @@ internal fun NextUpOverlay(
|
|||
)
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
"Next-up in $timeUntilNextVideo seconds",
|
||||
style = MaterialTheme.typography.titleLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
Box(
|
||||
|
|
@ -181,6 +187,7 @@ internal fun NextUpOverlay(
|
|||
)
|
||||
)
|
||||
MediaInfo()
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
|
|
|
|||
|
|
@ -61,7 +61,6 @@ class VideoPlayerImplementation(
|
|||
)
|
||||
.build()
|
||||
|
||||
|
||||
player?.stop()
|
||||
player?.clearMediaItems()
|
||||
player?.setMediaItem(mediaItem)
|
||||
|
|
|
|||
|
|
@ -58,8 +58,6 @@ internal fun ExoPlayer(
|
|||
val videoHost = VideoPlayerObject
|
||||
val context = LocalContext.current
|
||||
|
||||
var initialized = false
|
||||
|
||||
val extractorsFactory = DefaultExtractorsFactory().apply {
|
||||
val isLowRamDevice = context.getSystemService<ActivityManager>()?.isLowRamDevice == true
|
||||
setTsExtractorTimestampSearchBytes(
|
||||
|
|
@ -167,13 +165,17 @@ internal fun ExoPlayer(
|
|||
|
||||
override fun onTracksChanged(tracks: Tracks) {
|
||||
super.onTracksChanged(tracks)
|
||||
if (!initialized) {
|
||||
initialized = true
|
||||
val subTracks = exoPlayer.getSubtitleTracks()
|
||||
val audioTracks = exoPlayer.getAudioTracks()
|
||||
|
||||
if (subTracks.isEmpty() && audioTracks.isEmpty()) return
|
||||
|
||||
if (subTracks != VideoPlayerObject.exoSubTracks.value || audioTracks != VideoPlayerObject.exoAudioTracks.value) {
|
||||
VideoPlayerObject.implementation.playbackData.value?.let {
|
||||
exoPlayer.properlySetSubAndAudioTracks(it)
|
||||
}
|
||||
VideoPlayerObject.exoSubTracks.value = exoPlayer.getSubtitleTracks()
|
||||
VideoPlayerObject.exoAudioTracks.value = exoPlayer.getAudioTracks()
|
||||
VideoPlayerObject.exoSubTracks.value = subTracks
|
||||
VideoPlayerObject.exoAudioTracks.value = audioTracks
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package nl.jknaapen.fladder.utility
|
|||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.border
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
|
|
@ -13,13 +14,13 @@ import androidx.compose.runtime.setValue
|
|||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
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.graphics.Shape
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
|
|
@ -42,7 +43,7 @@ fun Modifier.highlightOnFocus(
|
|||
)
|
||||
.border(
|
||||
width = width,
|
||||
color = color.copy(alpha = 0.5f),
|
||||
color = color.copy(alpha = 0.8f),
|
||||
)
|
||||
} else
|
||||
if (width != 0.dp) {
|
||||
|
|
@ -54,7 +55,7 @@ fun Modifier.highlightOnFocus(
|
|||
)
|
||||
.border(
|
||||
width = width,
|
||||
color = color.copy(alpha = 0.5f),
|
||||
color = color.copy(alpha = 0.8f),
|
||||
shape = shape
|
||||
)
|
||||
} else {
|
||||
|
|
@ -104,12 +105,23 @@ fun Modifier.conditional(condition: Boolean, modifier: Modifier.() -> Modifier):
|
|||
fun Modifier.visible(
|
||||
visible: Boolean,
|
||||
): Modifier {
|
||||
val alphaAnimated by animateFloatAsState(if (visible) 1f else 0f)
|
||||
val alphaAnimated by animateFloatAsState(
|
||||
targetValue = if (visible) 1f else 0f,
|
||||
label = "AlphaAnimation"
|
||||
)
|
||||
|
||||
return this
|
||||
.graphicsLayer {
|
||||
alpha = alphaAnimated
|
||||
}
|
||||
.then(
|
||||
if (!visible) Modifier.pointerInput(Unit) {} else Modifier
|
||||
if (!visible) {
|
||||
//Collapse composable to disable input blocking
|
||||
Modifier
|
||||
.size(0.dp)
|
||||
.clipToBounds()
|
||||
} else {
|
||||
Modifier
|
||||
}
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,6 +56,7 @@ fun ExoPlayer.setInternalAudioTrack(audioTrack: InternalTrack) {
|
|||
selector.setParameters(
|
||||
selector.buildUponParameters()
|
||||
.setRendererDisabled(audioTrack.rendererIndex, false)
|
||||
.setTrackTypeDisabled(C.TRACK_TYPE_AUDIO, false)
|
||||
.build()
|
||||
)
|
||||
|
||||
|
|
@ -74,8 +75,12 @@ fun ExoPlayer.clearAudioTrack(disable: Boolean = true) {
|
|||
selector.setParameters(
|
||||
selector.buildUponParameters()
|
||||
.setRendererDisabled(C.TRACK_TYPE_AUDIO, disable)
|
||||
.setTrackTypeDisabled(C.TRACK_TYPE_AUDIO, disable)
|
||||
.build()
|
||||
)
|
||||
|
||||
this.trackSelectionParameters = selector.parameters.buildUpon()
|
||||
.build()
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
|
|
@ -110,21 +115,27 @@ fun ExoPlayer.getSubtitleTracks(): List<InternalTrack> {
|
|||
fun ExoPlayer.clearSubtitleTrack() {
|
||||
val selector = trackSelector as? DefaultTrackSelector ?: return
|
||||
val newParams = selector.buildUponParameters()
|
||||
.setRendererDisabled(C.TRACK_TYPE_TEXT, false) // keep text renderer active
|
||||
.setPreferredTextLanguage(null) // don't auto-pick a language
|
||||
.setTrackTypeDisabled(C.TRACK_TYPE_TEXT, true) // <– disables selection of *any* text track
|
||||
.setRendererDisabled(C.TRACK_TYPE_TEXT, false)
|
||||
.setPreferredTextLanguage(null)
|
||||
.setTrackTypeDisabled(C.TRACK_TYPE_TEXT, true)
|
||||
.build()
|
||||
selector.setParameters(newParams)
|
||||
|
||||
this.trackSelectionParameters = selector.parameters.buildUpon()
|
||||
.build()
|
||||
}
|
||||
|
||||
@OptIn(UnstableApi::class)
|
||||
fun ExoPlayer.enableSubtitles(language: String? = null) {
|
||||
val selector = trackSelector as? DefaultTrackSelector ?: return
|
||||
val newParams = selector.buildUponParameters()
|
||||
.setTrackTypeDisabled(C.TRACK_TYPE_TEXT, false) // allow text again
|
||||
.setPreferredTextLanguage(language) // optional: auto-pick by language
|
||||
.setTrackTypeDisabled(C.TRACK_TYPE_TEXT, false)
|
||||
.setPreferredTextLanguage(language)
|
||||
.build()
|
||||
selector.setParameters(newParams)
|
||||
|
||||
this.trackSelectionParameters = selector.parameters.buildUpon()
|
||||
.build()
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue