Skip to content

Keep recording controls stable during transcription setup - #151

Merged
tiensonqin merged 1 commit into
mainfrom
codex/recording-controls-layout
Nov 4, 2026
Merged

tiensonqin merged 1 commit into
mainfrom
codex/recording-controls-layout

Conversation

@tiensonqin

@tiensonqin tiensonqin commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

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.


Devin Review

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 potential issues.

Devin Review

Comment on lines +222 to +225
if (state.preparing || state.preparingTranscription) {
LinearProgressIndicator(modifier = Modifier.fillMaxWidth())
Text(stringResource(R.string.audio_preparing), modifier = Modifier.padding(top = 8.dp))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +220 to +225
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))
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +226 to +229
if (state.transcript.isNotBlank() || (!state.preparing && !state.preparingTranscription)) {
SelectionContainer {
Text(state.transcript.ifBlank { stringResource(R.string.audio_listening) },
modifier = Modifier.testTag("audio.transcript"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 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.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +441 to 456
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)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 iOS transcript height remains adaptive

Unlike the Android region, the iOS ScrollView has no fixed height. Check whether transcript growth still resizes the sheet despite the pinned recording button.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

@tiensonqin
tiensonqin merged commit 858b4a8 into main Nov 4, 2026
4 of 5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant