mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 13:38:49 +03:00
Support scrolling playback on timeline.
This commit is contained in:
parent
243a714586
commit
4254f46065
7 changed files with 71 additions and 1 deletions
|
@ -2051,6 +2051,14 @@ class TimelineFragment @Inject constructor(
|
|||
messageComposerViewModel.handle(MessageComposerAction.PlayOrPauseVoicePlayback(eventId, messageAudioContent))
|
||||
}
|
||||
|
||||
override fun onVoiceWaveformTouchedUp(eventId: String, messageAudioContent: MessageAudioContent, percentage: Float) {
|
||||
messageComposerViewModel.handle(MessageComposerAction.VoiceWaveformTouchedUp(eventId, messageAudioContent, percentage))
|
||||
}
|
||||
|
||||
override fun onVoiceWaveformMovedTo(eventId: String, messageAudioContent: MessageAudioContent, percentage: Float) {
|
||||
messageComposerViewModel.handle(MessageComposerAction.VoiceWaveformMovedTo(eventId, messageAudioContent, percentage))
|
||||
}
|
||||
|
||||
private fun onShareActionClicked(action: EventSharedAction.Share) {
|
||||
when (action.messageContent) {
|
||||
is MessageTextContent -> shareText(requireContext(), action.messageContent.body)
|
||||
|
|
|
@ -40,4 +40,6 @@ sealed class MessageComposerAction : VectorViewModelAction {
|
|||
data class PlayOrPauseVoicePlayback(val eventId: String, val messageAudioContent: MessageAudioContent) : MessageComposerAction()
|
||||
object PlayOrPauseRecordingPlayback : MessageComposerAction()
|
||||
data class EndAllVoiceActions(val deleteRecord: Boolean = true) : MessageComposerAction()
|
||||
data class VoiceWaveformTouchedUp(val eventId: String, val messageAudioContent: MessageAudioContent, val percentage: Float) : MessageComposerAction()
|
||||
data class VoiceWaveformMovedTo(val eventId: String, val messageAudioContent: MessageAudioContent, val percentage: Float) : MessageComposerAction()
|
||||
}
|
||||
|
|
|
@ -108,7 +108,9 @@ class MessageComposerViewModel @AssistedInject constructor(
|
|||
is MessageComposerAction.EndAllVoiceActions -> handleEndAllVoiceActions(action.deleteRecord)
|
||||
is MessageComposerAction.InitializeVoiceRecorder -> handleInitializeVoiceRecorder(action.attachmentData)
|
||||
is MessageComposerAction.OnEntersBackground -> handleEntersBackground(action.composerText)
|
||||
}
|
||||
is MessageComposerAction.VoiceWaveformTouchedUp -> handleVoiceWaveformTouchedUp(action)
|
||||
is MessageComposerAction.VoiceWaveformMovedTo -> handleVoiceWaveformMovedTo(action)
|
||||
}.exhaustive
|
||||
}
|
||||
|
||||
private fun handleOnVoiceRecordingUiStateChanged(action: MessageComposerAction.OnVoiceRecordingUiStateChanged) = setState {
|
||||
|
@ -861,6 +863,18 @@ class MessageComposerViewModel @AssistedInject constructor(
|
|||
voiceMessageHelper.pauseRecording()
|
||||
}
|
||||
|
||||
private fun handleVoiceWaveformTouchedUp(action: MessageComposerAction.VoiceWaveformTouchedUp) {
|
||||
val duration = (action.messageAudioContent.audioInfo?.duration ?: 0)
|
||||
val toMillisecond = (action.percentage * duration).toInt()
|
||||
voiceMessageHelper.movePlaybackTo(action.eventId, toMillisecond, duration)
|
||||
}
|
||||
|
||||
private fun handleVoiceWaveformMovedTo(action: MessageComposerAction.VoiceWaveformMovedTo) {
|
||||
val duration = (action.messageAudioContent.audioInfo?.duration ?: 0)
|
||||
val toMillisecond = (action.percentage * duration).toInt()
|
||||
voiceMessageHelper.movePlaybackTo(action.eventId, toMillisecond, duration)
|
||||
}
|
||||
|
||||
private fun handleEntersBackground(composerText: String) {
|
||||
val isVoiceRecording = com.airbnb.mvrx.withState(this) { it.isVoiceRecording }
|
||||
if (isVoiceRecording) {
|
||||
|
|
|
@ -174,6 +174,14 @@ class VoiceMessageHelper @Inject constructor(
|
|||
stopPlaybackTicker()
|
||||
}
|
||||
|
||||
fun movePlaybackTo(id: String, toMillisecond: Int, totalDuration: Int) {
|
||||
val percentage = toMillisecond.toFloat() / totalDuration
|
||||
playbackTracker.updateCurrentPlaybackTime(id, toMillisecond, percentage)
|
||||
|
||||
stopPlayback()
|
||||
playbackTracker.pausePlayback(id)
|
||||
}
|
||||
|
||||
private fun startRecordingAmplitudes() {
|
||||
amplitudeTicker?.stop()
|
||||
amplitudeTicker = CountUpTimer(50).apply {
|
||||
|
|
|
@ -138,6 +138,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
|||
fun getPreviewUrlRetriever(): PreviewUrlRetriever
|
||||
|
||||
fun onVoiceControlButtonClicked(eventId: String, messageAudioContent: MessageAudioContent)
|
||||
fun onVoiceWaveformTouchedUp(eventId: String, messageAudioContent: MessageAudioContent, percentage: Float)
|
||||
fun onVoiceWaveformMovedTo(eventId: String, messageAudioContent: MessageAudioContent, percentage: Float)
|
||||
}
|
||||
|
||||
interface ReactionPillCallback {
|
||||
|
|
|
@ -357,11 +357,22 @@ class MessageItemFactory @Inject constructor(
|
|||
}
|
||||
}
|
||||
|
||||
val waveformTouchListener: MessageVoiceItem.WaveformTouchListener = object : MessageVoiceItem.WaveformTouchListener {
|
||||
override fun onWaveformTouchedUp(percentage: Float) {
|
||||
params.callback?.onVoiceWaveformTouchedUp(informationData.eventId, messageContent, percentage)
|
||||
}
|
||||
|
||||
override fun onWaveformMovedTo(percentage: Float) {
|
||||
params.callback?.onVoiceWaveformMovedTo(informationData.eventId, messageContent, percentage)
|
||||
}
|
||||
}
|
||||
|
||||
return MessageVoiceItem_()
|
||||
.attributes(attributes)
|
||||
.duration(messageContent.audioWaveformInfo?.duration ?: 0)
|
||||
.waveform(messageContent.audioWaveformInfo?.waveform?.toFft().orEmpty())
|
||||
.playbackControlButtonClickListener(playbackControlButtonClickListener)
|
||||
.waveformTouchListener(waveformTouchListener)
|
||||
.voiceMessagePlaybackTracker(voiceMessagePlaybackTracker)
|
||||
.izLocalFile(localFilesHelper.isLocalFile(fileUrl))
|
||||
.izDownloaded(session.fileService().isFileInCache(
|
||||
|
|
|
@ -19,6 +19,7 @@ package im.vector.app.features.home.room.detail.timeline.item
|
|||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.text.format.DateUtils
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageButton
|
||||
|
@ -38,6 +39,11 @@ import im.vector.app.features.voice.AudioWaveformView
|
|||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||
abstract class MessageVoiceItem : AbsMessageItem<MessageVoiceItem.Holder>() {
|
||||
|
||||
interface WaveformTouchListener {
|
||||
fun onWaveformTouchedUp(percentage: Float)
|
||||
fun onWaveformMovedTo(percentage: Float)
|
||||
}
|
||||
|
||||
@EpoxyAttribute
|
||||
var mxcUrl: String = ""
|
||||
|
||||
|
@ -62,6 +68,9 @@ abstract class MessageVoiceItem : AbsMessageItem<MessageVoiceItem.Holder>() {
|
|||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
var playbackControlButtonClickListener: ClickListener? = null
|
||||
|
||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||
var waveformTouchListener: WaveformTouchListener? = null
|
||||
|
||||
@EpoxyAttribute
|
||||
lateinit var voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker
|
||||
|
||||
|
@ -87,6 +96,20 @@ abstract class MessageVoiceItem : AbsMessageItem<MessageVoiceItem.Holder>() {
|
|||
holder.voicePlaybackWaveform.add(AudioWaveformView.FFT(amplitude.toFloat(), waveformColorIdle))
|
||||
}
|
||||
holder.voicePlaybackWaveform.summarize()
|
||||
|
||||
holder.voicePlaybackWaveform.setOnTouchListener { view, motionEvent ->
|
||||
when (motionEvent.action) {
|
||||
MotionEvent.ACTION_UP -> {
|
||||
val percentage = getTouchedPositionPercentage(motionEvent, view)
|
||||
waveformTouchListener?.onWaveformTouchedUp(percentage)
|
||||
}
|
||||
MotionEvent.ACTION_MOVE -> {
|
||||
val percentage = getTouchedPositionPercentage(motionEvent, view)
|
||||
waveformTouchListener?.onWaveformMovedTo(percentage)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
val backgroundTint = if (attributes.informationData.messageLayout is TimelineMessageLayout.Bubble) {
|
||||
|
@ -111,6 +134,8 @@ abstract class MessageVoiceItem : AbsMessageItem<MessageVoiceItem.Holder>() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getTouchedPositionPercentage(motionEvent: MotionEvent, view: View) = motionEvent.x / view.width
|
||||
|
||||
private fun renderIdleState(holder: Holder, idleColor: Int, playedColor: Int) {
|
||||
holder.voicePlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play)
|
||||
holder.voicePlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_play_voice_message)
|
||||
|
|
Loading…
Reference in a new issue