PlayableState.Playable should only contain data useful for other components.

This commit is contained in:
Benoit Marty
2024-11-28 14:38:58 +01:00
parent 2cba5997b7
commit ff3355f453
5 changed files with 42 additions and 48 deletions

View File

@@ -30,6 +30,7 @@ import androidx.compose.material.icons.outlined.GraphicEq
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
@@ -172,17 +173,26 @@ private fun ExoPlayerMediaVideoView(
localMedia: LocalMedia?,
modifier: Modifier = Modifier,
) {
var playableState: PlayableState.Playable by remember {
var mediaPlayerControllerState: MediaPlayerControllerState by remember {
mutableStateOf(
PlayableState.Playable(
MediaPlayerControllerState(
isVisible = false,
isPlaying = false,
progressInMillis = 0,
durationInMillis = 0,
isShowingControls = false,
isMuted = false,
)
)
}
val playableState: PlayableState.Playable by remember {
derivedStateOf {
PlayableState.Playable(
isShowingControls = mediaPlayerControllerState.isVisible,
)
}
}
localMediaViewState.playableState = playableState
val context = LocalContext.current
@@ -195,16 +205,20 @@ private fun ExoPlayerMediaVideoView(
}
override fun onIsPlayingChanged(isPlaying: Boolean) {
playableState = playableState.copy(isPlaying = isPlaying)
mediaPlayerControllerState = mediaPlayerControllerState.copy(
isPlaying = isPlaying,
)
}
override fun onVolumeChanged(volume: Float) {
playableState = playableState.copy(isMuted = volume == 0f)
mediaPlayerControllerState = mediaPlayerControllerState.copy(
isMuted = volume == 0f,
)
}
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
if (reason == Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE) {
playableState = playableState.copy(
mediaPlayerControllerState = mediaPlayerControllerState.copy(
durationInMillis = exoPlayer.duration,
)
}
@@ -221,21 +235,23 @@ private fun ExoPlayerMediaVideoView(
LaunchedEffect(autoHideController) {
delay(5.seconds)
if (exoPlayer.isPlaying) {
playableState = playableState.copy(isShowingControls = false)
mediaPlayerControllerState = mediaPlayerControllerState.copy(
isVisible = false,
)
}
}
LaunchedEffect(exoPlayer.isPlaying) {
if (exoPlayer.isPlaying) {
while (true) {
playableState = playableState.copy(
mediaPlayerControllerState = mediaPlayerControllerState.copy(
progressInMillis = exoPlayer.currentPosition,
)
delay(200)
}
} else {
// Ensure we render the final state
playableState = playableState.copy(
mediaPlayerControllerState = mediaPlayerControllerState.copy(
progressInMillis = exoPlayer.currentPosition,
)
}
@@ -248,7 +264,7 @@ private fun ExoPlayerMediaVideoView(
} else {
exoPlayer.setMediaItems(emptyList())
}
KeepScreenOn(playableState.isPlaying)
KeepScreenOn(mediaPlayerControllerState.isPlaying)
Box(
modifier = modifier
.background(ElementTheme.colors.bgSubtlePrimary)
@@ -263,7 +279,9 @@ private fun ExoPlayerMediaVideoView(
layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
setOnClickListener {
autoHideController++
playableState = playableState.copy(isShowingControls = !playableState.isShowingControls)
mediaPlayerControllerState = mediaPlayerControllerState.copy(
isVisible = !mediaPlayerControllerState.isVisible,
)
}
useController = false
}
@@ -275,10 +293,7 @@ private fun ExoPlayerMediaVideoView(
},
)
MediaPlayerControllerView(
state = MediaPlayerControllerState(
isVisible = playableState.isShowingControls,
playableState = playableState,
),
state = mediaPlayerControllerState,
onTogglePlay = {
autoHideController++
if (exoPlayer.isPlaying) {

View File

@@ -29,11 +29,7 @@ class LocalMediaViewState internal constructor(
sealed interface PlayableState {
data object NotPlayable : PlayableState
data class Playable(
val isPlaying: Boolean,
val progressInMillis: Long,
val durationInMillis: Long,
val isShowingControls: Boolean,
val isMuted: Boolean,
) : PlayableState
}

View File

@@ -7,9 +7,10 @@
package io.element.android.libraries.mediaviewer.api.player
import io.element.android.libraries.mediaviewer.api.local.PlayableState
data class MediaPlayerControllerState(
val isVisible: Boolean,
val playableState: PlayableState.Playable,
val isPlaying: Boolean,
val progressInMillis: Long,
val durationInMillis: Long,
val isMuted: Boolean,
)

View File

@@ -8,7 +8,6 @@
package io.element.android.libraries.mediaviewer.api.player
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
import io.element.android.libraries.mediaviewer.api.local.PlayableState
open class MediaPlayerControllerStateProvider : PreviewParameterProvider<MediaPlayerControllerState> {
override val values: Sequence<MediaPlayerControllerState> = sequenceOf(
@@ -24,32 +23,15 @@ open class MediaPlayerControllerStateProvider : PreviewParameterProvider<MediaPl
private fun aMediaPlayerControllerState(
isVisible: Boolean = true,
isPlaying: Boolean = false,
isPlaying: Boolean = true,
progressInMillis: Long = 0,
// Default to 1 minute and 23 seconds
durationInMillis: Long = 83_000,
isMuted: Boolean = false,
) = MediaPlayerControllerState(
isVisible = isVisible,
playableState = aPlayableState(
isPlaying = isPlaying,
progressInMillis = progressInMillis,
durationInMillis = durationInMillis,
isMuted = isMuted,
),
)
private fun aPlayableState(
isPlaying: Boolean = false,
progressInMillis: Long = 0,
// Default to 1 minute and 23 seconds
durationInMillis: Long = 83_000,
isShowingControls: Boolean = false,
isMuted: Boolean = false,
) = PlayableState.Playable(
isPlaying = isPlaying,
progressInMillis = progressInMillis,
durationInMillis = durationInMillis,
isShowingControls = isShowingControls,
isMuted = isMuted,
)

View File

@@ -66,7 +66,7 @@ fun MediaPlayerControllerView(
IconButton(
onClick = onTogglePlay,
) {
if (state.playableState.isPlaying) {
if (state.isPlaying) {
Icon(
imageVector = CompoundIcons.PauseSolid(),
tint = ElementTheme.colors.iconPrimary,
@@ -84,7 +84,7 @@ fun MediaPlayerControllerView(
modifier = Modifier
.widthIn(min = 48.dp)
.padding(horizontal = 8.dp),
text = state.playableState.progressInMillis.toHumanReadableDuration(),
text = state.progressInMillis.toHumanReadableDuration(),
textAlign = TextAlign.Center,
color = ElementTheme.colors.textPrimary,
style = ElementTheme.typography.fontBodyXsMedium,
@@ -92,8 +92,8 @@ fun MediaPlayerControllerView(
var lastSelectedValue by remember { mutableFloatStateOf(-1f) }
Slider(
modifier = Modifier.weight(1f),
valueRange = 0f..state.playableState.durationInMillis.toFloat(),
value = lastSelectedValue.takeIf { it >= 0 } ?: state.playableState.progressInMillis.toFloat(),
valueRange = 0f..state.durationInMillis.toFloat(),
value = lastSelectedValue.takeIf { it >= 0 } ?: state.progressInMillis.toFloat(),
onValueChange = {
lastSelectedValue = it
},
@@ -103,8 +103,8 @@ fun MediaPlayerControllerView(
},
useCustomLayout = true,
)
val formattedDuration = remember(state.playableState.durationInMillis) {
state.playableState.durationInMillis.toHumanReadableDuration()
val formattedDuration = remember(state.durationInMillis) {
state.durationInMillis.toHumanReadableDuration()
}
Text(
modifier = Modifier
@@ -118,7 +118,7 @@ fun MediaPlayerControllerView(
IconButton(
onClick = onToggleMute,
) {
if (state.playableState.isMuted) {
if (state.isMuted) {
Icon(
imageVector = CompoundIcons.VolumeOffSolid(),
tint = ElementTheme.colors.iconPrimary,