Add variable playback speed feature for voice messages
Add playback speed control for voice messages with support for 0.5×, 1×, 1.5×, and 2× playback speeds. The speed button is displayed above the timestamp and cycles through the available speeds when tapped.
This commit is contained in:
parent
1c26c03cde
commit
5e07691fcd
13 changed files with 203 additions and 15 deletions
|
|
@ -79,6 +79,13 @@ interface VoiceMessagePlayer {
|
|||
*/
|
||||
fun seekTo(positionMs: Long)
|
||||
|
||||
/**
|
||||
* Set the playback speed.
|
||||
*
|
||||
* @param speed The playback speed (e.g., 0.5f for half speed, 1.0f for normal, 2.0f for double speed)
|
||||
*/
|
||||
fun setPlaybackSpeed(speed: Float)
|
||||
|
||||
data class State(
|
||||
/**
|
||||
* Whether the player is ready to play.
|
||||
|
|
@ -218,6 +225,10 @@ class Factory(
|
|||
}
|
||||
}
|
||||
|
||||
override fun setPlaybackSpeed(speed: Float) {
|
||||
mediaPlayer.setPlaybackSpeed(speed)
|
||||
}
|
||||
|
||||
private val MediaPlayer.State.isMyTrack: Boolean
|
||||
get() = if (eventId == null) false else this.mediaId == eventId.value
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,9 @@ class VoiceMessagePresenter(
|
|||
private val duration: Duration,
|
||||
) : Presenter<VoiceMessageState> {
|
||||
private val play = mutableStateOf<AsyncData<Unit>>(AsyncData.Uninitialized)
|
||||
private val playbackSpeed = mutableStateOf(1.0f)
|
||||
|
||||
private val availablePlaybackSpeeds = listOf(0.5f, 1.0f, 1.5f, 2.0f)
|
||||
|
||||
@Composable
|
||||
override fun present(): VoiceMessageState {
|
||||
|
|
@ -111,6 +114,13 @@ class VoiceMessagePresenter(
|
|||
is VoiceMessageEvents.Seek -> {
|
||||
player.seekTo((event.percentage * duration).toLong())
|
||||
}
|
||||
is VoiceMessageEvents.ChangePlaybackSpeed -> {
|
||||
val currentIndex = availablePlaybackSpeeds.indexOf(playbackSpeed.value)
|
||||
val nextIndex = (currentIndex + 1) % availablePlaybackSpeeds.size
|
||||
val newSpeed = availablePlaybackSpeeds[nextIndex]
|
||||
playbackSpeed.value = newSpeed
|
||||
player.setPlaybackSpeed(newSpeed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -119,6 +129,7 @@ class VoiceMessagePresenter(
|
|||
progress = progress,
|
||||
time = time,
|
||||
showCursor = showCursor,
|
||||
playbackSpeed = playbackSpeed.value,
|
||||
eventSink = { eventSink(it) },
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -222,6 +222,40 @@ class VoiceMessagePresenterTest {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `changing playback speed cycles through available speeds`() = runTest {
|
||||
val presenter = createVoiceMessagePresenter(
|
||||
duration = 10_000.milliseconds,
|
||||
)
|
||||
moleculeFlow(RecompositionMode.Immediate) {
|
||||
presenter.present()
|
||||
}.test {
|
||||
val initialState = awaitItem().also {
|
||||
assertThat(it.playbackSpeed).isEqualTo(1.0f)
|
||||
}
|
||||
|
||||
initialState.eventSink(VoiceMessageEvents.ChangePlaybackSpeed)
|
||||
awaitItem().also {
|
||||
assertThat(it.playbackSpeed).isEqualTo(1.5f)
|
||||
}
|
||||
|
||||
initialState.eventSink(VoiceMessageEvents.ChangePlaybackSpeed)
|
||||
awaitItem().also {
|
||||
assertThat(it.playbackSpeed).isEqualTo(2.0f)
|
||||
}
|
||||
|
||||
initialState.eventSink(VoiceMessageEvents.ChangePlaybackSpeed)
|
||||
awaitItem().also {
|
||||
assertThat(it.playbackSpeed).isEqualTo(0.5f)
|
||||
}
|
||||
|
||||
initialState.eventSink(VoiceMessageEvents.ChangePlaybackSpeed)
|
||||
awaitItem().also {
|
||||
assertThat(it.playbackSpeed).isEqualTo(1.0f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun TestScope.createVoiceMessagePresenter(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue