diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenter.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenter.kt index ad2901fa53..6fdd2f1752 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenter.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenter.kt @@ -249,7 +249,11 @@ class DefaultVoiceMessageComposerPresenter( private fun CoroutineScope.startRecording() = launch { try { - audioFocus.requestAudioFocus(AudioFocusRequester.RecordVoiceMessage) {} + audioFocus.requestAudioFocus(AudioFocusRequester.RecordVoiceMessage) { + // something else grabbed focus (phone call, etc) - finish gracefully + // so the user keeps their partial recording + sessionCoroutineScope.finishRecording() + } voiceRecorder.startRecord() } catch (e: SecurityException) { audioFocus.releaseAudioFocus() diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenterTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenterTest.kt index 53f725ad83..9c55bf3a85 100644 --- a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenterTest.kt +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenterTest.kt @@ -199,6 +199,30 @@ class DefaultVoiceMessageComposerPresenterTest { } } + @Test + fun `present - audio focus loss during recording finishes gracefully`() = runTest { + var onFocusLost: (() -> Unit)? = null + val testAudioFocus = FakeAudioFocus( + requestAudioFocusResult = { _, callback -> onFocusLost = callback }, + releaseAudioFocusResult = { }, + ) + val presenter = createDefaultVoiceMessageComposerPresenter(audioFocus = testAudioFocus) + presenter.test { + awaitItem().eventSink(VoiceMessageComposerEvent.RecorderEvent(VoiceMessageRecorderEvent.Start)) + awaitItem() + + // simulate focus loss (phone call, etc) + onFocusLost?.invoke() + advanceUntilIdle() + + val finalState = awaitItem() + assertThat(finalState.voiceMessageState).isEqualTo(aPreviewState()) + voiceRecorder.assertCalls(started = 1, stopped = 1) + + cancelAndIgnoreRemainingEvents() + } + } + @Test fun `present - abort recording`() = runTest { val presenter = createDefaultVoiceMessageComposerPresenter() @@ -687,6 +711,7 @@ class DefaultVoiceMessageComposerPresenterTest { private fun TestScope.createDefaultVoiceMessageComposerPresenter( permissionsPresenter: PermissionsPresenter = createFakePermissionsPresenter(), voiceRecorder: VoiceRecorder = this@DefaultVoiceMessageComposerPresenterTest.voiceRecorder, + audioFocus: AudioFocus = this@DefaultVoiceMessageComposerPresenterTest.audioFocus, ): DefaultVoiceMessageComposerPresenter { return DefaultVoiceMessageComposerPresenter( sessionCoroutineScope = backgroundScope,