fix(react-native): stop hard-coding Android output to the speaker - #965
fix(react-native): stop hard-coding Android output to the speaker#965chinmayv095 wants to merge 1 commit into
Conversation
reactNativeSessionSetup passed preferredOutputList: ["speaker"] to AudioSession.configureAudio on every startSession call, which is the entire automatic output routing order on Android, not just a default. A connected Bluetooth or wired headset was never auto-selected, so conversation audio always played from the phone speaker regardless of what the user had connected, and an app could not override this since the SDK re-applies its own config before every session. preferredOutputList is now omitted unless the caller supplies one, so the native module's own default order applies: bluetooth, then headset, then speaker, then earpiece. A new webRtc.reactNative.audioSession option lets a caller still force a fixed order, or override the iOS defaultOutput, for apps that want different behavior.
PR SummaryLow Risk Overview Adds an optional Reviewed by Cursor Bugbot for commit 554a414. Bugbot is set up for automated code reviews on this repo. Configure here. |
Fixes #881.
What happens
reactNativeSessionSetupinpackages/react-native/src/index.react-native.tscallsAudioSession.configureAudioon everystartSession(), withandroid.preferredOutputListhard-coded to["speaker"]:On Android,
preferredOutputListis not a fallback preference, it's the entire automatic output routing order. Anything not on the list is never auto-selected, so["speaker"]removes Bluetooth and wired headsets from routing completely. A user with a connected Bluetooth earpiece hears nothing, since audio always plays from the phone speaker. Because the SDK callsconfigureAudiobefore every session, an app can't work around this: any app-levelAudioSession.configureAudio(...)called beforehand is overwritten by the SDK's own copy on the nextstartSession().iOS is materially less affected, as the issue notes: the native module keeps
.allowBluetooth/.allowBluetoothA2DPin the category options regardless, anddefaultOutput: "speaker"only adds.defaultToSpeaker, which yields to a connected headset. Verified the premise againstmain(dce9815): the Android hard-coding is unchanged, iOS is as described.What this does
@livekit/react-native's ownAudioSession.configureAudiodocuments a defaultpreferredOutputListorder when the key is omitted: bluetooth, then headset, then speaker, then earpiece — a phone-call-like routing order. The SDK now omitspreferredOutputListunless a caller supplies one, so that default applies instead of the hard override. Speaker-only behavior is unchanged when no headset or Bluetooth device is connected, since it's still the third preference.The issue's second proposed direction (make it overridable) is folded in rather than picked over the first, since a fixed default is still an opinion some apps will want to override (e.g. an app that always wants speaker output regardless of connected accessories). A new
webRtc.reactNative.audioSessionsession option lets a caller overrideandroid.preferredOutputListand/orios.defaultOutputexplicitly; anything not overridden keeps the new default. This is scoped to exactly the two fields the issue is about, not a full passthrough ofAudioConfiguration(which also covers Android audio focus/mode/stream-type options unrelated to this bug).Tests
packages/react-nativehas no test infrastructure (no vitest config, no.test.tsfiles anywhere in the package, and no prior commit touchingindex.react-native.tshas added one), so this doesn't introduce one for a single conditional-spread change. Verified the merge logic (override present → key included as given; override absent → key omitted entirely, letting the native default apply;ios.defaultOutputfalls back to"speaker"when not overridden) in isolation against the exact expression used in the source.packages/client,packages/react-nativeandelevenlabs-react-native-exampleall type-check and lint clean (turbo check-types lint build, 29/29 tasks). The newwebRtc.reactNativefield is additive toBaseSessionConfiginpackages/client, so it doesn't affect anything not opting in.Changeset included: patch on both
@elevenlabs/client(new option type) and@elevenlabs/react-native(behavior fix).