Repository navigation
Keep recording controls stable during transcription setup - #151
Conversation
| if (state.preparing || state.preparingTranscription) { | ||
| LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) | ||
| Text(stringResource(R.string.audio_preparing), modifier = Modifier.padding(top = 8.dp)) | ||
| } |
There was a problem hiding this comment.
🟡 Preparation status hidden by long transcripts
After a long transcript, changing languages places preparingTranscription above the current scroll position. The preparation status stays hidden while recognition starts; the iOS AudioRecorderSheet uses the same ordering.
Learn more
The transcript scroll region preserves its position when its content changes. After enough text fills the 180 dp region, the user scrolls to the bottom automatically through LaunchedEffect(state.transcript) in AudioRecordingDialog. Starting a new transcription inserts progress at the top of this same region without changing state.transcript, leaving the status outside the viewport. AudioRecorderSheet also places its progress view before the existing transcript inside a scrolling region.
Example: Record enough speech to fill the transcript, then choose another language. The old transcript stays in view, but the new preparation message sits above it and remains unseen.
Recommended fix: Keep the preparation indicator outside the scrollable transcript content, or explicitly keep it visible while retaining the transcript in the fixed-height region. Apply the same layout rule to both platform sheets.
Was this helpful? React with 👍 or 👎 to provide feedback.
| if (request.transcriptionEnabled) { | ||
| Column(modifier = Modifier.padding(top = 12.dp).height(180.dp).verticalScroll(transcriptScroll)) { | ||
| if (state.preparing || state.preparingTranscription) { | ||
| LinearProgressIndicator(modifier = Modifier.fillMaxWidth()) | ||
| Text(stringResource(R.string.audio_preparing), modifier = Modifier.padding(top = 8.dp)) | ||
| } |
There was a problem hiding this comment.
🟡 Recording setup has no progress feedback
With transcription disabled, state.preparing has no visible indicator because preparation now lives inside the transcription-only region. The disabled Start button and 0:00 timer give no feedback while recording initializes.
Learn more
The service enters preparing while starting capture in start. The dialog disables Start during that state in AudioRecordingDialog. The only progress indicator now sits inside the region gated by request.transcriptionEnabled, so disabling transcription removes all setup feedback, including on Android versions where transcription is unavailable.
Example: Turn transcription off, close the dialog, then open a new recording. While the microphone initializes, Start cannot be pressed and only the idle 0:00 display appears; previously a preparation indicator appeared.
Recommended fix: Show recording preparation status independently of the transcription toggle, while keeping the timer and action layout stable.
Was this helpful? React with 👍 or 👎 to provide feedback.
| if (state.transcript.isNotBlank() || (!state.preparing && !state.preparingTranscription)) { | ||
| SelectionContainer { | ||
| Text(state.transcript.ifBlank { stringResource(R.string.audio_listening) }, | ||
| modifier = Modifier.testTag("audio.transcript")) |
There was a problem hiding this comment.
🟡 Failed recordings display a listening status
When microphone access fails, state.hasRecording stays false, but the transcript region displays “Listening…” once preparation ends. Users see a listening status beside the recording error.
Learn more
The dialog shows a placeholder whenever both preparation flags are false and the transcript is blank. AndroidAudioRecorder.start can fail before calling the recording service, leaving hasRecording false. The dialog then retains an enabled transcription option and shows its listening placeholder beside the error.
Example: On Android 12 or later, leave transcription enabled and deny microphone permission on first use. The dialog shows the microphone-access error and “Listening…” despite never starting capture.
Recommended fix: Require state.hasRecording before showing the listening placeholder; keep preparation and any existing transcript separately visible as needed.
Was this helpful? React with 👍 or 👎 to provide feedback.
| if recorder.hasRecording, transcriptionEnabled, !languages.isEmpty || recorder.isPreparingTranscription { | ||
| ScrollView { | ||
| Text(recorder.liveTranscript.isEmpty | ||
| ? CaptureLocalization.text("Listening…", locale: locale) : recorder.liveTranscript) | ||
| .frame(maxWidth: .infinity, alignment: .leading) | ||
| .textSelection(.enabled) | ||
| .accessibilityIdentifier("audio.transcript") | ||
| VStack(alignment: .leading, spacing: 12) { | ||
| if recorder.isPreparingTranscription { | ||
| ProgressView(CaptureLocalization.text("Preparing transcription…", locale: locale)) | ||
| } | ||
| if !recorder.liveTranscript.isEmpty || !recorder.isPreparingTranscription { | ||
| Text(recorder.liveTranscript.isEmpty | ||
| ? CaptureLocalization.text("Listening…", locale: locale) : recorder.liveTranscript) | ||
| .textSelection(.enabled) | ||
| .accessibilityIdentifier("audio.transcript") | ||
| } | ||
| } | ||
| .frame(maxWidth: .infinity, alignment: .leading) | ||
| } | ||
| .defaultScrollAnchor(.bottom) |
Starting transcription inserted a separate preparation row and removed it when recognition became ready, moving the recording button. Transcript growth also resized the Android dialog.
Pin the iOS recording action to the bottom safe area and show preparation status within the transcript region. Keep the Android timer row mounted and give preparation, listening, and transcript content a shared fixed-height scrolling region, so the dialog actions stay in place. Existing transcription remains visible during language changes.
Also reject invalid microphone formats before installing an audio tap. A simulator with no usable input otherwise triggers an uncaught AVFAudio exception.
Validation: simulator and signed iPhone builds pass; Android release Kotlin compilation passes. Runtime rendering captures startup and transcription-toggle layouts, and the unavailable-input path returns an error without crashing. Complete device bundle verification includes widgets, controls, App Shortcuts, share extension, Live Activity, and native core.