fix: зависание записи ГС — race condition в startVoiceRecording + утечка isVoiceRecordTransitioning
Root cause 1: startVoiceRecording() проверял только isVoiceRecording, но isVoiceRecording=true ставился через 192ms в scope.launch. При быстром двойном тапе два MediaRecorder создавались, первый терялся (утечка). Фикс: добавлен guard на isVoiceRecordTransitioning и voiceRecorder!=null. Root cause 2: isVoiceRecordTransitioning=true ставился перед scope.launch, но если launch крашился или composable disposed, transitioning навсегда оставался true — gesture guard блокировал все записи до перезапуска. Фикс: try/catch в launch + reset в DisposableEffect. Root cause 3: DisposableEffect проверял только isVoiceRecording, но не voiceRecorder!=null — если recorder создан но isVoiceRecording ещё false, recorder не освобождался при dispose. Фикс: проверка voiceRecorder!=null в dispose. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1180,7 +1180,7 @@ fun MessageInputBar(
|
||||
}
|
||||
|
||||
fun startVoiceRecording() {
|
||||
if (isVoiceRecording) return
|
||||
if (isVoiceRecording || isVoiceRecordTransitioning || voiceRecorder != null) return
|
||||
inputJumpLog(
|
||||
"startVoiceRecording begin mode=$recordMode state=$recordUiState kb=$isKeyboardVisible emojiBox=${coordinator.isEmojiBoxVisible} " +
|
||||
"emojiPicker=$showEmojiPicker panelH=$inputPanelHeightPx normalH=$normalInputRowHeightPx"
|
||||
@@ -1227,20 +1227,25 @@ fun MessageInputBar(
|
||||
)
|
||||
|
||||
scope.launch {
|
||||
repeat(12) {
|
||||
if (!isKeyboardVisible && !coordinator.isEmojiBoxVisible) return@repeat
|
||||
delay(16)
|
||||
try {
|
||||
repeat(12) {
|
||||
if (!isKeyboardVisible && !coordinator.isEmojiBoxVisible) return@repeat
|
||||
delay(16)
|
||||
}
|
||||
isVoiceRecording = true
|
||||
isVoiceRecordTransitioning = false
|
||||
if (recordUiState == RecordUiState.PRESSING || recordUiState == RecordUiState.IDLE) {
|
||||
setRecordUiState(RecordUiState.RECORDING, "voice-recorder-started")
|
||||
}
|
||||
inputJumpLog(
|
||||
"startVoiceRecording ui-enter mode=$recordMode state=$recordUiState voice=$isVoiceRecording kb=$isKeyboardVisible " +
|
||||
"emojiBox=${coordinator.isEmojiBoxVisible} transitioning=$isVoiceRecordTransitioning " +
|
||||
"panelH=$inputPanelHeightPx recH=$recordingInputRowHeightPx"
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
isVoiceRecordTransitioning = false
|
||||
inputJumpLog("startVoiceRecording launch failed: ${e.message}")
|
||||
}
|
||||
isVoiceRecording = true
|
||||
isVoiceRecordTransitioning = false
|
||||
if (recordUiState == RecordUiState.PRESSING || recordUiState == RecordUiState.IDLE) {
|
||||
setRecordUiState(RecordUiState.RECORDING, "voice-recorder-started")
|
||||
}
|
||||
inputJumpLog(
|
||||
"startVoiceRecording ui-enter mode=$recordMode state=$recordUiState voice=$isVoiceRecording kb=$isKeyboardVisible " +
|
||||
"emojiBox=${coordinator.isEmojiBoxVisible} transitioning=$isVoiceRecordTransitioning " +
|
||||
"panelH=$inputPanelHeightPx recH=$recordingInputRowHeightPx"
|
||||
)
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
isVoiceRecordTransitioning = false
|
||||
@@ -1420,8 +1425,9 @@ fun MessageInputBar(
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
pendingRecordAfterPermission = false
|
||||
isVoiceRecordTransitioning = false
|
||||
resetGestureState()
|
||||
if (isVoiceRecording) {
|
||||
if (isVoiceRecording || voiceRecorder != null) {
|
||||
stopVoiceRecording(send = false)
|
||||
} else {
|
||||
setRecordUiState(RecordUiState.IDLE, "dispose")
|
||||
|
||||
Reference in New Issue
Block a user