PlayableState.Playable should only contain data useful for other components.
This commit is contained in:
parent
2cba5997b7
commit
ff3355f453
5 changed files with 42 additions and 48 deletions
|
|
@ -30,6 +30,7 @@ import androidx.compose.material.icons.outlined.GraphicEq
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
|
@ -172,17 +173,26 @@ private fun ExoPlayerMediaVideoView(
|
||||||
localMedia: LocalMedia?,
|
localMedia: LocalMedia?,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
) {
|
) {
|
||||||
var playableState: PlayableState.Playable by remember {
|
var mediaPlayerControllerState: MediaPlayerControllerState by remember {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
PlayableState.Playable(
|
MediaPlayerControllerState(
|
||||||
|
isVisible = false,
|
||||||
isPlaying = false,
|
isPlaying = false,
|
||||||
progressInMillis = 0,
|
progressInMillis = 0,
|
||||||
durationInMillis = 0,
|
durationInMillis = 0,
|
||||||
isShowingControls = false,
|
|
||||||
isMuted = false,
|
isMuted = false,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val playableState: PlayableState.Playable by remember {
|
||||||
|
derivedStateOf {
|
||||||
|
PlayableState.Playable(
|
||||||
|
isShowingControls = mediaPlayerControllerState.isVisible,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
localMediaViewState.playableState = playableState
|
localMediaViewState.playableState = playableState
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
@ -195,16 +205,20 @@ private fun ExoPlayerMediaVideoView(
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
override fun onIsPlayingChanged(isPlaying: Boolean) {
|
||||||
playableState = playableState.copy(isPlaying = isPlaying)
|
mediaPlayerControllerState = mediaPlayerControllerState.copy(
|
||||||
|
isPlaying = isPlaying,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onVolumeChanged(volume: Float) {
|
override fun onVolumeChanged(volume: Float) {
|
||||||
playableState = playableState.copy(isMuted = volume == 0f)
|
mediaPlayerControllerState = mediaPlayerControllerState.copy(
|
||||||
|
isMuted = volume == 0f,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
|
override fun onTimelineChanged(timeline: Timeline, reason: Int) {
|
||||||
if (reason == Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE) {
|
if (reason == Player.TIMELINE_CHANGE_REASON_SOURCE_UPDATE) {
|
||||||
playableState = playableState.copy(
|
mediaPlayerControllerState = mediaPlayerControllerState.copy(
|
||||||
durationInMillis = exoPlayer.duration,
|
durationInMillis = exoPlayer.duration,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -221,21 +235,23 @@ private fun ExoPlayerMediaVideoView(
|
||||||
LaunchedEffect(autoHideController) {
|
LaunchedEffect(autoHideController) {
|
||||||
delay(5.seconds)
|
delay(5.seconds)
|
||||||
if (exoPlayer.isPlaying) {
|
if (exoPlayer.isPlaying) {
|
||||||
playableState = playableState.copy(isShowingControls = false)
|
mediaPlayerControllerState = mediaPlayerControllerState.copy(
|
||||||
|
isVisible = false,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(exoPlayer.isPlaying) {
|
LaunchedEffect(exoPlayer.isPlaying) {
|
||||||
if (exoPlayer.isPlaying) {
|
if (exoPlayer.isPlaying) {
|
||||||
while (true) {
|
while (true) {
|
||||||
playableState = playableState.copy(
|
mediaPlayerControllerState = mediaPlayerControllerState.copy(
|
||||||
progressInMillis = exoPlayer.currentPosition,
|
progressInMillis = exoPlayer.currentPosition,
|
||||||
)
|
)
|
||||||
delay(200)
|
delay(200)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Ensure we render the final state
|
// Ensure we render the final state
|
||||||
playableState = playableState.copy(
|
mediaPlayerControllerState = mediaPlayerControllerState.copy(
|
||||||
progressInMillis = exoPlayer.currentPosition,
|
progressInMillis = exoPlayer.currentPosition,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -248,7 +264,7 @@ private fun ExoPlayerMediaVideoView(
|
||||||
} else {
|
} else {
|
||||||
exoPlayer.setMediaItems(emptyList())
|
exoPlayer.setMediaItems(emptyList())
|
||||||
}
|
}
|
||||||
KeepScreenOn(playableState.isPlaying)
|
KeepScreenOn(mediaPlayerControllerState.isPlaying)
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.background(ElementTheme.colors.bgSubtlePrimary)
|
.background(ElementTheme.colors.bgSubtlePrimary)
|
||||||
|
|
@ -263,7 +279,9 @@ private fun ExoPlayerMediaVideoView(
|
||||||
layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
autoHideController++
|
autoHideController++
|
||||||
playableState = playableState.copy(isShowingControls = !playableState.isShowingControls)
|
mediaPlayerControllerState = mediaPlayerControllerState.copy(
|
||||||
|
isVisible = !mediaPlayerControllerState.isVisible,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
useController = false
|
useController = false
|
||||||
}
|
}
|
||||||
|
|
@ -275,10 +293,7 @@ private fun ExoPlayerMediaVideoView(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
MediaPlayerControllerView(
|
MediaPlayerControllerView(
|
||||||
state = MediaPlayerControllerState(
|
state = mediaPlayerControllerState,
|
||||||
isVisible = playableState.isShowingControls,
|
|
||||||
playableState = playableState,
|
|
||||||
),
|
|
||||||
onTogglePlay = {
|
onTogglePlay = {
|
||||||
autoHideController++
|
autoHideController++
|
||||||
if (exoPlayer.isPlaying) {
|
if (exoPlayer.isPlaying) {
|
||||||
|
|
|
||||||
|
|
@ -29,11 +29,7 @@ class LocalMediaViewState internal constructor(
|
||||||
sealed interface PlayableState {
|
sealed interface PlayableState {
|
||||||
data object NotPlayable : PlayableState
|
data object NotPlayable : PlayableState
|
||||||
data class Playable(
|
data class Playable(
|
||||||
val isPlaying: Boolean,
|
|
||||||
val progressInMillis: Long,
|
|
||||||
val durationInMillis: Long,
|
|
||||||
val isShowingControls: Boolean,
|
val isShowingControls: Boolean,
|
||||||
val isMuted: Boolean,
|
|
||||||
) : PlayableState
|
) : PlayableState
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,9 +7,10 @@
|
||||||
|
|
||||||
package io.element.android.libraries.mediaviewer.api.player
|
package io.element.android.libraries.mediaviewer.api.player
|
||||||
|
|
||||||
import io.element.android.libraries.mediaviewer.api.local.PlayableState
|
|
||||||
|
|
||||||
data class MediaPlayerControllerState(
|
data class MediaPlayerControllerState(
|
||||||
val isVisible: Boolean,
|
val isVisible: Boolean,
|
||||||
val playableState: PlayableState.Playable,
|
val isPlaying: Boolean,
|
||||||
|
val progressInMillis: Long,
|
||||||
|
val durationInMillis: Long,
|
||||||
|
val isMuted: Boolean,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,6 @@
|
||||||
package io.element.android.libraries.mediaviewer.api.player
|
package io.element.android.libraries.mediaviewer.api.player
|
||||||
|
|
||||||
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
import androidx.compose.ui.tooling.preview.PreviewParameterProvider
|
||||||
import io.element.android.libraries.mediaviewer.api.local.PlayableState
|
|
||||||
|
|
||||||
open class MediaPlayerControllerStateProvider : PreviewParameterProvider<MediaPlayerControllerState> {
|
open class MediaPlayerControllerStateProvider : PreviewParameterProvider<MediaPlayerControllerState> {
|
||||||
override val values: Sequence<MediaPlayerControllerState> = sequenceOf(
|
override val values: Sequence<MediaPlayerControllerState> = sequenceOf(
|
||||||
|
|
@ -24,32 +23,15 @@ open class MediaPlayerControllerStateProvider : PreviewParameterProvider<MediaPl
|
||||||
|
|
||||||
private fun aMediaPlayerControllerState(
|
private fun aMediaPlayerControllerState(
|
||||||
isVisible: Boolean = true,
|
isVisible: Boolean = true,
|
||||||
isPlaying: Boolean = false,
|
isPlaying: Boolean = true,
|
||||||
progressInMillis: Long = 0,
|
progressInMillis: Long = 0,
|
||||||
// Default to 1 minute and 23 seconds
|
// Default to 1 minute and 23 seconds
|
||||||
durationInMillis: Long = 83_000,
|
durationInMillis: Long = 83_000,
|
||||||
isMuted: Boolean = false,
|
isMuted: Boolean = false,
|
||||||
) = MediaPlayerControllerState(
|
) = MediaPlayerControllerState(
|
||||||
isVisible = isVisible,
|
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,
|
isPlaying = isPlaying,
|
||||||
progressInMillis = progressInMillis,
|
progressInMillis = progressInMillis,
|
||||||
durationInMillis = durationInMillis,
|
durationInMillis = durationInMillis,
|
||||||
isShowingControls = isShowingControls,
|
|
||||||
isMuted = isMuted,
|
isMuted = isMuted,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ fun MediaPlayerControllerView(
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onTogglePlay,
|
onClick = onTogglePlay,
|
||||||
) {
|
) {
|
||||||
if (state.playableState.isPlaying) {
|
if (state.isPlaying) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = CompoundIcons.PauseSolid(),
|
imageVector = CompoundIcons.PauseSolid(),
|
||||||
tint = ElementTheme.colors.iconPrimary,
|
tint = ElementTheme.colors.iconPrimary,
|
||||||
|
|
@ -84,7 +84,7 @@ fun MediaPlayerControllerView(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.widthIn(min = 48.dp)
|
.widthIn(min = 48.dp)
|
||||||
.padding(horizontal = 8.dp),
|
.padding(horizontal = 8.dp),
|
||||||
text = state.playableState.progressInMillis.toHumanReadableDuration(),
|
text = state.progressInMillis.toHumanReadableDuration(),
|
||||||
textAlign = TextAlign.Center,
|
textAlign = TextAlign.Center,
|
||||||
color = ElementTheme.colors.textPrimary,
|
color = ElementTheme.colors.textPrimary,
|
||||||
style = ElementTheme.typography.fontBodyXsMedium,
|
style = ElementTheme.typography.fontBodyXsMedium,
|
||||||
|
|
@ -92,8 +92,8 @@ fun MediaPlayerControllerView(
|
||||||
var lastSelectedValue by remember { mutableFloatStateOf(-1f) }
|
var lastSelectedValue by remember { mutableFloatStateOf(-1f) }
|
||||||
Slider(
|
Slider(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
valueRange = 0f..state.playableState.durationInMillis.toFloat(),
|
valueRange = 0f..state.durationInMillis.toFloat(),
|
||||||
value = lastSelectedValue.takeIf { it >= 0 } ?: state.playableState.progressInMillis.toFloat(),
|
value = lastSelectedValue.takeIf { it >= 0 } ?: state.progressInMillis.toFloat(),
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
lastSelectedValue = it
|
lastSelectedValue = it
|
||||||
},
|
},
|
||||||
|
|
@ -103,8 +103,8 @@ fun MediaPlayerControllerView(
|
||||||
},
|
},
|
||||||
useCustomLayout = true,
|
useCustomLayout = true,
|
||||||
)
|
)
|
||||||
val formattedDuration = remember(state.playableState.durationInMillis) {
|
val formattedDuration = remember(state.durationInMillis) {
|
||||||
state.playableState.durationInMillis.toHumanReadableDuration()
|
state.durationInMillis.toHumanReadableDuration()
|
||||||
}
|
}
|
||||||
Text(
|
Text(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|
@ -118,7 +118,7 @@ fun MediaPlayerControllerView(
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = onToggleMute,
|
onClick = onToggleMute,
|
||||||
) {
|
) {
|
||||||
if (state.playableState.isMuted) {
|
if (state.isMuted) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = CompoundIcons.VolumeOffSolid(),
|
imageVector = CompoundIcons.VolumeOffSolid(),
|
||||||
tint = ElementTheme.colors.iconPrimary,
|
tint = ElementTheme.colors.iconPrimary,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue