diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt index cea9641443..84e8260a97 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailFragment.kt @@ -709,17 +709,18 @@ class RoomDetailFragment @Inject constructor( } override fun onVoiceRecordingCancelled() { - onDeleteVoiceMessage() + roomDetailViewModel.handle(RoomDetailAction.EndRecordingVoiceMessage(isCancelled = true)) + updateRecordingUiState(RecordingUiState.Cancelled) + } + + override fun onVoiceRecordingLocked() { + updateRecordingUiState(RecordingUiState.Locked) } override fun onVoiceRecordingEnded() { onSendVoiceMessage() } - override fun onUiStateChanged(state: RecordingUiState) { - updateRecordingUiState(state) - } - override fun onSendVoiceMessage() { roomDetailViewModel.handle(RoomDetailAction.EndRecordingVoiceMessage(isCancelled = false)) updateRecordingUiState(RecordingUiState.None) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt index 4eb70138bb..fa19d129e9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/TextComposerViewState.kt @@ -54,8 +54,6 @@ data class TextComposerViewState( VoiceMessageRecorderView.RecordingUiState.None, VoiceMessageRecorderView.RecordingUiState.Cancelled, VoiceMessageRecorderView.RecordingUiState.Playback -> false - is VoiceMessageRecorderView.DraggingState.Cancelling, - is VoiceMessageRecorderView.DraggingState.Locking, VoiceMessageRecorderView.RecordingUiState.Locked, VoiceMessageRecorderView.RecordingUiState.Started -> true } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/DraggableStateProcessor.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/DraggableStateProcessor.kt index a8b19d6f6a..088070ceb9 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/DraggableStateProcessor.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/DraggableStateProcessor.kt @@ -21,7 +21,6 @@ import android.view.MotionEvent import im.vector.app.R import im.vector.app.core.utils.DimensionConverter import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView.DraggingState -import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView.RecordingUiState class DraggableStateProcessor( resources: Resources, @@ -44,37 +43,37 @@ class DraggableStateProcessor( lastDistanceY = 0F } - fun process(event: MotionEvent, recordingState: RecordingUiState): RecordingUiState { + fun process(event: MotionEvent, draggingState: DraggingState): DraggingState { val currentX = event.rawX val currentY = event.rawY val distanceX = firstX - currentX val distanceY = firstY - currentY - return recordingState.nextRecordingState(currentX, currentY, distanceX, distanceY).also { + return draggingState.nextDragState(currentX, currentY, distanceX, distanceY).also { lastDistanceX = distanceX lastDistanceY = distanceY } } - private fun RecordingUiState.nextRecordingState(currentX: Float, currentY: Float, distanceX: Float, distanceY: Float): RecordingUiState { + private fun DraggingState.nextDragState(currentX: Float, currentY: Float, distanceX: Float, distanceY: Float): DraggingState { return when (this) { - RecordingUiState.Started -> { + DraggingState.Ready -> { when { isDraggingToCancel(currentX, distanceX, distanceY) -> DraggingState.Cancelling(distanceX) isDraggingToLock(currentY, distanceX, distanceY) -> DraggingState.Locking(distanceY) - else -> this + else -> DraggingState.Ready } } is DraggingState.Cancelling -> { when { isDraggingToLock(currentY, distanceX, distanceY) -> DraggingState.Locking(distanceY) - shouldCancelRecording(distanceX) -> RecordingUiState.Cancelled + shouldCancelRecording(distanceX) -> DraggingState.Cancel else -> DraggingState.Cancelling(distanceX) } } is DraggingState.Locking -> { when { isDraggingToCancel(currentX, distanceX, distanceY) -> DraggingState.Cancelling(distanceX) - shouldLockRecording(distanceY) -> RecordingUiState.Locked + shouldLockRecording(distanceY) -> DraggingState.Lock else -> DraggingState.Locking(distanceY) } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageRecorderView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageRecorderView.kt index c673ecfc16..d212e800a8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageRecorderView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageRecorderView.kt @@ -44,7 +44,7 @@ class VoiceMessageRecorderView @JvmOverloads constructor( fun onVoiceRecordingEnded() fun onVoicePlaybackButtonClicked() fun onVoiceRecordingCancelled() - fun onUiStateChanged(state: RecordingUiState) + fun onVoiceRecordingLocked() fun onSendVoiceMessage() fun onDeleteVoiceMessage() fun onRecordingLimitReached() @@ -58,6 +58,7 @@ class VoiceMessageRecorderView @JvmOverloads constructor( private var recordingTicker: CountUpTimer? = null private var lastKnownState: RecordingUiState? = null + private var dragState: DraggingState = DraggingState.Ignored init { inflate(this.context, R.layout.view_voice_message_recorder, this) @@ -74,13 +75,13 @@ class VoiceMessageRecorderView @JvmOverloads constructor( voiceMessageViews.start(object : VoiceMessageViews.Actions { override fun onRequestRecording() = callback.onVoiceRecordingStarted() override fun onMicButtonReleased() { - when (lastKnownState) { - RecordingUiState.Locked -> { + when (dragState) { + DraggingState.Lock -> { // do nothing, // onSendVoiceMessage, onDeleteVoiceMessage or onRecordingLimitReached will be triggered instead } - RecordingUiState.Cancelled -> callback.onVoiceRecordingCancelled() - else -> callback.onVoiceRecordingEnded() + DraggingState.Cancel -> callback.onVoiceRecordingCancelled() + else -> callback.onVoiceRecordingEnded() } } @@ -88,21 +89,8 @@ class VoiceMessageRecorderView @JvmOverloads constructor( override fun onDeleteVoiceMessage() = callback.onDeleteVoiceMessage() override fun onWaveformClicked() = callback.onRecordingWaveformClicked() override fun onVoicePlaybackButtonClicked() = callback.onVoicePlaybackButtonClicked() - override fun onMicButtonDrag(updater: (RecordingUiState) -> RecordingUiState) { - when (val currentState = lastKnownState) { - null, RecordingUiState.None -> { - // ignore drag events when the view is idle - } - else -> { - updater(currentState).also { newState -> - when (newState) { - // display drag events directly without leaving the view for faster UI feedback - is DraggingState -> display(newState) - else -> callback.onUiStateChanged(newState) - } - } - } - } + override fun onMicButtonDrag(nextDragStateCreator: (DraggingState) -> DraggingState) { + onDrag(dragState, newDragState = nextDragStateCreator(dragState)) } }) } @@ -117,21 +105,19 @@ class VoiceMessageRecorderView @JvmOverloads constructor( fun display(recordingState: RecordingUiState) { if (lastKnownState == recordingState) return - val previousState = lastKnownState lastKnownState = recordingState when (recordingState) { RecordingUiState.None -> { - stopRecordingTicker() - voiceMessageViews.initViews() + reset() } RecordingUiState.Started -> { startRecordingTicker() voiceMessageViews.renderToast(context.getString(R.string.voice_message_release_to_send_toast)) voiceMessageViews.showRecordingViews() + dragState = DraggingState.Ready } RecordingUiState.Cancelled -> { - stopRecordingTicker() - voiceMessageViews.hideRecordingViews(recordingState) + reset() vibrate(context) } RecordingUiState.Locked -> { @@ -144,18 +130,34 @@ class VoiceMessageRecorderView @JvmOverloads constructor( stopRecordingTicker() voiceMessageViews.showPlaybackViews() } - is DraggingState -> when (recordingState) { - is DraggingState.Cancelling -> voiceMessageViews.renderCancelling(recordingState.distanceX) - is DraggingState.Locking -> { - if (previousState is DraggingState.Cancelling) { - voiceMessageViews.showRecordingViews() - } - voiceMessageViews.renderLocking(recordingState.distanceY) - } - }.exhaustive } } + private fun reset() { + stopRecordingTicker() + voiceMessageViews.initViews() + dragState = DraggingState.Ignored + } + + private fun onDrag(currentDragState: DraggingState, newDragState: DraggingState) { + when (newDragState) { + is DraggingState.Cancelling -> voiceMessageViews.renderCancelling(newDragState.distanceX) + is DraggingState.Locking -> { + if (currentDragState is DraggingState.Cancelling) { + voiceMessageViews.showRecordingViews() + } + voiceMessageViews.renderLocking(newDragState.distanceY) + } + DraggingState.Cancel -> callback.onVoiceRecordingCancelled() + DraggingState.Lock -> callback.onVoiceRecordingLocked() + DraggingState.Ignored, + DraggingState.Ready -> { + // do nothing + } + }.exhaustive + dragState = newDragState + } + private fun startRecordingTicker() { recordingTicker?.stop() recordingTicker = CountUpTimer().apply { @@ -214,8 +216,12 @@ class VoiceMessageRecorderView @JvmOverloads constructor( object Playback : RecordingUiState } - sealed interface DraggingState : RecordingUiState { + sealed interface DraggingState { + object Ready : DraggingState + object Ignored : DraggingState data class Cancelling(val distanceX: Float) : DraggingState data class Locking(val distanceY: Float) : DraggingState + object Cancel : DraggingState + object Lock : DraggingState } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageViews.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageViews.kt index 7aeb665486..32f21a3177 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageViews.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/voice/VoiceMessageViews.kt @@ -32,6 +32,7 @@ import im.vector.app.core.extensions.setAttributeTintedBackground import im.vector.app.core.extensions.setAttributeTintedImageResource import im.vector.app.core.utils.DimensionConverter import im.vector.app.databinding.ViewVoiceMessageRecorderBinding +import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView.DraggingState import im.vector.app.features.home.room.detail.composer.voice.VoiceMessageRecorderView.RecordingUiState import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlaybackTracker @@ -68,11 +69,11 @@ class VoiceMessageViews( @SuppressLint("ClickableViewAccessibility") private fun observeMicButton(actions: Actions) { - val positions = DraggableStateProcessor(resources, dimensionConverter) + val draggableStateProcessor = DraggableStateProcessor(resources, dimensionConverter) views.voiceMessageMicButton.setOnTouchListener { _, event -> when (event.action) { MotionEvent.ACTION_DOWN -> { - positions.initialize(event) + draggableStateProcessor.initialize(event) actions.onRequestRecording() true } @@ -81,7 +82,7 @@ class VoiceMessageViews( true } MotionEvent.ACTION_MOVE -> { - actions.onMicButtonDrag { currentState -> positions.process(event, currentState) } + actions.onMicButtonDrag { currentState -> draggableStateProcessor.process(event, currentState) } true } else -> false @@ -339,7 +340,7 @@ class VoiceMessageViews( interface Actions { fun onRequestRecording() fun onMicButtonReleased() - fun onMicButtonDrag(updater: (RecordingUiState) -> RecordingUiState) + fun onMicButtonDrag(nextDragStateCreator: (DraggingState) -> DraggingState) fun onSendVoiceMessage() fun onDeleteVoiceMessage() fun onWaveformClicked()