Skip to content

Read media durations as natural language for TalkBack#6465

Merged
andremion merged 5 commits into
developfrom
fix/compose-talkback-media-duration
May 27, 2026
Merged

Read media durations as natural language for TalkBack#6465
andremion merged 5 commits into
developfrom
fix/compose-talkback-media-duration

Conversation

@andremion
Copy link
Copy Markdown
Contributor

@andremion andremion commented May 22, 2026

AND-1180

Goal

TalkBack used to read media durations character by character ("zero colon two three"), which is slow and hard to parse. This PR makes durations spoken as natural language ("23 seconds", "1 minute 23 seconds") across every surface that displays a duration in the Compose SDK.

Implementation

  • New spokenDuration helper in PlaybackTimer.kt produces a speech-friendly duration string via MeasureFormat (API 24+), falling back to the existing ChatTheme.durationFormatter on older API levels. A non-Composable variant is exposed for call sites that build strings outside Compose (QuotedMessageBodyBuilder).
  • Applied as contentDescription on every visible mm:ss Text while keeping the displayed text unchanged:
    • Audio recording playback timer (PlaybackTimerText)
    • Composer audio recording — active, locked, and overview playback rows (AudioRecordingContent)
    • Video player playback time (VideoPlaybackControls)
    • Photos / Videos thumbnails — VideoBadge and AudioBadge (MediaBadges)
    • Quoted-message preview of an audio recording (QuotedMessageBody.spokenText populated by QuotedMessageBodyBuilder, consumed by QuotedMessageText)

No visible UI changes; the rendered text and layout are unchanged.

Testing

  1. Enable TalkBack.
  2. Record and send a voice message. While recording, focus the duration counter — it should announce "X seconds" / "X minutes Y seconds" instead of digit-by-digit.
  3. Lock the recording and let it run for a few seconds. Focus the overview playback row — same natural-language announcement.
  4. Play back the attached audio in the message list — same announcement on the playback timer.
  5. Quote a voice-message message. The quoted preview should announce "Voice message X seconds".
  6. Send a video. On the Photos & Videos list, focus the thumbnail badge — it should announce the duration as natural language.
  7. Open the full-screen video player — same announcement on the time counter.

Summary by CodeRabbit

  • New Features
    • Added voice-accessible duration descriptions for audio and media playback components, including playback controls, progress timers, media badges, quoted messages, and audio recording interfaces. Descriptions are locale-aware and integrate with screen readers and voice assistants.

Review Change Stack

@andremion andremion requested a review from a team as a code owner May 22, 2026 09:35
@andremion andremion added the pr:improvement Improvement label May 22, 2026
@andremion
Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled, or the PR is bot-authored.
  • An issue is linked (Linear ticket or GitHub issue), or the PR is bot-authored.

🎉 Great job! This PR is ready for review.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 4b174a5b-2697-4010-88f7-db7b89a54193

📥 Commits

Reviewing files that changed from the base of the PR and between acf931d and dbb2329.

📒 Files selected for processing (7)
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/VideoPlaybackControls.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/audio/PlaybackTimer.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/MediaBadges.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessage.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessageBodyBuilder.kt
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/AudioRecordingContent.kt
  • stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/QuotedMessageBodyBuilderTest.kt

Walkthrough

This pull request adds comprehensive accessibility enhancements by introducing a locale-aware spokenDuration helper that generates natural-language audio/video duration descriptions. The helper is applied across playback controls, media badges, audio recording UI, and quoted message audio attachments, ensuring consistent TalkBack-friendly duration descriptions throughout the chat interface.

Changes

Accessibility enhancements for audio/video durations

Layer / File(s) Summary
spokenDuration helper with ICU and API-level support
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/audio/PlaybackTimer.kt
Introduces composable and non-composable spokenDuration(durationInMs) functions that generate natural-language duration strings using ICU's MeasureFormat for API 24+ and fallback to ChatTheme.durationFormatter for older APIs. Adds MillisPerSecond and SecondsPerMinute constants.
Direct component duration accessibility
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/attachments/preview/internal/VideoPlaybackControls.kt, stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/messages/composer/internal/AudioRecordingContent.kt, stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/common/MediaBadges.kt
Updates duration Text displays in video playback, audio recording (Hold/Locked/Overview states), and media badges to expose spoken duration via semantics contentDescription modifiers.
Quoted message builder with locale and audio text generation
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessageBodyBuilder.kt
Extends QuotedMessageBodyBuilder to capture device Locale, refactors audio recording text helper to accept durationMs, and adds spokenTextForAudioRecording for locale-aware spoken text. Updates rememberBodyBuilder() to derive and inject Locale from configuration.
Quoted message model and display with spoken text
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/ui/components/messages/QuotedMessage.kt
Adds nullable spokenText field to QuotedMessageBody and wraps quoted text Text with semantics contentDescription when available.
Test updates for locale-aware builder behavior
stream-chat-android-compose/src/test/kotlin/io/getstream/chat/android/compose/ui/components/messages/QuotedMessageBodyBuilderTest.kt
Updates QuotedMessageBodyBuilderTest to explicitly pass Locale.US and verifies audio recording quoted messages now include expected spokenText.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Suggested reviewers

  • gpunto

Poem

🐰 Spoken durations now ring clear,
From Hold to Locked, we help them hear,
Locale-aware and API-wise,
Accessibility reaches new highs! 📱✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 27.78% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely describes the main change: making media durations spoken as natural language for TalkBack instead of character-by-character.
Description check ✅ Passed The description includes Goal, Implementation, and Testing sections with clear explanations. However, it lacks the UI Changes section with before/after screenshots/videos and the Contributor/Reviewer checklists from the template.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/compose-talkback-media-duration

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 22, 2026

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.82 MB 5.82 MB 0.00 MB 🟢
stream-chat-android-ui-components 11.02 MB 11.02 MB 0.00 MB 🟢
stream-chat-android-compose 12.44 MB 12.45 MB 0.00 MB 🟢

@andremion andremion enabled auto-merge (squash) May 22, 2026 09:43
andremion added 5 commits May 27, 2026 12:34
ConfigurationCompat.getLocales(...)[0] can return null on a degenerate
empty locale list. Crashing the app for a TalkBack content-description
is disproportionate; Locale.getDefault() is a graceful fallback that
costs nothing and stays correct.
The helper is used in audio, video, media badges, and quoted-message
builders. Moving it out of PlaybackTimer.kt into SpokenDuration.kt
(same package) makes it easier to locate without changing any call
sites.
… off

PlaybackTimerText and other playback / recording UIs recomputed the
spoken duration on every frame, re-resolving the locale and calling
MeasureFormat.getInstance(...) each time. Introduce
SpokenDurationFormatter, which selects a strategy once at construction
(locale-aware natural language on API 24+, the visible clock format
below), and a rememberSpokenDurationFormatter helper that resolves
locale once per composition and returns null when no accessibility
service is active — call sites then skip setting contentDescription
entirely in the common case.
PR #6441 refactored the recording overview content into
OverviewPlaybackButton and OverviewPlaybackRow, and put a merged-row
contentDescription on the row. During the rebase, the spoken-duration
contentDescription that lived on the inline duration Text was dropped
along with the inline body. Without it, TalkBack reads the visible
"0:00" as "audio recording, zero hundred". Restore the spoken-duration
contentDescription on the duration Text so the merged-row announcement
becomes "audio recording, 0 seconds" and tracks the visible position
while scrubbing.
@andremion andremion force-pushed the fix/compose-talkback-media-duration branch from ad66389 to 6efc257 Compare May 27, 2026 12:54
@andremion andremion requested review from VelikovPetar and gpunto May 27, 2026 12:55
@sonarqubecloud
Copy link
Copy Markdown

Quality Gate Failed Quality Gate failed

Failed conditions
63.6% Coverage on New Code (required ≥ 80%)

See analysis details on SonarQube Cloud

@andremion andremion disabled auto-merge May 27, 2026 13:29
@andremion andremion merged commit 1548887 into develop May 27, 2026
15 of 16 checks passed
@andremion andremion deleted the fix/compose-talkback-media-duration branch May 27, 2026 13:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:improvement Improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants