Add audio output switcher for voice calls (Android speaker fix)

- Add Audio button during active calls to switch output
- Fallback to Speaker/Earpiece options when LiveKit API unavailable
- Speaker now works correctly on Android

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Sergei 2026-01-26 13:25:19 -08:00
parent 8dd8590c1c
commit 5b5cdf1098

View File

@ -45,6 +45,7 @@ import {
import { ConnectionState, Track } from 'livekit-client'; import { ConnectionState, Track } from 'livekit-client';
import { getToken, type BeneficiaryData } from '@/services/livekitService'; import { getToken, type BeneficiaryData } from '@/services/livekitService';
import { useAuth } from '@/contexts/AuthContext'; import { useAuth } from '@/contexts/AuthContext';
import { getAvailableAudioOutputs, selectAudioOutput, setAudioOutput } from '@/utils/audioSession';
// Register LiveKit globals (must be called before using LiveKit) // Register LiveKit globals (must be called before using LiveKit)
registerGlobals(); registerGlobals();
@ -612,6 +613,40 @@ export default function ChatScreen() {
endVoiceCallContext(); endVoiceCallContext();
}, [endVoiceCallContext, callState.callDuration]); }, [endVoiceCallContext, callState.callDuration]);
// Audio output picker
const showAudioPicker = useCallback(async () => {
const devices = await getAvailableAudioOutputs();
// If devices found from LiveKit API, use them
if (devices.length > 0) {
const buttons: any[] = devices.map(device => ({
text: device.name,
onPress: () => selectAudioOutput(device.id),
}));
buttons.push({ text: 'Cancel', style: 'cancel' });
Alert.alert('Audio Output', 'Select audio device:', buttons);
return;
}
// Fallback for Android (and iOS if no devices found)
// Show simple Speaker/Earpiece toggle using setAudioOutput()
Alert.alert(
'Audio Output',
'Select audio output:',
[
{
text: '🔊 Speaker',
onPress: () => setAudioOutput(true),
},
{
text: '📱 Earpiece',
onPress: () => setAudioOutput(false),
},
{ text: 'Cancel', style: 'cancel' },
]
);
}, []);
// Handle voice transcript entries - add to chat in real-time // Handle voice transcript entries - add to chat in real-time
const handleVoiceTranscript = useCallback((role: 'user' | 'assistant', text: string) => { const handleVoiceTranscript = useCallback((role: 'user' | 'assistant', text: string) => {
if (!text.trim()) return; if (!text.trim()) return;
@ -1025,6 +1060,15 @@ export default function ChatScreen() {
</Text> </Text>
</View> </View>
)} )}
{/* Audio output button - only during active call */}
{isCallActive && (
<TouchableOpacity
style={styles.audioButton}
onPress={showAudioPicker}
>
<Ionicons name="volume-high" size={20} color={AppColors.primary} />
</TouchableOpacity>
)}
<TextInput <TextInput
style={styles.input} style={styles.input}
@ -1238,6 +1282,17 @@ const styles = StyleSheet.create({
backgroundColor: AppColors.error, backgroundColor: AppColors.error,
borderColor: AppColors.error, borderColor: AppColors.error,
}, },
audioButton: {
width: 44,
height: 44,
borderRadius: 22,
backgroundColor: AppColors.surface,
justifyContent: 'center',
alignItems: 'center',
marginRight: Spacing.sm,
borderWidth: 1,
borderColor: AppColors.primary,
},
callActiveIndicator: { callActiveIndicator: {
width: '100%', width: '100%',
height: '100%', height: '100%',