diff --git a/utils/audioSession.ts b/utils/audioSession.ts index e566f7f..d884e89 100644 --- a/utils/audioSession.ts +++ b/utils/audioSession.ts @@ -93,50 +93,32 @@ export async function configureAudioForVoiceCall(): Promise { } } else if (Platform.OS === 'android') { // Android-specific configuration - FORCE SPEAKER OUTPUT - // SOLUTION: Use 'inCommunication' for echo cancellation + forceHandleAudioRouting + explicit speaker selection - console.log('[AudioSession] Configuring Android audio for SPEAKER with echo cancellation...'); + // CRITICAL: Use 'music' stream type - it defaults to SPEAKER! + // 'voiceCall' stream type defaults to EARPIECE on many Android devices + console.log('[AudioSession] Configuring Android audio for SPEAKER...'); await AudioSession.configureAudio({ android: { - // Force speaker as preferred output - preferredOutputList: ['speaker'], - // CRITICAL: This flag forces audio routing even in communication mode - forceHandleAudioRouting: true, + // Use MEDIA mode to ensure speaker output audioTypeOptions: { manageAudioFocus: true, - // Use 'inCommunication' for echo cancellation (important for voice calls!) - audioMode: 'inCommunication', + audioMode: 'normal', audioFocusMode: 'gain', - // Voice call stream type for proper routing - audioStreamType: 'voiceCall', - audioAttributesUsageType: 'voiceCommunication', - audioAttributesContentType: 'speech', + // Use 'music' stream - goes to speaker by default! + audioStreamType: 'music', + audioAttributesUsageType: 'media', + audioAttributesContentType: 'music', }, + // Force speaker as output + preferredOutputList: ['speaker'], + // Allow us to control audio routing + forceHandleAudioRouting: true, }, }); console.log('[AudioSession] Starting Android audio session...'); await AudioSession.startAudioSession(); - - // 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] Android speaker mode configured!'); } console.log('[AudioSession] Configuration complete!'); @@ -210,30 +192,22 @@ export async function reconfigureAudioForPlayback(): Promise { }); console.log('[AudioSession] iOS reconfigured for speaker playback'); } 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({ android: { - preferredOutputList: ['speaker'], - forceHandleAudioRouting: true, audioTypeOptions: { manageAudioFocus: true, - audioMode: 'inCommunication', // Keep for echo cancellation + audioMode: 'normal', audioFocusMode: 'gain', - audioStreamType: 'voiceCall', - audioAttributesUsageType: 'voiceCommunication', - audioAttributesContentType: 'speech', + audioStreamType: 'music', + audioAttributesUsageType: 'media', + 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'); } @@ -276,29 +250,25 @@ export async function setAudioOutput(useSpeaker: boolean): Promise { }, }); } 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({ android: { - preferredOutputList: useSpeaker ? ['speaker'] : ['earpiece'], - forceHandleAudioRouting: true, audioTypeOptions: { manageAudioFocus: true, - // Always use inCommunication for echo cancellation - audioMode: 'inCommunication', + audioMode: useSpeaker ? 'normal' : 'inCommunication', audioFocusMode: 'gain', - audioStreamType: 'voiceCall', - audioAttributesUsageType: 'voiceCommunication', - audioAttributesContentType: 'speech', + // Key difference: music→speaker, voiceCall→earpiece + audioStreamType: useSpeaker ? 'music' : 'voiceCall', + 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'}`);