Skip to content

fix(react-native): stop hard-coding Android output to the speaker - #965

Open
chinmayv095 wants to merge 1 commit into
elevenlabs:mainfrom
chinmayv095:fix/react-native-audio-output-routing
Open

fix(react-native): stop hard-coding Android output to the speaker#965
chinmayv095 wants to merge 1 commit into
elevenlabs:mainfrom
chinmayv095:fix/react-native-audio-output-routing

Conversation

@chinmayv095

Copy link
Copy Markdown
Contributor

Fixes #881.

What happens

reactNativeSessionSetup in packages/react-native/src/index.react-native.ts calls AudioSession.configureAudio on every startSession(), with android.preferredOutputList hard-coded to ["speaker"]:

await AudioSession.configureAudio({
  android: {
    preferredOutputList: ["speaker"],
    audioTypeOptions: AndroidAudioTypePresets.communication,
  },
  ios: {
    defaultOutput: "speaker",
  },
});

On Android, preferredOutputList is 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 calls configureAudio before every session, an app can't work around this: any app-level AudioSession.configureAudio(...) called beforehand is overwritten by the SDK's own copy on the next startSession().

iOS is materially less affected, as the issue notes: the native module keeps .allowBluetooth/.allowBluetoothA2DP in the category options regardless, and defaultOutput: "speaker" only adds .defaultToSpeaker, which yields to a connected headset. Verified the premise against main (dce9815): the Android hard-coding is unchanged, iOS is as described.

What this does

@livekit/react-native's own AudioSession.configureAudio documents a default preferredOutputList order when the key is omitted: bluetooth, then headset, then speaker, then earpiece — a phone-call-like routing order. The SDK now omits preferredOutputList unless 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.audioSession session option lets a caller override android.preferredOutputList and/or ios.defaultOutput explicitly; anything not overridden keeps the new default. This is scoped to exactly the two fields the issue is about, not a full passthrough of AudioConfiguration (which also covers Android audio focus/mode/stream-type options unrelated to this bug).

Tests

packages/react-native has no test infrastructure (no vitest config, no .test.ts files anywhere in the package, and no prior commit touching index.react-native.ts has 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.defaultOutput falls back to "speaker" when not overridden) in isolation against the exact expression used in the source. packages/client, packages/react-native and elevenlabs-react-native-example all type-check and lint clean (turbo check-types lint build, 29/29 tasks). The new webRtc.reactNative field is additive to BaseSessionConfig in packages/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).

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.
@cursor

cursor Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

PR Summary

Low Risk
Narrow, session-start audio configuration change with additive typing; default behavior improves headset routing without touching auth or data paths.

Overview
Fixes Android conversation audio always playing on the phone speaker by no longer passing preferredOutputList: ["speaker"] into AudioSession.configureAudio on every startSession(). When that key is omitted, LiveKit’s native default order (bluetooth → headset → speaker → earpiece) applies so connected headsets are used.

Adds an optional webRtc.reactNative.audioSession on session config (@elevenlabs/client) so apps can still set android.preferredOutputList and/or ios.defaultOutput; iOS continues to default to "speaker" when not overridden.

Reviewed by Cursor Bugbot for commit 554a414. Bugbot is set up for automated code reviews on this repo. Configure here.

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.

react-native: hard-coded preferredOutputList ["speaker"] blocks Bluetooth/wired headset audio on Android

1 participant