Skip to content

Expose completion callback for audio recording#6290

Merged
VelikovPetar merged 1 commit intodevelopfrom
feature/AND-1132_expose_complete_recording_completion_callback
Mar 26, 2026
Merged

Expose completion callback for audio recording#6290
VelikovPetar merged 1 commit intodevelopfrom
feature/AND-1132_expose_complete_recording_completion_callback

Conversation

@VelikovPetar
Copy link
Copy Markdown
Contributor

@VelikovPetar VelikovPetar commented Mar 25, 2026

Goal

Allow callers to observe when an audio recording completes and receive the resulting Attachment, so they can perform follow-up actions (e.g. immediately send the message) without relying on state observation.

Reason: A customer relied on the viewModel.completeRecording() completing synchronously, which was changed in: #6036. In this PR we introduce a convenience callback so that customers relying on viewModel.completeRecording() before specific operations have a mechanism to know when the operation is ready.

Implementation

  • Added an optional onComplete: ((Result<Attachment>) -> Unit)? parameter to completeRecording() in:
    • MessageComposerController
    • MessageComposerViewModel (Compose)
    • MessageComposerViewModel (UI Components)
  • When the callback is provided, completeRecordingSync() is used internally. On success the attachment is added to selectedAttachments before invoking the callback, so callers can safely build a message with it.
  • When no callback is provided, the existing fire-and-forget behavior is preserved.

UI Changes

No UI changes.

Testing

  1. Call completeRecording() without a callback — verify existing behavior is unchanged.
  2. Call completeRecording { result -> ... } — verify the callback fires with Result.Success<Attachment> after recording completes, and the attachment is present in selectedAttachments.

Summary by CodeRabbit

  • New Features
    • Enhanced voice recording with optional completion callbacks in the message composer, allowing applications to respond to recording completion events and handle attachment results and error states more effectively.

Co-Authored-By: Claude <noreply@anthropic.com>
@VelikovPetar VelikovPetar added the pr:new-feature New feature label Mar 25, 2026
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Mar 25, 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.

🎉 Great job! This PR is ready for review.

@sonarqubecloud
Copy link
Copy Markdown

@github-actions
Copy link
Copy Markdown
Contributor

SDK Size Comparison 📏

SDK Before After Difference Status
stream-chat-android-client 5.26 MB 5.26 MB 0.00 MB 🟢
stream-chat-android-offline 5.49 MB 5.49 MB 0.00 MB 🟢
stream-chat-android-ui-components 10.63 MB 10.63 MB 0.00 MB 🟢
stream-chat-android-compose 12.86 MB 12.86 MB 0.00 MB 🟢

@VelikovPetar VelikovPetar marked this pull request as ready for review March 25, 2026 14:18
@VelikovPetar VelikovPetar requested a review from a team as a code owner March 25, 2026 14:18
@VelikovPetar VelikovPetar changed the title AND-1132: Expose completion callback for audio recording Expose completion callback for audio recording Mar 25, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 25, 2026

Walkthrough

The completeRecording() method signature is updated across ViewModels and Controllers to accept an optional completion callback returning a Result<Attachment>. The controller implementation now supports both synchronous recording completion with callback invocation and asynchronous completion when no callback is provided.

Changes

Cohort / File(s) Summary
Compose Module - ViewModel & API
stream-chat-android-compose/api/stream-chat-android-compose.api, stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/MessageComposerViewModel.kt
Updated MessageComposerViewModel.completeRecording() signature to accept optional ((Result<Attachment>) -> Unit)? callback. Added Result import and forwarding to controller method with callback parameter.
UI Common Module - Controller
stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/composer/MessageComposerController.kt
Enhanced completeRecording() to conditionally handle synchronous or asynchronous recording completion based on callback presence. When callback provided, launches coroutine to complete recording sync, append attachment to selectedAttachments, and invoke callback with result.
UI Components Module - ViewModel & API
stream-chat-android-ui-components/api/stream-chat-android-ui-components.api, stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageComposerViewModel.kt
Updated MessageComposerViewModel.completeRecording() signature to accept optional ((Result<Attachment>) -> Unit)? callback. Added Result import and forwarding to controller method with callback parameter.

Sequence Diagram(s)

sequenceDiagram
    participant VM as ViewModel
    participant Ctrl as MessageComposerController
    participant Audio as AudioRecordingController
    participant CB as Callback Handler

    alt With Completion Callback
        VM->>Ctrl: completeRecording(onComplete)
        Ctrl->>Ctrl: Launch Coroutine
        Ctrl->>Audio: completeRecordingSync()
        Audio-->>Ctrl: Attachment Result
        Ctrl->>Ctrl: Append to selectedAttachments
        Ctrl->>CB: onComplete(Result<Attachment>)
        CB-->>CB: Handle Result
    else Without Callback
        VM->>Ctrl: completeRecording(null)
        Ctrl->>Audio: completeRecording()
        Audio-->>Ctrl: Async completion
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Recording completes with callbacks now,
A gentle function takes a bow,
Sync or async, the choice is clear,
Results whisper in the ear! 🎙️✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The description covers Goal, Implementation, and Testing sections from the template. It clearly explains the motivation, implementation details, and testing approach, though UI Changes and Contributor Checklist sections are minimal or absent.
Title check ✅ Passed The title 'Expose completion callback for audio recording' accurately and concisely describes the main change: adding a completion callback parameter to the completeRecording() method across multiple components.

✏️ 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 feature/AND-1132_expose_complete_recording_completion_callback

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.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/MessageComposerViewModel.kt (1)

277-278: Consider adding KDoc for consistency.

Other public methods in this ViewModel (e.g., setMessageInput, sendMessage) include KDoc. Adding documentation for completeRecording would maintain consistency and help SDK consumers understand the callback behavior.

📝 Suggested KDoc
+    /**
+     * Completes audio recording and updates the composer attachments.
+     *
+     * `@param` onComplete Optional callback invoked with the result of the recording.
+     * On success, the recorded [Attachment] is added to [selectedAttachments] before
+     * the callback is invoked.
+     */
     public fun completeRecording(onComplete: ((Result<Attachment>) -> Unit)? = null): Unit =
         messageComposerController.completeRecording(onComplete)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/MessageComposerViewModel.kt`
around lines 277 - 278, Add a KDoc comment for the public function
MessageComposerViewModel.completeRecording explaining its purpose (finalizes the
current audio recording), the optional onComplete callback signature
((Result<Attachment>) -> Unit) and what values the Result conveys (success
contains the created Attachment, failure contains the error), and any
threading/ordering expectations; place the KDoc immediately above the
completeRecording method (which delegates to
messageComposerController.completeRecording) to match the style and detail used
for other public methods like setMessageInput and sendMessage.
stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/composer/MessageComposerController.kt (1)

925-930: Consider documenting callback execution context.

The KDoc describes what happens when onComplete is provided, but it would be helpful to document that the callback is invoked on the main/immediate dispatcher thread. This helps callers understand thread safety when performing UI updates in the callback.

📝 Suggested KDoc enhancement
  * `@param` onComplete Optional callback invoked with the result of the recording once the recording has been
  * finalized. On success, the recorded [Attachment] is added to [selectedAttachments] before the callback
- * is invoked, so callers can safely build and send a message using the received attachment.
+ * is invoked, so callers can safely build and send a message using the received attachment.
+ * The callback is invoked on the main thread.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/composer/MessageComposerController.kt`
around lines 925 - 930, Update the KDoc for MessageComposerController's
audio-completion method to state the execution context of the onComplete
callback: document that the onComplete callback (invoked after the recorded
Attachment is added to MessageComposerState.attachments/selectedAttachments) is
executed on the main (UI) dispatcher / immediate coroutine context so callers
can safely perform UI updates. Mention the dispatcher explicitly in the KDoc for
the method in MessageComposerController to make thread-safety expectations
clear.
stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageComposerViewModel.kt (1)

254-255: Consider adding KDoc for consistency.

Similar to the Compose ViewModel, this method would benefit from KDoc documentation to match other public methods in this class and help SDK consumers understand the callback behavior.

📝 Suggested KDoc
+    /**
+     * Completes audio recording and updates the composer attachments.
+     *
+     * `@param` onComplete Optional callback invoked with the result of the recording.
+     * On success, the recorded [Attachment] is added to [selectedAttachments] before
+     * the callback is invoked.
+     */
     public fun completeRecording(onComplete: ((Result<Attachment>) -> Unit)? = null): Unit =
         messageComposerController.completeRecording(onComplete)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageComposerViewModel.kt`
around lines 254 - 255, Add KDoc for the public function completeRecording in
MessageComposerViewModel describing what the method does, the optional
onComplete callback signature (Result<Attachment>), when/why the callback is
invoked, and any threading or lifecycle considerations; locate the function
declaration "completeRecording(onComplete: ((Result<Attachment>) -> Unit)? =
null): Unit" and add a concise `@param` for onComplete and a brief `@return/`@see if
applicable to match existing public method docs in this class.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/MessageComposerViewModel.kt`:
- Around line 277-278: Add a KDoc comment for the public function
MessageComposerViewModel.completeRecording explaining its purpose (finalizes the
current audio recording), the optional onComplete callback signature
((Result<Attachment>) -> Unit) and what values the Result conveys (success
contains the created Attachment, failure contains the error), and any
threading/ordering expectations; place the KDoc immediately above the
completeRecording method (which delegates to
messageComposerController.completeRecording) to match the style and detail used
for other public methods like setMessageInput and sendMessage.

In
`@stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/composer/MessageComposerController.kt`:
- Around line 925-930: Update the KDoc for MessageComposerController's
audio-completion method to state the execution context of the onComplete
callback: document that the onComplete callback (invoked after the recorded
Attachment is added to MessageComposerState.attachments/selectedAttachments) is
executed on the main (UI) dispatcher / immediate coroutine context so callers
can safely perform UI updates. Mention the dispatcher explicitly in the KDoc for
the method in MessageComposerController to make thread-safety expectations
clear.

In
`@stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageComposerViewModel.kt`:
- Around line 254-255: Add KDoc for the public function completeRecording in
MessageComposerViewModel describing what the method does, the optional
onComplete callback signature (Result<Attachment>), when/why the callback is
invoked, and any threading or lifecycle considerations; locate the function
declaration "completeRecording(onComplete: ((Result<Attachment>) -> Unit)? =
null): Unit" and add a concise `@param` for onComplete and a brief `@return/`@see if
applicable to match existing public method docs in this class.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: af54a94b-8238-426a-bd58-115de89fb0bc

📥 Commits

Reviewing files that changed from the base of the PR and between 6c63890 and e9368b8.

📒 Files selected for processing (5)
  • stream-chat-android-compose/api/stream-chat-android-compose.api
  • stream-chat-android-compose/src/main/java/io/getstream/chat/android/compose/viewmodel/messages/MessageComposerViewModel.kt
  • stream-chat-android-ui-common/src/main/kotlin/io/getstream/chat/android/ui/common/feature/messages/composer/MessageComposerController.kt
  • stream-chat-android-ui-components/api/stream-chat-android-ui-components.api
  • stream-chat-android-ui-components/src/main/kotlin/io/getstream/chat/android/ui/viewmodel/messages/MessageComposerViewModel.kt

@VelikovPetar VelikovPetar merged commit 0f268bd into develop Mar 26, 2026
17 of 18 checks passed
@VelikovPetar VelikovPetar deleted the feature/AND-1132_expose_complete_recording_completion_callback branch March 26, 2026 12:27
@stream-public-bot stream-public-bot added the released Included in a release label Mar 26, 2026
@stream-public-bot
Copy link
Copy Markdown
Contributor

🚀 Available in v6.36.0

andremion added a commit that referenced this pull request Apr 7, 2026
* Improve `Message.createdLocallyAt` creation logic using estimated server time (#6199)

* Fix createdLocallyAt using NTP-style server clock offset estimation

Co-Authored-By: Claude <noreply@anthropic.com>

* Pr remarks

* Adjust thread message createdLocallyAt.

* Ensure exceedsSyncThreshold is compared against estimated server time (where applicable).

* Add max allowed offset.

---------

Co-authored-by: Claude <noreply@anthropic.com>

* [skip ci] Update SDK sizes

* Update README cover image (#6282)

* Fix XML image flicker caused by `interceptorCoroutineContext(Dispatchers.IO)` (#6284)

Co-authored-by: Claude <noreply@anthropic.com>

* [skip ci] Update SDK sizes

* AUTOMATION: Version Bump

* Fix race condition in plugin resolution during disconnect (#6269)

* Update `DependencyResolverTest` to verify error handling when dependency resolution races with disconnection.

* Prevent race conditions during disconnects in `ChatClient`.

* Handle unresolvable attachments in picker (#6285)

- Update `StorageHelper` and `AttachmentMetaDataMapper` to safely handle cases where content URIs (e.g. cloud-backed files) cannot be opened.
- Introduce `hasUnresolvedAttachments` state in `AttachmentsPickerViewModel` to track failed attachment resolutions.
- Show a toast message in both View-based and Compose attachment pickers when files are unavailable and need to be downloaded to the device.
- Add `clearUnresolvedAttachments` to reset the error state after it has been consumed by the UI.
- Add unit tests for unresolved attachment scenarios in `AttachmentsPickerViewModelTest`.

* [skip ci] Update SDK sizes

* Fix wrong message selected on quoted message long click (#6292)

* Use type-specific attachment URL fields and deprecate `imagePreviewUrl` (#6280)

* Deprecate imagePreviewUrl and use type-specific attachment URL fields

Co-Authored-By: Claude <noreply@anthropic.com>

* Extract common extensions.

---------

Co-authored-by: Claude <noreply@anthropic.com>

* Expose optional completion callback for audio recording (#6290)

Co-authored-by: Claude <noreply@anthropic.com>

* AUTOMATION: Version Bump

* AUTOMATION: Clean Detekt Baseline Files (#6299)

Co-authored-by: adasiewiczr <17440581+adasiewiczr@users.noreply.github.com>

* Add support for intercepting CDN file requests (#6295)

* Add new CDN contract.

* Add CDN for document files.

* Add CDN support for downloading attachments.

* Deprecate current CDN methods.

* Add progress indicator snackbar.

* Add useDocumentGView config flag.

* Add file sharing cache handling.

* Add file sharing cache handling.

* Remove CDNResponse.kt

* Add tests

* PR remarks

* [skip ci] Update SDK sizes

* Post-merge clean-up.

* Post-merge clean-up.

* ApiDump.

* Improve attachment URI resolution and error handling in `AttachmentsPickerViewModel` and `AttachmentStorageHelper`.

- Add `isUriResolvable` to `StorageHelper` to verify if a content URI can be opened for reading.
- Implement `partitionResolvable` in `AttachmentStorageHelper` to separate metadata based on URI accessibility.
- Update `AttachmentsPickerViewModel.resolveAndSubmitUris` to exclude inaccessible URIs (e.g., undownloaded cloud files) from the submission.
- Ensure `hasUnresolvedAttachments` is correctly set when URIs are inaccessible, independent of file type support.
- Add unit tests in `AttachmentStorageHelperTest` and `AttachmentsPickerViewModelTest` to verify partitioning logic and view model state updates.

* Handle unresolvable attachments in XML

* apiDump.

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: André Mion <andremion@gmail.com>
Co-authored-by: Gianmarco <47775302+gpunto@users.noreply.github.com>
Co-authored-by: stream-pr-merger[bot] <117762243+stream-pr-merger[bot]@users.noreply.github.com>
Co-authored-by: adasiewiczr <17440581+adasiewiczr@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:new-feature New feature released Included in a release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants