diff --git a/vector/src/main/java/im/vector/app/core/utils/CountUpTimer.kt b/vector/src/main/java/im/vector/app/core/utils/CountUpTimer.kt index e14fd99ef5..b2034b5515 100644 --- a/vector/src/main/java/im/vector/app/core/utils/CountUpTimer.kt +++ b/vector/src/main/java/im/vector/app/core/utils/CountUpTimer.kt @@ -22,7 +22,7 @@ import java.util.concurrent.TimeUnit import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicLong -class CountUpTimer(private val intervalInMs: Long) { +class CountUpTimer(private val intervalInMs: Long = 1_000) { private val elapsedTime: AtomicLong = AtomicLong() private val resumed: AtomicBoolean = AtomicBoolean(false) diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt index a2f23f1df9..745a8e3019 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/RoomDetailViewModel.kt @@ -627,13 +627,13 @@ class RoomDetailViewModel @AssistedInject constructor( private fun handleEndRecordingVoiceMessage(isCancelled: Boolean) { if (isCancelled) { voiceMessageHelper.deleteRecording() - return - } - voiceMessageHelper.stopRecording()?.let { audioType -> - if (audioType.duration > 1000) { - room.sendMedia(audioType.toContentAttachmentData(), false, emptySet()) - } else { - voiceMessageHelper.deleteRecording() + } else { + voiceMessageHelper.stopRecording()?.let { audioType -> + if (audioType.duration > 1000) { + room.sendMedia(audioType.toContentAttachmentData(), false, emptySet()) + } else { + voiceMessageHelper.deleteRecording() + } } } } diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt index 84d2cab2a9..828072e52a 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageHelper.kt @@ -31,8 +31,6 @@ import java.io.File import java.io.FileInputStream import java.io.FileNotFoundException import java.io.FileOutputStream -import java.lang.IllegalStateException -import java.lang.RuntimeException import java.util.Timer import java.util.TimerTask import java.util.UUID diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageRecorderView.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageRecorderView.kt index 924ec3ed98..2d29406d75 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageRecorderView.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/composer/VoiceMessageRecorderView.kt @@ -25,13 +25,12 @@ import androidx.core.view.isVisible import im.vector.app.BuildConfig import im.vector.app.R import im.vector.app.core.hardware.vibrate +import im.vector.app.core.utils.CountUpTimer import im.vector.app.core.utils.DimensionConverter import im.vector.app.databinding.ViewVoiceMessageRecorderBinding import im.vector.app.features.home.room.detail.timeline.helper.VoiceMessagePlaybackTracker import org.matrix.android.sdk.api.extensions.orFalse import timber.log.Timber -import java.util.Timer -import java.util.TimerTask import kotlin.math.abs import kotlin.math.floor @@ -68,10 +67,8 @@ class VoiceMessageRecorderView @JvmOverloads constructor( private var lastX: Float = 0f private var lastY: Float = 0f - private var recordingTime: Int = -1 private var amplitudeList = emptyList() - private val recordingTimer = Timer() - private var recordingTimerTask: TimerTask? = null + private var recordingTimer: CountUpTimer? = null private val dimensionConverter = DimensionConverter(context.resources) @@ -80,6 +77,7 @@ class VoiceMessageRecorderView @JvmOverloads constructor( views = ViewVoiceMessageRecorderBinding.bind(this) initVoiceRecordingViews() + initListeners() } fun initVoiceRecordingViews() { @@ -88,7 +86,9 @@ class VoiceMessageRecorderView @JvmOverloads constructor( views.voiceMessageMicButton.isVisible = true views.voiceMessageSendButton.isVisible = false + } + private fun initListeners() { views.voiceMessageSendButton.setOnClickListener { stopRecordingTimer() hideRecordingViews(animationDuration = 0) @@ -232,27 +232,34 @@ class VoiceMessageRecorderView @JvmOverloads constructor( } private fun startRecordingTimer() { - recordingTimerTask = object : TimerTask() { - override fun run() { - recordingTime++ - showRecordingTimer() - showRecordingWaveform() - val timeDiffToRecordingLimit = BuildConfig.VOICE_MESSAGE_DURATION_LIMIT_MS - recordingTime * 1000 - if (timeDiffToRecordingLimit <= 0) { - views.voiceMessageRecordingLayout.post { - recordingState = RecordingState.PLAYBACK - showPlaybackViews() - stopRecordingTimer() - } - } else if (timeDiffToRecordingLimit in 10000..10999) { - views.voiceMessageRecordingLayout.post { - renderToast(context.getString(R.string.voice_message_n_seconds_warning_toast, floor(timeDiffToRecordingLimit / 1000f).toInt())) - vibrate(context) - } + recordingTimer?.stop() + recordingTimer = CountUpTimer().apply { + tickListener = object : CountUpTimer.TickListener { + override fun onTick(milliseconds: Long) { + onRecordingTimerTick(milliseconds) } } + resume() + } + onRecordingTimerTick(0L) + } + + private fun onRecordingTimerTick(milliseconds: Long) { + renderRecordingTimer(milliseconds / 1_000) + renderRecordingWaveform() + val timeDiffToRecordingLimit = BuildConfig.VOICE_MESSAGE_DURATION_LIMIT_MS - milliseconds + if (timeDiffToRecordingLimit <= 0) { + views.voiceMessageRecordingLayout.post { + recordingState = RecordingState.PLAYBACK + showPlaybackViews() + stopRecordingTimer() + } + } else if (timeDiffToRecordingLimit in 10_000..10_999) { + views.voiceMessageRecordingLayout.post { + renderToast(context.getString(R.string.voice_message_n_seconds_warning_toast, floor(timeDiffToRecordingLimit / 1000f).toInt())) + vibrate(context) + } } - recordingTimer.scheduleAtFixedRate(recordingTimerTask, 0, 1000) } private fun renderToast(message: String) { @@ -266,8 +273,8 @@ class VoiceMessageRecorderView @JvmOverloads constructor( views.voiceMessageToast.isVisible = false } - private fun showRecordingTimer() { - val formattedTimerText = DateUtils.formatElapsedTime(recordingTime.toLong()) + private fun renderRecordingTimer(recordingTimeMillis: Long) { + val formattedTimerText = DateUtils.formatElapsedTime(recordingTimeMillis) if (recordingState == RecordingState.LOCKED) { views.voicePlaybackTime.apply { post { @@ -281,7 +288,7 @@ class VoiceMessageRecorderView @JvmOverloads constructor( } } - private fun showRecordingWaveform() { + private fun renderRecordingWaveform() { val audioRecordView = views.voicePlaybackWaveform audioRecordView.apply { post { @@ -294,8 +301,8 @@ class VoiceMessageRecorderView @JvmOverloads constructor( } private fun stopRecordingTimer() { - recordingTimerTask?.cancel() - recordingTime = -1 + recordingTimer?.stop() + recordingTimer = null } private fun showRecordingViews() {