Skip to content

Commit d693aee

Browse files
authored
Merge pull request #6109 from kknappe/fix/start-voice-recording-when-permission-is-granted
Fix voice message recording not starting after permission is granted
2 parents 1e268b0 + 7c7043c commit d693aee

File tree

2 files changed

+24
-5
lines changed

2 files changed

+24
-5
lines changed

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenter.kt

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import androidx.compose.runtime.getValue
1717
import androidx.compose.runtime.mutableStateOf
1818
import androidx.compose.runtime.remember
1919
import androidx.compose.runtime.rememberCoroutineScope
20+
import androidx.compose.runtime.rememberUpdatedState
2021
import androidx.compose.runtime.setValue
2122
import androidx.core.net.toUri
2223
import androidx.lifecycle.Lifecycle
@@ -69,6 +70,7 @@ class DefaultVoiceMessageComposerPresenter(
6970
}
7071

7172
private val permissionsPresenter = permissionsPresenterFactory.create(Manifest.permission.RECORD_AUDIO)
73+
private var pendingEvent: VoiceMessageRecorderEvent.Start? = null
7274
private val mediaSender = mediaSenderFactory.create(timelineMode)
7375

7476
@Composable
@@ -77,8 +79,7 @@ class DefaultVoiceMessageComposerPresenter(
7779
val recorderState by voiceRecorder.state.collectAsState(initial = VoiceRecorderState.Idle)
7880
val playerState by player.state.collectAsState(initial = VoiceMessageComposerPlayer.State.Initial)
7981
val keepScreenOn by remember { derivedStateOf { recorderState is VoiceRecorderState.Recording } }
80-
81-
val permissionState = permissionsPresenter.present()
82+
val permissionState by rememberUpdatedState(permissionsPresenter.present())
8283
var isSending by remember { mutableStateOf(false) }
8384
var showSendFailureDialog by remember { mutableStateOf(false) }
8485

@@ -88,6 +89,15 @@ class DefaultVoiceMessageComposerPresenter(
8889
player.setMedia(recording.file.path)
8990
}
9091

92+
LaunchedEffect(permissionState.permissionGranted) {
93+
if (permissionState.permissionGranted) {
94+
pendingEvent?.let {
95+
localCoroutineScope.startRecording()
96+
pendingEvent = null
97+
}
98+
}
99+
}
100+
91101
fun handleLifecycleEvent(event: Lifecycle.Event) {
92102
when (event) {
93103
Lifecycle.Event.ON_PAUSE -> {
@@ -102,6 +112,7 @@ class DefaultVoiceMessageComposerPresenter(
102112
}
103113

104114
fun handleVoiceMessageRecorderEvent(event: VoiceMessageRecorderEvent) {
115+
pendingEvent = null
105116
when (event) {
106117
VoiceMessageRecorderEvent.Start -> {
107118
Timber.v("Voice message record button pressed")
@@ -111,6 +122,7 @@ class DefaultVoiceMessageComposerPresenter(
111122
}
112123
else -> {
113124
Timber.i("Voice message permission needed")
125+
pendingEvent = VoiceMessageRecorderEvent.Start
114126
permissionState.eventSink(PermissionsEvent.RequestPermissions)
115127
}
116128
}

features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/voicemessages/composer/DefaultVoiceMessageComposerPresenterTest.kt

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -522,7 +522,9 @@ class DefaultVoiceMessageComposerPresenterTest {
522522
permissionsPresenter.setPermissionGranted()
523523

524524
awaitItem().eventSink(VoiceMessageComposerEvent.RecorderEvent(VoiceMessageRecorderEvent.Start))
525-
val finalState = awaitItem()
525+
advanceUntilIdle()
526+
527+
val finalState = expectMostRecentItem()
526528
assertThat(finalState.voiceMessageState).isEqualTo(RECORDING_STATE)
527529
voiceRecorder.assertCalls(stopped = 1, started = 1)
528530

@@ -547,14 +549,16 @@ class DefaultVoiceMessageComposerPresenterTest {
547549
assertThat(it.showPermissionRationaleDialog).isTrue()
548550
it.eventSink(VoiceMessageComposerEvent.AcceptPermissionRationale)
549551
}
552+
skipItems(1)
550553

551554
// Dialog is hidden, user accepts permissions
552555
assertThat(awaitItem().showPermissionRationaleDialog).isFalse()
553556

557+
// Permission is granted, recording starts automatically
554558
permissionsPresenter.setPermissionGranted()
559+
advanceUntilIdle()
555560

556-
awaitItem().eventSink(VoiceMessageComposerEvent.RecorderEvent(VoiceMessageRecorderEvent.Start))
557-
val finalState = awaitItem()
561+
val finalState = expectMostRecentItem()
558562
assertThat(finalState.voiceMessageState).isEqualTo(RECORDING_STATE)
559563
voiceRecorder.assertCalls(started = 1)
560564

@@ -579,12 +583,14 @@ class DefaultVoiceMessageComposerPresenterTest {
579583
assertThat(it.showPermissionRationaleDialog).isTrue()
580584
it.eventSink(VoiceMessageComposerEvent.DismissPermissionsRationale)
581585
}
586+
skipItems(1)
582587

583588
// Dialog is hidden, user tries to record again
584589
awaitItem().also {
585590
assertThat(it.showPermissionRationaleDialog).isFalse()
586591
it.eventSink(VoiceMessageComposerEvent.RecorderEvent(VoiceMessageRecorderEvent.Start))
587592
}
593+
skipItems(1)
588594

589595
// Dialog is shown once again
590596
val finalState = awaitItem().also {
@@ -593,6 +599,7 @@ class DefaultVoiceMessageComposerPresenterTest {
593599
}
594600
voiceRecorder.assertCalls(started = 0)
595601

602+
cancelAndIgnoreRemainingEvents()
596603
testPauseAndDestroy(finalState)
597604
}
598605
}

0 commit comments

Comments
 (0)