Support scrolling playback on recorded audio before sending.

This commit is contained in:
Onuray Sahin 2022-03-04 16:21:28 +03:00
parent 3bd4a4ccd3
commit 5168d715ce
8 changed files with 65 additions and 23 deletions

View File

@ -786,6 +786,18 @@ class TimelineFragment @Inject constructor(
updateRecordingUiState(RecordingUiState.Draft)
}
override fun onVoiceWaveformTouchedUp(percentage: Float, duration: Int) {
messageComposerViewModel.handle(
MessageComposerAction.VoiceWaveformTouchedUp(VoiceMessagePlaybackTracker.RECORDING_ID, duration, percentage)
)
}
override fun onVoiceWaveformMoved(percentage: Float, duration: Int) {
messageComposerViewModel.handle(
MessageComposerAction.VoiceWaveformTouchedUp(VoiceMessagePlaybackTracker.RECORDING_ID, duration, percentage)
)
}
private fun updateRecordingUiState(state: RecordingUiState) {
messageComposerViewModel.handle(
MessageComposerAction.OnVoiceRecordingUiStateChanged(state))
@ -2051,12 +2063,12 @@ 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 onVoiceWaveformTouchedUp(eventId: String, duration: Int, percentage: Float) {
messageComposerViewModel.handle(MessageComposerAction.VoiceWaveformTouchedUp(eventId, duration, percentage))
}
override fun onVoiceWaveformMovedTo(eventId: String, messageAudioContent: MessageAudioContent, percentage: Float) {
messageComposerViewModel.handle(MessageComposerAction.VoiceWaveformMovedTo(eventId, messageAudioContent, percentage))
override fun onVoiceWaveformMovedTo(eventId: String, duration: Int, percentage: Float) {
messageComposerViewModel.handle(MessageComposerAction.VoiceWaveformMovedTo(eventId, duration, percentage))
}
private fun onShareActionClicked(action: EventSharedAction.Share) {

View File

@ -40,6 +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()
data class VoiceWaveformTouchedUp(val eventId: String, val duration: Int, val percentage: Float) : MessageComposerAction()
data class VoiceWaveformMovedTo(val eventId: String, val duration: Int, val percentage: Float) : MessageComposerAction()
}

View File

@ -864,15 +864,11 @@ class MessageComposerViewModel @AssistedInject constructor(
}
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)
voiceMessageHelper.movePlaybackTo(action.eventId, action.percentage, action.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)
voiceMessageHelper.movePlaybackTo(action.eventId, action.percentage, action.duration)
}
private fun handleEntersBackground(composerText: String) {

View File

@ -171,13 +171,13 @@ class VoiceMessageHelper @Inject constructor(
}
fun stopPlayback() {
playbackTracker.stopPlayback(VoiceMessagePlaybackTracker.RECORDING_ID)
playbackTracker.pausePlayback(VoiceMessagePlaybackTracker.RECORDING_ID)
mediaPlayer?.stop()
stopPlaybackTicker()
}
fun movePlaybackTo(id: String, toMillisecond: Int, totalDuration: Int) {
val percentage = toMillisecond.toFloat() / totalDuration
fun movePlaybackTo(id: String, percentage: Float, totalDuration: Int) {
val toMillisecond = (totalDuration * percentage).toInt()
playbackTracker.updateCurrentPlaybackTime(id, toMillisecond, percentage)
stopPlayback()

View File

@ -53,6 +53,8 @@ class VoiceMessageRecorderView @JvmOverloads constructor(
fun onDeleteVoiceMessage()
fun onRecordingLimitReached()
fun onRecordingWaveformClicked()
fun onVoiceWaveformTouchedUp(percentage: Float, duration: Int)
fun onVoiceWaveformMoved(percentage: Float, duration: Int)
}
@Inject lateinit var clock: Clock
@ -65,6 +67,7 @@ class VoiceMessageRecorderView @JvmOverloads constructor(
private var recordingTicker: CountUpTimer? = null
private var lastKnownState: RecordingUiState? = null
private var dragState: DraggingState = DraggingState.Ignored
private var recordingDuration: Long = 0
init {
inflate(this.context, R.layout.view_voice_message_recorder, this)
@ -95,7 +98,6 @@ class VoiceMessageRecorderView @JvmOverloads constructor(
override fun onDeleteVoiceMessage() = callback.onDeleteVoiceMessage()
override fun onWaveformClicked() {
when (lastKnownState) {
RecordingUiState.Draft -> callback.onVoicePlaybackButtonClicked()
is RecordingUiState.Recording,
is RecordingUiState.Locked -> callback.onRecordingWaveformClicked()
}
@ -105,6 +107,18 @@ class VoiceMessageRecorderView @JvmOverloads constructor(
override fun onMicButtonDrag(nextDragStateCreator: (DraggingState) -> DraggingState) {
onDrag(dragState, newDragState = nextDragStateCreator(dragState))
}
override fun onVoiceWaveformTouchedUp(percentage: Float) {
if (lastKnownState == RecordingUiState.Draft) {
callback.onVoiceWaveformTouchedUp(percentage, recordingDuration.toInt())
}
}
override fun onVoiceWaveformMoved(percentage: Float) {
if (lastKnownState == RecordingUiState.Draft) {
callback.onVoiceWaveformMoved(percentage, recordingDuration.toInt())
}
}
})
}
@ -203,6 +217,7 @@ class VoiceMessageRecorderView @JvmOverloads constructor(
}
private fun stopRecordingTicker() {
recordingDuration = recordingTicker?.elapsedTime() ?: 0
recordingTicker?.stop()
recordingTicker = null
}

View File

@ -60,8 +60,21 @@ class VoiceMessageViews(
actions.onDeleteVoiceMessage()
}
views.voicePlaybackWaveform.setOnClickListener {
actions.onWaveformClicked()
views.voicePlaybackWaveform.setOnTouchListener { view, motionEvent ->
when (motionEvent.action) {
MotionEvent.ACTION_DOWN -> {
actions.onWaveformClicked()
}
MotionEvent.ACTION_UP -> {
val percentage = getTouchedPositionPercentage(motionEvent, view)
actions.onVoiceWaveformTouchedUp(percentage)
}
MotionEvent.ACTION_MOVE -> {
val percentage = getTouchedPositionPercentage(motionEvent, view)
actions.onVoiceWaveformMoved(percentage)
}
}
true
}
views.voicePlaybackControlButton.setOnClickListener {
@ -70,6 +83,8 @@ class VoiceMessageViews(
observeMicButton(actions)
}
private fun getTouchedPositionPercentage(motionEvent: MotionEvent, view: View) = motionEvent.x / view.width
@SuppressLint("ClickableViewAccessibility")
private fun observeMicButton(actions: Actions) {
val draggableStateProcessor = DraggableStateProcessor(resources, dimensionConverter)
@ -332,7 +347,7 @@ class VoiceMessageViews(
fun renderRecordingWaveform(amplitudeList: Array<Int>) {
views.voicePlaybackWaveform.doOnLayout { waveFormView ->
val waveformColor = ThemeUtils.getColor(waveFormView.context, R.attr.vctr_content_secondary)
val waveformColor = ThemeUtils.getColor(waveFormView.context, R.attr.vctr_content_quaternary)
amplitudeList.iterator().forEach {
(waveFormView as AudioWaveformView).add(AudioWaveformView.FFT(it.toFloat(), waveformColor))
}
@ -355,5 +370,7 @@ class VoiceMessageViews(
fun onDeleteVoiceMessage()
fun onWaveformClicked()
fun onVoicePlaybackButtonClicked()
fun onVoiceWaveformTouchedUp(percentage: Float)
fun onVoiceWaveformMoved(percentage: Float)
}
}

View File

@ -138,8 +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)
fun onVoiceWaveformTouchedUp(eventId: String, duration: Int, percentage: Float)
fun onVoiceWaveformMovedTo(eventId: String, duration: Int, percentage: Float)
}
interface ReactionPillCallback {

View File

@ -359,11 +359,13 @@ class MessageItemFactory @Inject constructor(
val waveformTouchListener: MessageVoiceItem.WaveformTouchListener = object : MessageVoiceItem.WaveformTouchListener {
override fun onWaveformTouchedUp(percentage: Float) {
params.callback?.onVoiceWaveformTouchedUp(informationData.eventId, messageContent, percentage)
val duration = messageContent.audioInfo?.duration ?: 0
params.callback?.onVoiceWaveformTouchedUp(informationData.eventId, duration, percentage)
}
override fun onWaveformMovedTo(percentage: Float) {
params.callback?.onVoiceWaveformMovedTo(informationData.eventId, messageContent, percentage)
val duration = messageContent.audioInfo?.duration ?: 0
params.callback?.onVoiceWaveformMovedTo(informationData.eventId, duration, percentage)
}
}