mirror of
https://github.com/Novaloop-AG/element-x-android.git
synced 2026-11-03 16:10:00 +00:00
Voice message MediaPlayer: wait until player is ready (#1772)
Change to `MediaPlayer` API to allow waiting for the player to be in a ready state. This is needed in order to perform some tasks (e.g. read the media duration, seek) after changing the media file.
This commit is contained in:
+4
-3
@@ -42,7 +42,7 @@ class VoiceMessageComposerPlayer @Inject constructor(
|
||||
State(
|
||||
isPlaying = state.isPlaying,
|
||||
currentPosition = state.currentPosition,
|
||||
duration = state.duration,
|
||||
duration = state.duration ?: 0L,
|
||||
)
|
||||
}.distinctUntilChanged()
|
||||
|
||||
@@ -52,16 +52,17 @@ class VoiceMessageComposerPlayer @Inject constructor(
|
||||
* @param mediaPath The path to the media to be played.
|
||||
* @param mimeType The mime type of the media file.
|
||||
*/
|
||||
fun play(mediaPath: String, mimeType: String) {
|
||||
suspend fun play(mediaPath: String, mimeType: String) {
|
||||
if (mediaPath == curPlayingMediaId) {
|
||||
mediaPlayer.play()
|
||||
} else {
|
||||
lastPlayedMediaPath = mediaPath
|
||||
mediaPlayer.acquireControlAndPlay(
|
||||
mediaPlayer.setMedia(
|
||||
uri = mediaPath,
|
||||
mediaId = mediaPath,
|
||||
mimeType = mimeType,
|
||||
)
|
||||
mediaPlayer.play()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+6
-4
@@ -120,10 +120,12 @@ class VoiceMessageComposerPresenter @Inject constructor(
|
||||
VoiceMessagePlayerEvent.Play ->
|
||||
when (val recording = recorderState) {
|
||||
is VoiceRecorderState.Finished ->
|
||||
player.play(
|
||||
mediaPath = recording.file.path,
|
||||
mimeType = recording.mimeType,
|
||||
)
|
||||
localCoroutineScope.launch {
|
||||
player.play(
|
||||
mediaPath = recording.file.path,
|
||||
mimeType = recording.mimeType,
|
||||
)
|
||||
}
|
||||
else -> Timber.e("Voice message player event received but no file to play")
|
||||
}
|
||||
VoiceMessagePlayerEvent.Pause -> {
|
||||
|
||||
+2
-1
@@ -151,11 +151,12 @@ class DefaultVoiceMessagePlayer(
|
||||
} else {
|
||||
if (eventId != null) {
|
||||
repo.getMediaFile().mapCatching { mediaFile ->
|
||||
mediaPlayer.acquireControlAndPlay(
|
||||
mediaPlayer.setMedia(
|
||||
uri = mediaFile.path,
|
||||
mediaId = eventId.value,
|
||||
mimeType = "audio/ogg" // Files in the voice cache have no extension so we need to set the mime type manually.
|
||||
)
|
||||
mediaPlayer.play()
|
||||
}
|
||||
} else {
|
||||
Result.failure(IllegalStateException("Cannot play a voice message with no eventId"))
|
||||
|
||||
+12
@@ -195,6 +195,7 @@ class VoiceMessageComposerPresenterTest {
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart))
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd))
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play))
|
||||
awaitItem().apply { assertThat(voiceMessageState).isEqualTo(aLoadedState()) }
|
||||
val finalState = awaitItem().also {
|
||||
assertThat(it.voiceMessageState).isEqualTo(aPlayingState())
|
||||
}
|
||||
@@ -213,6 +214,7 @@ class VoiceMessageComposerPresenterTest {
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart))
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd))
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play))
|
||||
skipItems(1) // Loaded state
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Pause))
|
||||
val finalState = awaitItem().also {
|
||||
assertThat(it.voiceMessageState).isEqualTo(aPausedState())
|
||||
@@ -250,6 +252,7 @@ class VoiceMessageComposerPresenterTest {
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart))
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd))
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play))
|
||||
skipItems(1) // Loaded state
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.DeleteVoiceMessage)
|
||||
awaitItem().apply {
|
||||
assertThat(voiceMessageState).isEqualTo(aPausedState())
|
||||
@@ -321,6 +324,7 @@ class VoiceMessageComposerPresenterTest {
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.PressStart))
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.RecordButtonEvent(PressEvent.LongPressEnd))
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.PlayerEvent(VoiceMessagePlayerEvent.Play))
|
||||
skipItems(1) // Loaded state
|
||||
awaitItem().eventSink(VoiceMessageComposerEvents.SendVoiceMessage)
|
||||
assertThat(awaitItem().voiceMessageState).isEqualTo(aPlayingState().toSendingState())
|
||||
|
||||
@@ -635,6 +639,14 @@ class VoiceMessageComposerPresenterTest {
|
||||
waveform = waveform.toImmutableList(),
|
||||
)
|
||||
|
||||
private fun aLoadedState() =
|
||||
aPreviewState(
|
||||
isPlaying = false,
|
||||
playbackProgress = 0.0f,
|
||||
showCursor = true,
|
||||
time = 0.seconds,
|
||||
)
|
||||
|
||||
private fun aPlayingState() =
|
||||
aPreviewState(
|
||||
isPlaying = true,
|
||||
|
||||
+8
-3
@@ -47,6 +47,11 @@ class DefaultVoiceMessagePlayerTest {
|
||||
player.state.test {
|
||||
skipItems(1) // skip initial state.
|
||||
Truth.assertThat(player.play().isSuccess).isTrue()
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
Truth.assertThat(it.isMyMedia).isEqualTo(true)
|
||||
Truth.assertThat(it.currentPosition).isEqualTo(0)
|
||||
}
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(true)
|
||||
Truth.assertThat(it.isMyMedia).isEqualTo(true)
|
||||
@@ -85,7 +90,7 @@ class DefaultVoiceMessagePlayerTest {
|
||||
player.state.test {
|
||||
skipItems(1) // skip initial state.
|
||||
Truth.assertThat(player.play().isSuccess).isTrue()
|
||||
skipItems(1) // skip play state
|
||||
skipItems(2) // skip play states
|
||||
player.pause()
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(false)
|
||||
@@ -101,7 +106,7 @@ class DefaultVoiceMessagePlayerTest {
|
||||
player.state.test {
|
||||
skipItems(1) // skip initial state.
|
||||
Truth.assertThat(player.play().isSuccess).isTrue()
|
||||
skipItems(1) // skip play state
|
||||
skipItems(2) // skip play states
|
||||
player.pause()
|
||||
skipItems(1)
|
||||
player.play()
|
||||
@@ -119,7 +124,7 @@ class DefaultVoiceMessagePlayerTest {
|
||||
player.state.test {
|
||||
skipItems(1) // skip initial state.
|
||||
Truth.assertThat(player.play().isSuccess).isTrue()
|
||||
skipItems(1) // skip play state
|
||||
skipItems(2) // skip play states
|
||||
player.seekTo(2000)
|
||||
awaitItem().let {
|
||||
Truth.assertThat(it.isPlaying).isEqualTo(true)
|
||||
|
||||
+7
-2
@@ -70,6 +70,11 @@ class VoiceMessagePresenterTest {
|
||||
Truth.assertThat(it.progress).isEqualTo(0f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:02")
|
||||
}
|
||||
awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Downloading)
|
||||
Truth.assertThat(it.progress).isEqualTo(0f)
|
||||
Truth.assertThat(it.time).isEqualTo("0:00")
|
||||
}
|
||||
awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause)
|
||||
Truth.assertThat(it.progress).isEqualTo(0.5f)
|
||||
@@ -128,7 +133,7 @@ class VoiceMessagePresenterTest {
|
||||
}
|
||||
|
||||
initialState.eventSink(VoiceMessageEvents.PlayPause)
|
||||
skipItems(1) // skip downloading state
|
||||
skipItems(2) // skip downloading states
|
||||
|
||||
val playingState = awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause)
|
||||
@@ -177,7 +182,7 @@ class VoiceMessagePresenterTest {
|
||||
|
||||
initialState.eventSink(VoiceMessageEvents.PlayPause)
|
||||
|
||||
skipItems(1) // skip downloading state
|
||||
skipItems(2) // skip downloading states
|
||||
|
||||
awaitItem().also {
|
||||
Truth.assertThat(it.button).isEqualTo(VoiceMessageState.Button.Pause)
|
||||
|
||||
Reference in New Issue
Block a user