Skip to content

Commit 2c24653

Browse files
committed
RU-T47 Minor fixes
1 parent f93e920 commit 2c24653

3 files changed

Lines changed: 54 additions & 11 deletions

File tree

src/components/livekit/livekit-bottom-sheet.tsx

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { t } from 'i18next';
2+
import { Audio, InterruptionModeIOS } from 'expo-av';
23
import { Headphones, Mic, MicOff, PhoneOff, Settings } from 'lucide-react-native';
34
import { useColorScheme } from 'nativewind';
45
import React, { useCallback, useEffect, useRef, useState } from 'react';
@@ -146,6 +147,45 @@ export const LiveKitBottomSheet = () => {
146147
}
147148
}, [isConnected, currentRoomInfo]);
148149

150+
// Audio Routing Logic
151+
useEffect(() => {
152+
const updateAudioRouting = async () => {
153+
if (!selectedAudioDevices.speaker) return;
154+
155+
try {
156+
const speaker = selectedAudioDevices.speaker;
157+
console.log('Updating audio routing for:', speaker.type);
158+
159+
if (speaker.type === 'speaker') {
160+
// Force Speakerphone
161+
await Audio.setAudioModeAsync({
162+
allowsRecordingIOS: true,
163+
staysActiveInBackground: true,
164+
playsInSilentModeIOS: true,
165+
shouldDuckAndroid: true,
166+
playThroughEarpieceAndroid: false, // This forces speaker on Android
167+
interruptionModeIOS: InterruptionModeIOS.DoNotMix,
168+
});
169+
} else {
170+
// Default (Earpiece) or Bluetooth
171+
// For Bluetooth to work, we usually just need to NOT force speaker.
172+
await Audio.setAudioModeAsync({
173+
allowsRecordingIOS: true,
174+
staysActiveInBackground: true,
175+
playsInSilentModeIOS: true,
176+
shouldDuckAndroid: true,
177+
playThroughEarpieceAndroid: true, // This allows earpiece/bluetooth
178+
interruptionModeIOS: InterruptionModeIOS.DoNotMix,
179+
});
180+
}
181+
} catch (error) {
182+
console.error('Failed to update audio routing:', error);
183+
}
184+
};
185+
186+
updateAudioRouting();
187+
}, [selectedAudioDevices.speaker]);
188+
149189
const handleRoomSelect = useCallback(
150190
(room: DepartmentVoiceChannelResultData) => {
151191
connectToRoom(room, room.Token);
@@ -303,7 +343,7 @@ export const LiveKitBottomSheet = () => {
303343
<View className="w-full p-4">
304344
<HStack className="mb-4 items-center justify-between">
305345
<Text className="text-xl font-bold">{t('livekit.title')}</Text>
306-
{currentView === BottomSheetView.CONNECTED && (
346+
{currentView !== BottomSheetView.AUDIO_SETTINGS && (
307347
<TouchableOpacity onPress={handleShowAudioSettings} testID="header-audio-settings-button">
308348
<Headphones size={20} color="#6B7280" />
309349
</TouchableOpacity>
@@ -323,7 +363,7 @@ const styles = StyleSheet.create({
323363
content: {
324364
flex: 1,
325365
width: '100%',
326-
paddingHorizontal: 16,
366+
paddingHorizontal: 8,
327367
},
328368
roomList: {
329369
flex: 1,
@@ -334,6 +374,7 @@ const styles = StyleSheet.create({
334374
controls: {
335375
flexDirection: 'row',
336376
justifyContent: 'space-around',
377+
width: '100%',
337378
marginTop: 16,
338379
},
339380
controlButton: {

src/components/settings/__tests__/audio-device-selection.test.tsx

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ describe('AudioDeviceSelection', () => {
235235

236236
expect(screen.getAllByText('Available BT').length).toBeGreaterThan(0);
237237
expect(screen.queryByText('Unavailable BT')).toBeNull();
238-
expect(screen.getAllByText('Wired Device').length).toBeGreaterThan(0);
238+
expect(screen.queryByText('Wired Device')).toBeNull();
239239
});
240240

241241
it('filters out unavailable devices for speakers', () => {
@@ -247,9 +247,8 @@ describe('AudioDeviceSelection', () => {
247247
render(<AudioDeviceSelection />);
248248

249249
expect(screen.getAllByText('Available Device').length).toBeGreaterThan(0);
250-
// Note: The component actually shows ALL devices in microphone section unless they are unavailable bluetooth
251-
// So the unavailable speaker will show in microphone section but not speaker section
252-
expect(screen.getAllByText('Unavailable Device').length).toBeGreaterThan(0); // Shows in microphone section
250+
// Note: We now filter out unavailable devices from BOTH sections.
251+
expect(screen.queryByText('Unavailable Device')).toBeNull();
253252
});
254253
});
255254

@@ -275,9 +274,8 @@ describe('AudioDeviceSelection', () => {
275274

276275
render(<AudioDeviceSelection />);
277276

278-
// Device should appear but with fallback label
279-
expect(screen.getAllByText('Unknown Device').length).toBeGreaterThan(0);
280-
expect(screen.getAllByText('Unknown Device').length).toBeGreaterThan(0);
277+
// Device should be filtered out as type is unknown
278+
expect(screen.queryByText('Unknown Device')).toBeNull();
281279
});
282280
});
283281
});

src/components/settings/audio-device-selection.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,13 @@ export const AudioDeviceSelection: React.FC<AudioDeviceSelectionProps> = ({ show
7070
);
7171
};
7272

73-
const availableMicrophones = availableAudioDevices.filter((device) => (device.type === 'bluetooth' ? device.isAvailable : true));
73+
const availableMicrophones = availableAudioDevices.filter(
74+
(device) => device.isAvailable && (device.type === 'default' || device.type === 'microphone' || device.type === 'bluetooth' || device.type === 'wired')
75+
);
7476

75-
const availableSpeakers = availableAudioDevices.filter((device) => device.isAvailable);
77+
const availableSpeakers = availableAudioDevices.filter(
78+
(device) => device.isAvailable && (device.type === 'default' || device.type === 'speaker' || device.type === 'bluetooth' || device.type === 'wired')
79+
);
7680

7781
return (
7882
<ScrollView className="flex-1">

0 commit comments

Comments
 (0)