mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 03:48:12 +03:00
Update seek bar tick progress while playing
This commit is contained in:
parent
ac0d823c88
commit
b0a31304a1
4 changed files with 100 additions and 11 deletions
|
@ -18,6 +18,7 @@ package im.vector.app.features.home.room.detail.timeline.factory
|
|||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.core.resources.DrawableProvider
|
||||
import im.vector.app.features.displayname.getBestName
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.AvatarSizeProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.VoiceBroadcastEventsGroup
|
||||
import im.vector.app.features.home.room.detail.timeline.item.AbsMessageItem
|
||||
|
@ -44,6 +45,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
|
|||
private val drawableProvider: DrawableProvider,
|
||||
private val voiceBroadcastRecorder: VoiceBroadcastRecorder?,
|
||||
private val voiceBroadcastPlayer: VoiceBroadcastPlayer,
|
||||
private val playbackTracker: AudioMessagePlaybackTracker,
|
||||
) {
|
||||
|
||||
fun create(
|
||||
|
@ -71,6 +73,7 @@ class VoiceBroadcastItemFactory @Inject constructor(
|
|||
recorderName = params.event.root.stateKey?.let { session.getUserOrDefault(it) }?.toMatrixItem()?.getBestName().orEmpty(),
|
||||
recorder = voiceBroadcastRecorder,
|
||||
player = voiceBroadcastPlayer,
|
||||
playbackTracker = playbackTracker,
|
||||
roomItem = session.getRoom(params.event.roomId)?.roomSummary()?.toMatrixItem(),
|
||||
colorProvider = colorProvider,
|
||||
drawableProvider = drawableProvider,
|
||||
|
|
|
@ -25,6 +25,7 @@ import im.vector.app.R
|
|||
import im.vector.app.core.extensions.tintBackground
|
||||
import im.vector.app.core.resources.ColorProvider
|
||||
import im.vector.app.core.resources.DrawableProvider
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
|
||||
import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer
|
||||
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||
import im.vector.app.features.voicebroadcast.recording.VoiceBroadcastRecorder
|
||||
|
@ -40,6 +41,8 @@ abstract class AbsMessageVoiceBroadcastItem<H : AbsMessageVoiceBroadcastItem.Hol
|
|||
protected val recorderName get() = voiceBroadcastAttributes.recorderName
|
||||
protected val recorder get() = voiceBroadcastAttributes.recorder
|
||||
protected val player get() = voiceBroadcastAttributes.player
|
||||
protected val playbackTracker get() = voiceBroadcastAttributes.playbackTracker
|
||||
protected val duration get() = voiceBroadcastAttributes.duration
|
||||
protected val roomItem get() = voiceBroadcastAttributes.roomItem
|
||||
protected val colorProvider get() = voiceBroadcastAttributes.colorProvider
|
||||
protected val drawableProvider get() = voiceBroadcastAttributes.drawableProvider
|
||||
|
@ -98,6 +101,7 @@ abstract class AbsMessageVoiceBroadcastItem<H : AbsMessageVoiceBroadcastItem.Hol
|
|||
val recorderName: String,
|
||||
val recorder: VoiceBroadcastRecorder?,
|
||||
val player: VoiceBroadcastPlayer,
|
||||
val playbackTracker: AudioMessagePlaybackTracker,
|
||||
val roomItem: MatrixItem?,
|
||||
val colorProvider: ColorProvider,
|
||||
val drawableProvider: DrawableProvider,
|
||||
|
|
|
@ -27,6 +27,7 @@ import com.airbnb.epoxy.EpoxyModelClass
|
|||
import im.vector.app.R
|
||||
import im.vector.app.core.epoxy.onClick
|
||||
import im.vector.app.features.home.room.detail.RoomDetailAction.VoiceBroadcastAction
|
||||
import im.vector.app.features.home.room.detail.timeline.helper.AudioMessagePlaybackTracker
|
||||
import im.vector.app.features.voicebroadcast.listening.VoiceBroadcastPlayer
|
||||
import im.vector.app.features.voicebroadcast.views.VoiceBroadcastMetadataView
|
||||
|
||||
|
@ -34,6 +35,7 @@ import im.vector.app.features.voicebroadcast.views.VoiceBroadcastMetadataView
|
|||
abstract class MessageVoiceBroadcastListeningItem : AbsMessageVoiceBroadcastItem<MessageVoiceBroadcastListeningItem.Holder>() {
|
||||
|
||||
private lateinit var playerListener: VoiceBroadcastPlayer.Listener
|
||||
private var isUserSeeking = false
|
||||
|
||||
override fun bind(holder: Holder) {
|
||||
super.bind(holder)
|
||||
|
@ -86,15 +88,36 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageVoiceBroadcastItem
|
|||
}
|
||||
|
||||
private fun bindSeekBar(holder: Holder) {
|
||||
holder.durationView.text = formatPlaybackTime(voiceBroadcastAttributes.duration)
|
||||
holder.seekBar.max = voiceBroadcastAttributes.duration
|
||||
holder.durationView.text = formatPlaybackTime(duration)
|
||||
holder.seekBar.max = duration
|
||||
holder.seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
||||
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) = Unit
|
||||
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) = Unit
|
||||
override fun onStartTrackingTouch(seekBar: SeekBar) {
|
||||
isUserSeeking = true
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar) {
|
||||
callback?.onTimelineItemAction(VoiceBroadcastAction.Listening.SeekTo(voiceBroadcastId, seekBar.progress))
|
||||
isUserSeeking = false
|
||||
}
|
||||
})
|
||||
playbackTracker.track(voiceBroadcastId, object : AudioMessagePlaybackTracker.Listener {
|
||||
override fun onUpdate(state: AudioMessagePlaybackTracker.Listener.State) {
|
||||
when (state) {
|
||||
is AudioMessagePlaybackTracker.Listener.State.Paused -> {
|
||||
if (!isUserSeeking) {
|
||||
holder.seekBar.progress = state.playbackTime
|
||||
}
|
||||
}
|
||||
is AudioMessagePlaybackTracker.Listener.State.Playing -> {
|
||||
if (!isUserSeeking) {
|
||||
holder.seekBar.progress = state.playbackTime
|
||||
}
|
||||
}
|
||||
AudioMessagePlaybackTracker.Listener.State.Idle -> Unit
|
||||
is AudioMessagePlaybackTracker.Listener.State.Recording -> Unit
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@ -105,6 +128,7 @@ abstract class MessageVoiceBroadcastListeningItem : AbsMessageVoiceBroadcastItem
|
|||
super.unbind(holder)
|
||||
player.removeListener(voiceBroadcastId, playerListener)
|
||||
holder.seekBar.setOnSeekBarChangeListener(null)
|
||||
playbackTracker.untrack(voiceBroadcastId)
|
||||
}
|
||||
|
||||
override fun getViewStubId() = STUB_ID
|
||||
|
|
|
@ -29,6 +29,7 @@ import im.vector.app.features.voicebroadcast.listening.usecase.GetLiveVoiceBroad
|
|||
import im.vector.app.features.voicebroadcast.model.VoiceBroadcastState
|
||||
import im.vector.app.features.voicebroadcast.sequence
|
||||
import im.vector.app.features.voicebroadcast.usecase.GetVoiceBroadcastUseCase
|
||||
import im.vector.lib.core.utils.timer.CountUpTimer
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
|
@ -37,6 +38,7 @@ import kotlinx.coroutines.flow.launchIn
|
|||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.android.sdk.api.extensions.orFalse
|
||||
import org.matrix.android.sdk.api.extensions.tryOrNull
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioContent
|
||||
import org.matrix.android.sdk.api.session.room.model.message.MessageAudioEvent
|
||||
|
@ -60,6 +62,7 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
|
|||
private var voiceBroadcastStateJob: Job? = null
|
||||
|
||||
private val mediaPlayerListener = MediaPlayerListener()
|
||||
private val playbackTicker = PlaybackTicker()
|
||||
|
||||
private var currentMediaPlayer: MediaPlayer? = null
|
||||
private var nextMediaPlayer: MediaPlayer? = null
|
||||
|
@ -79,6 +82,24 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
|
|||
field = value
|
||||
// Notify state change to all the listeners attached to the current voice broadcast id
|
||||
currentVoiceBroadcastId?.let { voiceBroadcastId ->
|
||||
when (value) {
|
||||
State.PLAYING -> {
|
||||
playbackTracker.startPlayback(voiceBroadcastId)
|
||||
playbackTicker.startPlaybackTicker(voiceBroadcastId)
|
||||
}
|
||||
State.PAUSED -> {
|
||||
playbackTracker.pausePlayback(voiceBroadcastId)
|
||||
playbackTicker.stopPlaybackTicker()
|
||||
}
|
||||
State.BUFFERING -> {
|
||||
playbackTracker.pausePlayback(voiceBroadcastId)
|
||||
playbackTicker.stopPlaybackTicker()
|
||||
}
|
||||
State.IDLE -> {
|
||||
playbackTracker.stopPlayback(voiceBroadcastId)
|
||||
playbackTicker.stopPlaybackTicker()
|
||||
}
|
||||
}
|
||||
listeners[voiceBroadcastId]?.forEach { listener -> listener.onStateChanged(value) }
|
||||
}
|
||||
}
|
||||
|
@ -99,15 +120,16 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
|
|||
}
|
||||
|
||||
override fun pause() {
|
||||
currentMediaPlayer?.pause()
|
||||
currentVoiceBroadcastId?.let { playbackTracker.pausePlayback(it) }
|
||||
playingState = State.PAUSED
|
||||
currentMediaPlayer?.pause()
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
// Update state
|
||||
playingState = State.IDLE
|
||||
|
||||
// Stop playback
|
||||
currentMediaPlayer?.stop()
|
||||
currentVoiceBroadcastId?.let { playbackTracker.stopPlayback(it) }
|
||||
isLive = false
|
||||
|
||||
// Release current player
|
||||
|
@ -126,9 +148,6 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
|
|||
fetchPlaylistJob?.cancel()
|
||||
fetchPlaylistJob = null
|
||||
|
||||
// Update state
|
||||
playingState = State.IDLE
|
||||
|
||||
// Clear playlist
|
||||
playlist = emptyList()
|
||||
currentSequence = null
|
||||
|
@ -218,7 +237,6 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
|
|||
if (position > 0) {
|
||||
currentMediaPlayer?.seekTo(position)
|
||||
}
|
||||
currentVoiceBroadcastId?.let { playbackTracker.startPlayback(it) }
|
||||
currentSequence = computedSequence
|
||||
withContext(Dispatchers.Main) { playingState = State.PLAYING }
|
||||
nextMediaPlayer = prepareNextMediaPlayer()
|
||||
|
@ -231,7 +249,6 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
|
|||
|
||||
private fun resumePlayback() {
|
||||
currentMediaPlayer?.start()
|
||||
currentVoiceBroadcastId?.let { playbackTracker.startPlayback(it) }
|
||||
playingState = State.PLAYING
|
||||
}
|
||||
|
||||
|
@ -352,4 +369,45 @@ class VoiceBroadcastPlayerImpl @Inject constructor(
|
|||
private fun getVoiceBroadcastDuration() = playlist.lastOrNull()?.let { it.startTime + it.audioEvent.duration } ?: 0
|
||||
|
||||
private data class PlaylistItem(val audioEvent: MessageAudioEvent, val startTime: Int)
|
||||
|
||||
private inner class PlaybackTicker(
|
||||
private var playbackTicker: CountUpTimer? = null,
|
||||
) {
|
||||
|
||||
fun startPlaybackTicker(id: String) {
|
||||
playbackTicker?.stop()
|
||||
playbackTicker = CountUpTimer().apply {
|
||||
tickListener = object : CountUpTimer.TickListener {
|
||||
override fun onTick(milliseconds: Long) {
|
||||
onPlaybackTick(id)
|
||||
}
|
||||
}
|
||||
resume()
|
||||
}
|
||||
onPlaybackTick(id)
|
||||
}
|
||||
|
||||
private fun onPlaybackTick(id: String) {
|
||||
if (currentMediaPlayer?.isPlaying.orFalse()) {
|
||||
val itemStartPosition = currentSequence?.let { seq -> playlist.find { it.audioEvent.sequence == seq } }?.startTime
|
||||
val currentVoiceBroadcastPosition = itemStartPosition?.plus(currentMediaPlayer?.currentPosition ?: 0)
|
||||
if (currentVoiceBroadcastPosition != null) {
|
||||
val totalDuration = getVoiceBroadcastDuration()
|
||||
val percentage = currentVoiceBroadcastPosition.toFloat() / totalDuration
|
||||
playbackTracker.updatePlayingAtPlaybackTime(id, currentVoiceBroadcastPosition, percentage)
|
||||
} else {
|
||||
playbackTracker.stopPlayback(id)
|
||||
stopPlaybackTicker()
|
||||
}
|
||||
} else {
|
||||
playbackTracker.stopPlayback(id)
|
||||
stopPlaybackTicker()
|
||||
}
|
||||
}
|
||||
|
||||
fun stopPlaybackTicker() {
|
||||
playbackTicker?.stop()
|
||||
playbackTicker = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue