mirror of
https://github.com/SchildiChat/SchildiChat-android.git
synced 2025-02-17 04:20:00 +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))
|
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) {
|
private fun onShareActionClicked(action: EventSharedAction.Share) {
|
||||||
when (action.messageContent) {
|
when (action.messageContent) {
|
||||||
is MessageTextContent -> shareText(requireContext(), action.messageContent.body)
|
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()
|
data class PlayOrPauseVoicePlayback(val eventId: String, val messageAudioContent: MessageAudioContent) : MessageComposerAction()
|
||||||
object PlayOrPauseRecordingPlayback : MessageComposerAction()
|
object PlayOrPauseRecordingPlayback : MessageComposerAction()
|
||||||
data class EndAllVoiceActions(val deleteRecord: Boolean = true) : 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.EndAllVoiceActions -> handleEndAllVoiceActions(action.deleteRecord)
|
||||||
is MessageComposerAction.InitializeVoiceRecorder -> handleInitializeVoiceRecorder(action.attachmentData)
|
is MessageComposerAction.InitializeVoiceRecorder -> handleInitializeVoiceRecorder(action.attachmentData)
|
||||||
is MessageComposerAction.OnEntersBackground -> handleEntersBackground(action.composerText)
|
is MessageComposerAction.OnEntersBackground -> handleEntersBackground(action.composerText)
|
||||||
}
|
is MessageComposerAction.VoiceWaveformTouchedUp -> handleVoiceWaveformTouchedUp(action)
|
||||||
|
is MessageComposerAction.VoiceWaveformMovedTo -> handleVoiceWaveformMovedTo(action)
|
||||||
|
}.exhaustive
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun handleOnVoiceRecordingUiStateChanged(action: MessageComposerAction.OnVoiceRecordingUiStateChanged) = setState {
|
private fun handleOnVoiceRecordingUiStateChanged(action: MessageComposerAction.OnVoiceRecordingUiStateChanged) = setState {
|
||||||
|
@ -861,6 +863,18 @@ class MessageComposerViewModel @AssistedInject constructor(
|
||||||
voiceMessageHelper.pauseRecording()
|
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) {
|
private fun handleEntersBackground(composerText: String) {
|
||||||
val isVoiceRecording = com.airbnb.mvrx.withState(this) { it.isVoiceRecording }
|
val isVoiceRecording = com.airbnb.mvrx.withState(this) { it.isVoiceRecording }
|
||||||
if (isVoiceRecording) {
|
if (isVoiceRecording) {
|
||||||
|
|
|
@ -174,6 +174,14 @@ class VoiceMessageHelper @Inject constructor(
|
||||||
stopPlaybackTicker()
|
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() {
|
private fun startRecordingAmplitudes() {
|
||||||
amplitudeTicker?.stop()
|
amplitudeTicker?.stop()
|
||||||
amplitudeTicker = CountUpTimer(50).apply {
|
amplitudeTicker = CountUpTimer(50).apply {
|
||||||
|
|
|
@ -138,6 +138,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec
|
||||||
fun getPreviewUrlRetriever(): PreviewUrlRetriever
|
fun getPreviewUrlRetriever(): PreviewUrlRetriever
|
||||||
|
|
||||||
fun onVoiceControlButtonClicked(eventId: String, messageAudioContent: MessageAudioContent)
|
fun onVoiceControlButtonClicked(eventId: String, messageAudioContent: MessageAudioContent)
|
||||||
|
fun onVoiceWaveformTouchedUp(eventId: String, messageAudioContent: MessageAudioContent, percentage: Float)
|
||||||
|
fun onVoiceWaveformMovedTo(eventId: String, messageAudioContent: MessageAudioContent, percentage: Float)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ReactionPillCallback {
|
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_()
|
return MessageVoiceItem_()
|
||||||
.attributes(attributes)
|
.attributes(attributes)
|
||||||
.duration(messageContent.audioWaveformInfo?.duration ?: 0)
|
.duration(messageContent.audioWaveformInfo?.duration ?: 0)
|
||||||
.waveform(messageContent.audioWaveformInfo?.waveform?.toFft().orEmpty())
|
.waveform(messageContent.audioWaveformInfo?.waveform?.toFft().orEmpty())
|
||||||
.playbackControlButtonClickListener(playbackControlButtonClickListener)
|
.playbackControlButtonClickListener(playbackControlButtonClickListener)
|
||||||
|
.waveformTouchListener(waveformTouchListener)
|
||||||
.voiceMessagePlaybackTracker(voiceMessagePlaybackTracker)
|
.voiceMessagePlaybackTracker(voiceMessagePlaybackTracker)
|
||||||
.izLocalFile(localFilesHelper.isLocalFile(fileUrl))
|
.izLocalFile(localFilesHelper.isLocalFile(fileUrl))
|
||||||
.izDownloaded(session.fileService().isFileInCache(
|
.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.content.res.ColorStateList
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
|
@ -38,6 +39,11 @@ import im.vector.app.features.voice.AudioWaveformView
|
||||||
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
@EpoxyModelClass(layout = R.layout.item_timeline_event_base)
|
||||||
abstract class MessageVoiceItem : AbsMessageItem<MessageVoiceItem.Holder>() {
|
abstract class MessageVoiceItem : AbsMessageItem<MessageVoiceItem.Holder>() {
|
||||||
|
|
||||||
|
interface WaveformTouchListener {
|
||||||
|
fun onWaveformTouchedUp(percentage: Float)
|
||||||
|
fun onWaveformMovedTo(percentage: Float)
|
||||||
|
}
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
var mxcUrl: String = ""
|
var mxcUrl: String = ""
|
||||||
|
|
||||||
|
@ -62,6 +68,9 @@ abstract class MessageVoiceItem : AbsMessageItem<MessageVoiceItem.Holder>() {
|
||||||
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||||
var playbackControlButtonClickListener: ClickListener? = null
|
var playbackControlButtonClickListener: ClickListener? = null
|
||||||
|
|
||||||
|
@EpoxyAttribute(EpoxyAttribute.Option.DoNotHash)
|
||||||
|
var waveformTouchListener: WaveformTouchListener? = null
|
||||||
|
|
||||||
@EpoxyAttribute
|
@EpoxyAttribute
|
||||||
lateinit var voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker
|
lateinit var voiceMessagePlaybackTracker: VoiceMessagePlaybackTracker
|
||||||
|
|
||||||
|
@ -87,6 +96,20 @@ abstract class MessageVoiceItem : AbsMessageItem<MessageVoiceItem.Holder>() {
|
||||||
holder.voicePlaybackWaveform.add(AudioWaveformView.FFT(amplitude.toFloat(), waveformColorIdle))
|
holder.voicePlaybackWaveform.add(AudioWaveformView.FFT(amplitude.toFloat(), waveformColorIdle))
|
||||||
}
|
}
|
||||||
holder.voicePlaybackWaveform.summarize()
|
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) {
|
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) {
|
private fun renderIdleState(holder: Holder, idleColor: Int, playedColor: Int) {
|
||||||
holder.voicePlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play)
|
holder.voicePlaybackControlButton.setImageResource(R.drawable.ic_play_pause_play)
|
||||||
holder.voicePlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_play_voice_message)
|
holder.voicePlaybackControlButton.contentDescription = holder.view.context.getString(R.string.a11y_play_voice_message)
|
||||||
|
|
Loading…
Add table
Reference in a new issue