feat: add iOS audio output selection methods#2334
Conversation
# Conflicts: # packages/react-native-callingx/ios/AudioSessionManager.swift # packages/react-native-sdk/src/modules/call-manager/CallManager.ts # packages/react-native-sdk/src/modules/call-manager/native-module.d.ts # sample-apps/react-native/dogfood/src/components/CallControlls/MoreActionsButton/AndroidAudioRoutePickerDrawer.tsx # sample-apps/react-native/dogfood/src/components/CallControls/MoreActionsButton/AndroidAudioRoutePickerDrawer.tsx # sample-apps/react-native/dogfood/src/components/CallControls/MoreActionsButton/AudioRoutePickerDrawer.tsx
Superseded by this branch's iOS audio device selection work; merged with strategy=ours to preserve his contribution in history without changing the current implementation. Co-Authored-By: ajit <ajitpatel7389@gmail.com>
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe PR unifies audio-device state and selection across Android and iOS using stable device IDs. It adds native route-change events, iOS route persistence and reapplication, Android ID-based endpoint handling, a shared CallManager API, a status hook, updated tests, and a cross-platform route picker. ChangesAudio device contracts and manager
Android stable-id routing
iOS route selection and events
Picker integration and validation
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant Picker
participant CallManager
participant NativeManager
participant AVAudioSession
Picker->>CallManager: audioDevices.select(deviceId)
CallManager->>NativeManager: chooseAudioDeviceEndpoint(deviceId)
NativeManager->>AVAudioSession: apply output routing
AVAudioSession-->>CallManager: audioDeviceChanged
CallManager-->>Picker: updated AudioDevicesState
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Bundle sizeBuilt package output. Sizes in KB; delta vs
|
There was a problem hiding this comment.
Actionable comments posted: 7
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt (2)
452-458: 🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy liftPreserve the explicit user selection across device-state updates.
After selecting speaker or earpiece, any later endpoint update recomputes
WH → BT → defaultand overwrites that choice. Retain the selected device ID until it disconnects, then fall back to automatic priority routing.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt` around lines 452 - 458, Update the device selection logic around newAudioDevice to retain the explicitly selected speaker or earpiece ID across endpoint updates. Reuse the stored selection while that device remains connected; clear it when disconnected, then preserve the existing WH → BT → default automatic priority routing.
257-260: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winHandle
setCommunicationDevice()failures before updating the selected device
setCommunicationDevice(...)returnsfalsewhen the system rejects the route. Don’t assignselectedAudioDeviceEndpointunless the call succeeds; otherwise JS can show a Bluetooth device that was never activated.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt` around lines 257 - 260, Update the Android 31+ branch in the audio device selection flow to check the boolean result from setCommunicationDevice before calling bluetoothManager.updateDevice() or assigning selectedAudioDeviceEndpoint. Only update and select btDevice when the communication route is successfully accepted; preserve the existing behavior for failed calls without marking the device active.
🧹 Nitpick comments (1)
packages/react-native-sdk/__tests__/call-manager/CallManager.test.ts (1)
78-113: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winCover the non-Telecom and iOS branches of the public API.
Add tests for native
getStatus/select,didChangeAudioRouterefresh and cleanup, and rejected status reads. Current coverage validates only the Android Telecom branch of this cross-platform manager.As per coding guidelines, “add tests for new public APIs, bug fixes, and performance-sensitive utilities.”
Also applies to: 202-226
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/react-native-sdk/__tests__/call-manager/CallManager.test.ts` around lines 78 - 113, Expand the CallManager audioDevices tests beyond the Android Telecom path: cover native getStatus and select behavior, iOS routing, didChangeAudioRoute-triggered refresh and listener cleanup, and rejected status reads. Reuse the existing loadCallManager, makeNativeManager, and makeCallingx fixtures while asserting the public API results and relevant native/CallingX calls.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioManagerUtil.kt`:
- Around line 111-117: The Bluetooth endpoint resolution in the relevant
AudioManagerUtil method must rely exclusively on
audioManager.communicationDevice.id and never fall back to matching
bluetoothManager.getDeviceName(). Return null when the active ID is absent or
not present in endpointMaps.bluetoothEndpoints, while preserving any
caller-provided requested ID in the corresponding logic around the additionally
referenced lines.
In `@packages/react-native-sdk/ios/StreamInCallManager.swift`:
- Around line 547-570: Update buildAudioDevicesState to include active output
routes alongside selectable inputs, deduplicating devices that represent the
same route. Derive selectedDeviceId from the active route’s output when present,
while preserving the speaker identifier and input-based selection for routes
without a distinct output device; ensure wired and other output-only devices
appear in devices.
- Around line 420-434: Update setForceSpeakerphoneOn’s audio-session branch to
route both enabled and disabled states through applyOutputRouting(...), ensuring
that helper also updates selectedOutput consistently. Remove the direct
overrideOutputAudioPort and setPreferredInput calls from this path while
preserving the existing configuration locking and error logging.
- Around line 464-526: Update chooseAudioDeviceEndpoint so the requested routing
is validated and applied before assigning it to selectedOutput. Keep the
previous selectedOutput unchanged when setConfiguration or applyOutputRouting
fails, and commit the new routing only after both operations succeed; ensure
makeAudioConfiguration and applyConfigForEngineEnable continue using only
successfully applied routes.
In `@packages/react-native-sdk/src/modules/call-manager/CallManager.ts`:
- Around line 112-120: Wrap the native calls introduced in
ensureInterruptionReassert and the related handlers around
chooseAudioDeviceEndpoint, reapplyAudioRoute, and default-endpoint forwarding in
try-catch blocks, and handle any returned promise rejections. Keep the existing
event/action behavior while preventing bridge errors from escaping unhandled.
- Around line 204-214: Make asynchronous audio-status updates lifecycle-safe in
packages/react-native-sdk/src/modules/call-manager/CallManager.ts lines 204-214
and packages/react-native-sdk/src/hooks/useAudioDeviceStatus.ts lines 26-35. In
the CallManager route listener, track subscription activity so callbacks after
unsubscribe are ignored, and handle getStatus() rejections. In
useAudioDeviceStatus, prevent the initial request from replacing newer
event-driven state, ignore all updates after unmount, wrap native calls in
try-catch, and handle promise rejections.
In `@packages/react-native-sdk/src/modules/call-manager/types.ts`:
- Around line 12-16: Update the device identity documentation around
AudioDevice.id to distinguish Android sources: specify that Telecom devices use
Callingx endpoint IDs, while AudioDeviceInfo.id applies only to SDK-managed
Android routing. Preserve the existing iOS ID descriptions and the stable-ID
guidance.
---
Outside diff comments:
In
`@packages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.kt`:
- Around line 452-458: Update the device selection logic around newAudioDevice
to retain the explicitly selected speaker or earpiece ID across endpoint
updates. Reuse the stored selection while that device remains connected; clear
it when disconnected, then preserve the existing WH → BT → default automatic
priority routing.
- Around line 257-260: Update the Android 31+ branch in the audio device
selection flow to check the boolean result from setCommunicationDevice before
calling bluetoothManager.updateDevice() or assigning
selectedAudioDeviceEndpoint. Only update and select btDevice when the
communication route is successfully accepted; preserve the existing behavior for
failed calls without marking the device active.
---
Nitpick comments:
In `@packages/react-native-sdk/__tests__/call-manager/CallManager.test.ts`:
- Around line 78-113: Expand the CallManager audioDevices tests beyond the
Android Telecom path: cover native getStatus and select behavior, iOS routing,
didChangeAudioRoute-triggered refresh and listener cleanup, and rejected status
reads. Reuse the existing loadCallManager, makeNativeManager, and makeCallingx
fixtures while asserting the public API results and relevant native/CallingX
calls.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 45b72234-211c-4ca5-b3af-74d69d94eaaf
⛔ Files ignored due to path filters (1)
sample-apps/react-native/dogfood/ios/Podfile.lockis excluded by!**/*.lock
📒 Files selected for processing (16)
packages/react-native-callingx/ios/AudioSessionManager.swiftpackages/react-native-callingx/ios/CallingxImpl.swiftpackages/react-native-callingx/src/types.tspackages/react-native-sdk/__tests__/call-manager/CallManager.test.tspackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/AudioDeviceManager.ktpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/audio/utils/AudioManagerUtil.ktpackages/react-native-sdk/android/src/main/java/com/streamvideo/reactnative/callmanager/StreamInCallManagerModule.ktpackages/react-native-sdk/ios/StreamInCallManager.mpackages/react-native-sdk/ios/StreamInCallManager.swiftpackages/react-native-sdk/src/hooks/index.tspackages/react-native-sdk/src/hooks/useAudioDeviceStatus.tspackages/react-native-sdk/src/modules/call-manager/CallManager.tspackages/react-native-sdk/src/modules/call-manager/native-module.d.tspackages/react-native-sdk/src/modules/call-manager/types.tssample-apps/react-native/dogfood/src/components/CallControls/MoreActionsButton/AudioRoutePickerDrawer.tsxsample-apps/react-native/dogfood/src/components/CallControls/MoreActionsButton/index.tsx
✅ Action performedReview finished.
|
# Conflicts: # packages/react-native-callingx/ios/CallingxImpl.swift
💡 Overview
Adds a single, cross-platform audio output device picker for the React Native SDK.
One API —
callManager.audioDevicesand theuseAudioDeviceStatus()hook📝 Implementation notes
Changed method names and behaviour:
callManager.android→callManager.audioDevices.getAudioDeviceStatus()→getStatus();selectAudioDevice(name)→select(deviceId);addAudioDeviceChangeListener()→addChangeListener().AudioDeviceStatustype removed → useAudioDevicesState+AudioDevice.🎫 Ticket: https://linear.app/stream/issue/XYZ-123
📑 Docs: https://github.com/GetStream/docs-content/pull/1442
Summary by CodeRabbit
useAudioDeviceStatus.didChangeAudioRouteevent for audio-route updates.AudioDevicesState/AudioDevicewithselectedDeviceId.reapplyAudioRoute.AudioDevicesStateshape.