diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/LiveWaveformView.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/LiveWaveformView.kt index aa4a6cc749..0d083fe3e2 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/LiveWaveformView.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/LiveWaveformView.kt @@ -44,6 +44,7 @@ import io.element.android.libraries.designsystem.preview.PreviewsDayNight import io.element.android.libraries.theme.ElementTheme import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.toPersistentList +import java.lang.Float.min private const val DEFAULT_GRAPHICS_LAYER_ALPHA: Float = 0.99F private val waveFormHeight = 26.dp @@ -57,7 +58,7 @@ fun LiveWaveformView( ) { var canvasSize by remember { mutableStateOf(DpSize(0.dp, 0.dp)) } - val canvasWidth by remember(levels, lineWidth, linePadding) { + val waveformWidth by remember(levels, lineWidth, linePadding) { derivedStateOf { levels.size * (lineWidth.value + linePadding.value) } @@ -72,11 +73,11 @@ fun LiveWaveformView( ) { Canvas( modifier = Modifier - .width(Dp(canvasWidth)) + .width(canvasSize.width) .graphicsLayer(alpha = DEFAULT_GRAPHICS_LAYER_ALPHA) .then(modifier) ) { - canvasSize = DpSize(Dp(canvasWidth), size.height.toDp()) + canvasSize = DpSize(Dp(min(waveformWidth, width.toFloat())), size.height.toDp()) val countThatFitsWidth = (width.toFloat() / (lineWidth.toPx() + linePadding.toPx())).toInt() drawWaveform( waveformData = levels.takeLast(countThatFitsWidth).toPersistentList(), diff --git a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt index 29993a0ec6..038864d4da 100644 --- a/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt +++ b/libraries/textcomposer/impl/src/main/kotlin/io/element/android/libraries/textcomposer/components/VoiceMessageRecording.kt @@ -73,7 +73,7 @@ internal fun VoiceMessageRecording( LiveWaveformView( modifier = Modifier - .height(34.dp) + .height(26.dp) .weight(1f), levels = levels ) diff --git a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt index 2590cf64e0..0dcd085a81 100644 --- a/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt +++ b/libraries/voicerecorder/impl/src/main/kotlin/io/element/android/libraries/voicerecorder/impl/VoiceRecorderImpl.kt @@ -23,6 +23,7 @@ import io.element.android.libraries.core.coroutine.CoroutineDispatchers import io.element.android.libraries.core.coroutine.childScope import io.element.android.libraries.di.RoomScope import io.element.android.libraries.di.SingleIn +import io.element.android.libraries.matrix.api.roomlist.RoomSummary import io.element.android.libraries.voicerecorder.api.VoiceRecorder import io.element.android.libraries.voicerecorder.api.VoiceRecorderState import io.element.android.libraries.voicerecorder.impl.audio.Audio @@ -38,6 +39,8 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.yield import timber.log.Timber import java.io.File @@ -67,6 +70,7 @@ class VoiceRecorderImpl @Inject constructor( private var audioReader: AudioReader? = null private var recordingJob: Job? = null private val levels: MutableList = mutableListOf() + private val lock = Mutex() private val _state = MutableStateFlow(VoiceRecorderState.Idle) override val state: StateFlow = _state @@ -76,7 +80,10 @@ class VoiceRecorderImpl @Inject constructor( Timber.i("Voice recorder started recording") outputFile = fileManager.createFile() .also(encoder::init) - levels.clear() + + lock.withLock { + levels.clear() + } val audioRecorder = audioReaderFactory.create(config, dispatchers).also { audioReader = it } @@ -96,8 +103,11 @@ class VoiceRecorderImpl @Inject constructor( when (audio) { is Audio.Data -> { val audioLevel = audioLevelCalculator.calculateAudioLevel(audio.buffer) - levels.add(audioLevel) - _state.emit(VoiceRecorderState.Recording(elapsedTime, levels)) + + lock.withLock{ + levels.add(audioLevel) + _state.emit(VoiceRecorderState.Recording(elapsedTime, levels.toList())) + } encoder.encode(audio.buffer, audio.readSize) } is Audio.Error -> { @@ -126,21 +136,24 @@ class VoiceRecorderImpl @Inject constructor( audioReader = null encoder.release() - if (cancelled) { - deleteRecording() - levels.clear() - } - _state.emit( - when (val file = outputFile) { - null -> VoiceRecorderState.Idle - else -> VoiceRecorderState.Finished( - file = file, - mimeType = fileConfig.mimeType, - waveform = levels.resample(100), - ) + lock.withLock { + if (cancelled) { + deleteRecording() + levels.clear() } - ) + + _state.emit( + when (val file = outputFile) { + null -> VoiceRecorderState.Idle + else -> VoiceRecorderState.Finished( + file = file, + mimeType = fileConfig.mimeType, + waveform = levels.resample(100), + ) + } + ) + } } /**