Merge pull request #7346 from hayaksi1/fix/5780-voice-recorder-double-start

Ignore a second voice recording start while one is already running
This commit is contained in:
Benoit Marty
2026-08-11 16:20:20 +02:00
committed by GitHub
3 changed files with 39 additions and 7 deletions
@@ -71,14 +71,17 @@ class DefaultVoiceRecorder(
override val state: StateFlow<VoiceRecorderState> = _state
@RequiresPermission(Manifest.permission.RECORD_AUDIO)
override suspend fun startRecord() {
override suspend fun startRecord() = lock.withLock {
if (recordingJob != null) {
Timber.w("Voice recorder is already recording, ignoring this start")
return@withLock
}
Timber.i("Voice recorder started recording")
outputFile = fileManager.createFile()
.also(encoder::init)
lock.withLock {
levels.clear()
}
levels.clear()
val audioRecorder = audioReaderFactory.create(config, dispatchers).also { audioReader = it }
@@ -39,6 +39,7 @@ import kotlin.time.TestTimeSource
class DefaultVoiceRecorderTest {
private val fakeFileSystem = FakeFileSystem()
private val timeSource = TestTimeSource()
private val audioReaderFactory = FakeAudioReaderFactory(audio = AUDIO)
@Test
fun `it emits the initial state`() = runTest {
@@ -109,6 +110,32 @@ class DefaultVoiceRecorderTest {
}
}
@Test
fun `when startRecord is called twice, the second call is ignored`() = runTest {
val voiceRecorder = createDefaultVoiceRecorder()
voiceRecorder.state.test {
assertThat(awaitItem()).isEqualTo(VoiceRecorderState.Idle)
voiceRecorder.startRecord()
voiceRecorder.startRecord()
assertThat(audioReaderFactory.createdCount).isEqualTo(1)
skipItems(1)
timeSource += 5.seconds
skipItems(2)
voiceRecorder.stopRecord()
assertThat(awaitItem()).isEqualTo(
VoiceRecorderState.Finished(
file = File(FILE_PATH),
mimeType = MimeTypes.Ogg,
waveform = List(100) { 1f },
duration = 5.seconds,
)
)
expectNoEvents()
}
}
@Test
fun `when cancelled, it deletes the file`() = runTest {
val voiceRecorder = createDefaultVoiceRecorder()
@@ -128,9 +155,7 @@ class DefaultVoiceRecorderTest {
return DefaultVoiceRecorder(
dispatchers = testCoroutineDispatchers(),
timeSource = timeSource,
audioReaderFactory = FakeAudioReaderFactory(
audio = AUDIO,
),
audioReaderFactory = audioReaderFactory,
encoder = FakeEncoder(fakeFileSystem),
config = AudioConfig(
format = audioFormat,
@@ -16,7 +16,11 @@ import io.element.android.libraries.voicerecorder.impl.audio.AudioReader
class FakeAudioReaderFactory(
private val audio: List<Audio>
) : AudioReader.Factory {
var createdCount: Int = 0
private set
override fun create(config: AudioConfig, dispatchers: CoroutineDispatchers): AudioReader {
createdCount++
return FakeAudioReader(dispatchers, audio)
}
}