diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt index 1bf952d4a0..01115b7c91 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/audio/MediaAudioView.kt @@ -121,6 +121,7 @@ private fun ExoPlayerMediaAudioView( durationInMillis = 0, canMute = false, isMuted = false, + seekingToMillis = null, ) ) } @@ -171,15 +172,21 @@ private fun ExoPlayerMediaAudioView( LaunchedEffect(exoPlayer.isPlaying) { if (exoPlayer.isPlaying) { while (true) { + val position = exoPlayer.currentPosition + val seekingTo = mediaPlayerControllerState.seekingToMillis mediaPlayerControllerState = mediaPlayerControllerState.copy( - progressInMillis = exoPlayer.currentPosition, + progressInMillis = position, + seekingToMillis = if (seekingTo != null && position >= seekingTo) null else seekingTo, ) delay(200) } } else { // Ensure we render the final state + val position = exoPlayer.currentPosition + val seekingTo = mediaPlayerControllerState.seekingToMillis mediaPlayerControllerState = mediaPlayerControllerState.copy( - progressInMillis = exoPlayer.currentPosition, + progressInMillis = position, + seekingToMillis = if (seekingTo != null && position >= seekingTo) null else seekingTo, ) } } @@ -294,6 +301,9 @@ private fun ExoPlayerMediaAudioView( exoPlayer.togglePlay() }, onSeekChange = { + mediaPlayerControllerState = mediaPlayerControllerState.copy( + seekingToMillis = it.toLong(), + ) exoPlayer.seekToEnsurePlaying(it.toLong()) }, onToggleMute = { diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerState.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerState.kt index b7fe22a27a..79bd7be503 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerState.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerState.kt @@ -18,7 +18,16 @@ data class MediaPlayerControllerState( val durationInMillis: Long, val canMute: Boolean, val isMuted: Boolean, + val seekingToMillis: Long?, ) { + /** + * The progress in milliseconds to display. When [seekingToMillis] is non-null (during a seek operation), + * this returns the target seek position. Once the player catches up to the seek position, + * [seekingToMillis] is cleared (set to null) and this returns [progressInMillis] again. + */ + val displayProgressInMillis: Long + get() = seekingToMillis ?: progressInMillis + @FloatRange(from = 0.0, to = 1.0) - val progressAsFloat = (progressInMillis.toFloat() / durationInMillis.toFloat()).coerceIn(0f, 1f) + val progressAsFloat = (displayProgressInMillis.toFloat() / durationInMillis.toFloat()).coerceIn(0f, 1f) } diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerStateProvider.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerStateProvider.kt index bcdef4713a..55432fb817 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerStateProvider.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerStateProvider.kt @@ -34,6 +34,7 @@ private fun aMediaPlayerControllerState( durationInMillis: Long = 83_000, canMute: Boolean = true, isMuted: Boolean = false, + seekingToMillis: Long? = null, ) = MediaPlayerControllerState( isVisible = isVisible, isPlaying = isPlaying, @@ -42,4 +43,5 @@ private fun aMediaPlayerControllerState( durationInMillis = durationInMillis, canMute = canMute, isMuted = isMuted, + seekingToMillis = seekingToMillis, ) diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt index 7c09c02867..b83c598c10 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/player/MediaPlayerControllerView.kt @@ -126,7 +126,7 @@ fun MediaPlayerControllerView( modifier = Modifier .widthIn(min = 48.dp) .padding(horizontal = 8.dp), - text = state.progressInMillis.toHumanReadableDuration(), + text = state.displayProgressInMillis.toHumanReadableDuration(), textAlign = TextAlign.Center, color = ElementTheme.colors.textPrimary, style = ElementTheme.typography.fontBodyXsMedium, @@ -135,7 +135,9 @@ fun MediaPlayerControllerView( Slider( modifier = Modifier.weight(1f), valueRange = 0f..state.durationInMillis.toFloat(), - value = lastSelectedValue.takeIf { it >= 0 } ?: state.progressInMillis.toFloat(), + value = lastSelectedValue.takeIf { it >= 0 } + ?: state.seekingToMillis?.toFloat() + ?: state.progressInMillis.toFloat(), onValueChange = { lastSelectedValue = it }, diff --git a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt index 65148f37ff..082dc0571c 100644 --- a/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt +++ b/libraries/mediaviewer/impl/src/main/kotlin/io/element/android/libraries/mediaviewer/impl/local/video/MediaVideoView.kt @@ -108,6 +108,7 @@ private fun ExoPlayerMediaVideoView( durationInMillis = 0, canMute = true, isMuted = false, + seekingToMillis = null, ) ) } @@ -225,6 +226,9 @@ private fun ExoPlayerMediaVideoView( }, onSeekChange = { autoHideController++ + mediaPlayerControllerState = mediaPlayerControllerState.copy( + seekingToMillis = it.toLong(), + ) exoPlayer.seekToEnsurePlaying(it.toLong()) }, onToggleMute = { @@ -242,15 +246,21 @@ private fun ExoPlayerMediaVideoView( LaunchedEffect(exoPlayer.isPlaying) { if (exoPlayer.isPlaying) { while (true) { + val position = exoPlayer.currentPosition + val seekingTo = mediaPlayerControllerState.seekingToMillis mediaPlayerControllerState = mediaPlayerControllerState.copy( - progressInMillis = exoPlayer.currentPosition, + progressInMillis = position, + seekingToMillis = if (seekingTo != null && position >= seekingTo) null else seekingTo, ) delay(200) } } else { // Ensure we render the final state + val position = exoPlayer.currentPosition + val seekingTo = mediaPlayerControllerState.seekingToMillis mediaPlayerControllerState = mediaPlayerControllerState.copy( - progressInMillis = exoPlayer.currentPosition, + progressInMillis = position, + seekingToMillis = if (seekingTo != null && position >= seekingTo) null else seekingTo, ) } }