Fix Android speaker: use music stream type instead of voiceCall

- audioStreamType: music (routes to SPEAKER by default)
- audioMode: normal (not inCommunication which uses earpiece)
- audioAttributesUsageType: media
- audioAttributesContentType: music

Previous voiceCall stream was routing to earpiece on Android devices.
This commit is contained in:
Sergei 2026-01-25 13:12:16 -08:00
parent 8240e51bc5
commit cd4137ef36

View File

@ -93,50 +93,32 @@ export async function configureAudioForVoiceCall(): Promise<void> {
} }
} else if (Platform.OS === 'android') { } else if (Platform.OS === 'android') {
// Android-specific configuration - FORCE SPEAKER OUTPUT // Android-specific configuration - FORCE SPEAKER OUTPUT
// SOLUTION: Use 'inCommunication' for echo cancellation + forceHandleAudioRouting + explicit speaker selection // CRITICAL: Use 'music' stream type - it defaults to SPEAKER!
console.log('[AudioSession] Configuring Android audio for SPEAKER with echo cancellation...'); // 'voiceCall' stream type defaults to EARPIECE on many Android devices
console.log('[AudioSession] Configuring Android audio for SPEAKER...');
await AudioSession.configureAudio({ await AudioSession.configureAudio({
android: { android: {
// Force speaker as preferred output // Use MEDIA mode to ensure speaker output
preferredOutputList: ['speaker'],
// CRITICAL: This flag forces audio routing even in communication mode
forceHandleAudioRouting: true,
audioTypeOptions: { audioTypeOptions: {
manageAudioFocus: true, manageAudioFocus: true,
// Use 'inCommunication' for echo cancellation (important for voice calls!) audioMode: 'normal',
audioMode: 'inCommunication',
audioFocusMode: 'gain', audioFocusMode: 'gain',
// Voice call stream type for proper routing // Use 'music' stream - goes to speaker by default!
audioStreamType: 'voiceCall', audioStreamType: 'music',
audioAttributesUsageType: 'voiceCommunication', audioAttributesUsageType: 'media',
audioAttributesContentType: 'speech', audioAttributesContentType: 'music',
}, },
// Force speaker as output
preferredOutputList: ['speaker'],
// Allow us to control audio routing
forceHandleAudioRouting: true,
}, },
}); });
console.log('[AudioSession] Starting Android audio session...'); console.log('[AudioSession] Starting Android audio session...');
await AudioSession.startAudioSession(); await AudioSession.startAudioSession();
console.log('[AudioSession] Android speaker mode configured!');
// CRITICAL: Explicitly select speaker AFTER session starts
// This overrides the default earpiece routing of inCommunication mode
try {
console.log('[AudioSession] Explicitly selecting speaker output...');
await AudioSession.selectAudioOutput('speaker');
console.log('[AudioSession] Speaker output explicitly selected!');
} catch (speakerErr) {
console.warn('[AudioSession] selectAudioOutput failed, trying showAudioRoutePicker:', speakerErr);
// Fallback: try to show audio route picker or use alternative method
try {
if (AudioSession.showAudioRoutePicker) {
await AudioSession.showAudioRoutePicker();
}
} catch (pickerErr) {
console.warn('[AudioSession] showAudioRoutePicker also failed:', pickerErr);
}
}
console.log('[AudioSession] Android speaker mode with echo cancellation configured!');
} }
console.log('[AudioSession] Configuration complete!'); console.log('[AudioSession] Configuration complete!');
@ -210,30 +192,22 @@ export async function reconfigureAudioForPlayback(): Promise<void> {
}); });
console.log('[AudioSession] iOS reconfigured for speaker playback'); console.log('[AudioSession] iOS reconfigured for speaker playback');
} else if (Platform.OS === 'android') { } else if (Platform.OS === 'android') {
// Reconfigure Android - force speaker while keeping echo cancellation // Reconfigure Android audio to ensure speaker output
// Using 'music' stream type to force speaker
await AudioSession.configureAudio({ await AudioSession.configureAudio({
android: { android: {
preferredOutputList: ['speaker'],
forceHandleAudioRouting: true,
audioTypeOptions: { audioTypeOptions: {
manageAudioFocus: true, manageAudioFocus: true,
audioMode: 'inCommunication', // Keep for echo cancellation audioMode: 'normal',
audioFocusMode: 'gain', audioFocusMode: 'gain',
audioStreamType: 'voiceCall', audioStreamType: 'music',
audioAttributesUsageType: 'voiceCommunication', audioAttributesUsageType: 'media',
audioAttributesContentType: 'speech', audioAttributesContentType: 'music',
}, },
preferredOutputList: ['speaker'],
forceHandleAudioRouting: true,
}, },
}); });
// Explicitly select speaker output
try {
await AudioSession.selectAudioOutput('speaker');
console.log('[AudioSession] Android speaker explicitly selected');
} catch (err) {
console.warn('[AudioSession] selectAudioOutput failed in reconfigure:', err);
}
console.log('[AudioSession] Android reconfigured for speaker playback'); console.log('[AudioSession] Android reconfigured for speaker playback');
} }
@ -276,29 +250,25 @@ export async function setAudioOutput(useSpeaker: boolean): Promise<void> {
}, },
}); });
} else if (Platform.OS === 'android') { } else if (Platform.OS === 'android') {
// Android: Keep inCommunication mode for echo cancellation, use explicit output selection // Android: Switch stream type to control speaker/earpiece
// - 'music' stream goes to speaker by default
// - 'voiceCall' stream goes to earpiece by default
await AudioSession.configureAudio({ await AudioSession.configureAudio({
android: { android: {
preferredOutputList: useSpeaker ? ['speaker'] : ['earpiece'],
forceHandleAudioRouting: true,
audioTypeOptions: { audioTypeOptions: {
manageAudioFocus: true, manageAudioFocus: true,
// Always use inCommunication for echo cancellation audioMode: useSpeaker ? 'normal' : 'inCommunication',
audioMode: 'inCommunication',
audioFocusMode: 'gain', audioFocusMode: 'gain',
audioStreamType: 'voiceCall', // Key difference: music→speaker, voiceCall→earpiece
audioAttributesUsageType: 'voiceCommunication', audioStreamType: useSpeaker ? 'music' : 'voiceCall',
audioAttributesContentType: 'speech', audioAttributesUsageType: useSpeaker ? 'media' : 'voiceCommunication',
audioAttributesContentType: useSpeaker ? 'music' : 'speech',
}, },
// Also set preferred output list
preferredOutputList: useSpeaker ? ['speaker'] : ['earpiece'],
forceHandleAudioRouting: true,
}, },
}); });
// Explicitly select output device
try {
await AudioSession.selectAudioOutput(useSpeaker ? 'speaker' : 'earpiece');
} catch (err) {
console.warn('[AudioSession] selectAudioOutput failed:', err);
}
} }
console.log(`[AudioSession] Audio output set to ${useSpeaker ? 'SPEAKER' : 'EARPIECE'}`); console.log(`[AudioSession] Audio output set to ${useSpeaker ? 'SPEAKER' : 'EARPIECE'}`);