Skip to content

Commit f7b8233

Browse files
committed
RU-T47 PR#209 fixes
1 parent c99bb09 commit f7b8233

14 files changed

Lines changed: 99 additions & 213 deletions

app.config.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ export default ({ config }: ConfigContext): ExpoConfig => ({
8181
'android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE',
8282
'android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK',
8383
'android.permission.READ_PHONE_STATE',
84-
'android.permission.READ_PHONE_NUMBERS',
8584
'android.permission.MANAGE_OWN_CALLS',
8685
],
8786
},

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@
168168
"react-native-restart": "0.0.27",
169169
"react-native-safe-area-context": "5.4.0",
170170
"react-native-screens": "~4.11.1",
171-
"react-native-sound": "^0.13.0",
171+
172172
"react-native-svg": "15.11.2",
173173
"react-native-web": "^0.20.0",
174174
"react-native-webview": "~13.13.1",

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,8 +130,8 @@ const mockAvailableRooms = [
130130
];
131131

132132
const mockSelectedAudioDevices = {
133-
microphone: { id: 'mic1', name: 'Default Microphone', type: 'default' as const, isAvailable: true },
134-
speaker: { id: 'speaker1', name: 'Default Speaker', type: 'default' as const, isAvailable: true },
133+
microphone: { id: 'mic1', name: 'Default Microphone', type: 'microphone' as const, isAvailable: true },
134+
speaker: { id: 'speaker1', name: 'Default Speaker', type: 'speaker' as const, isAvailable: true },
135135
};
136136

137137
describe('LiveKitBottomSheet', () => {

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

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

73-
const availableMicrophones = availableAudioDevices.filter((device) => device.isAvailable && (device.type === 'default' || device.type === 'microphone' || device.type === 'bluetooth' || device.type === 'wired'));
73+
const availableMicrophones = availableAudioDevices.filter((device) => device.isAvailable && (device.type === 'microphone' || device.type === 'bluetooth' || device.type === 'wired'));
7474

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

7777
return (
7878
<ScrollView className="flex-1">

src/components/settings/bluetooth-device-selection-bottom-sheet.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { BluetoothIcon, RefreshCwIcon, WifiIcon } from 'lucide-react-native';
22
import React, { useCallback, useEffect, useState } from 'react';
33
import { useTranslation } from 'react-i18next';
4-
import { showMessage } from 'react-native-flash-message';
54
import { Alert, useWindowDimensions } from 'react-native';
5+
import { showMessage } from 'react-native-flash-message';
66

77
import { Box } from '@/components/ui/box';
88
import { Button, ButtonIcon, ButtonText } from '@/components/ui/button';
@@ -108,7 +108,6 @@ export function BluetoothDeviceSelectionBottomSheet({ isOpen, onClose }: Bluetoo
108108
// Checking previous logs, service seems to play "connectedDevice".
109109

110110
onClose();
111-
112111
} catch (error) {
113112
logger.warn({
114113
message: 'Failed to connect to device',
@@ -119,7 +118,7 @@ export function BluetoothDeviceSelectionBottomSheet({ isOpen, onClose }: Bluetoo
119118
const errorMessage = error instanceof Error ? error.message : String(error);
120119
showMessage({
121120
message: t('bluetooth.connection_error_title') || 'Connection Failed',
122-
description: errorMessage === 'Device disconnected' ? t('bluetooth.device_disconnected') : (errorMessage || t('bluetooth.connection_error_message') || 'Could not connect to device'),
121+
description: errorMessage === 'Device disconnected' ? t('bluetooth.device_disconnected') : errorMessage || t('bluetooth.connection_error_message') || 'Could not connect to device',
123122
type: 'danger',
124123
duration: 4000,
125124
});

src/components/ui/actionsheet/index.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -392,11 +392,7 @@ const ActionsheetVirtualizedList = React.forwardRef<React.ElementRef<typeof UIAc
392392
const ActionsheetFlatList = React.forwardRef<React.ElementRef<typeof UIActionsheet.FlatList>, IActionsheetFlatListProps>(({ className, style, ...props }, ref) => {
393393
return (
394394
<View className={actionsheetFlatListStyle({ class: className })} style={style}>
395-
<UIActionsheet.FlatList
396-
ref={ref}
397-
estimatedItemSize={94}
398-
{...props}
399-
/>
395+
<UIActionsheet.FlatList ref={ref} estimatedItemSize={94} {...props} />
400396
</View>
401397
);
402398
});

src/components/ui/select/select-actionsheet.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -392,11 +392,7 @@ const ActionsheetVirtualizedList = React.forwardRef<React.ComponentRef<typeof UI
392392
const ActionsheetFlatList = React.forwardRef<React.ComponentRef<typeof UIActionsheet.FlatList>, IActionsheetFlatListProps>(function ActionsheetFlatList({ className, style, ...props }, ref) {
393393
return (
394394
<View className={actionsheetFlatListStyle({ class: className })} style={style}>
395-
<UIActionsheet.FlatList
396-
ref={ref}
397-
estimatedItemSize={94}
398-
{...props}
399-
/>
395+
<UIActionsheet.FlatList ref={ref} estimatedItemSize={94} {...props} />
400396
</View>
401397
);
402398
});

src/services/audio.service.ts

Lines changed: 51 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,18 @@
11
import { Asset } from 'expo-asset';
2-
import { Audio, type AVPlaybackSource, InterruptionModeIOS } from 'expo-av';
3-
import { Platform } from 'react-native';
4-
import Sound from 'react-native-sound';
2+
import { Audio, InterruptionModeIOS } from 'expo-av';
53

64
import { logger } from '@/lib/logging';
75

8-
// Enable playback in silence mode
9-
Sound.setCategory('Ambient', true);
10-
116
class AudioService {
127
private static instance: AudioService;
13-
// Use specific type for react-native-sound instances
14-
private startTransmittingSound: Sound | null = null;
15-
private stopTransmittingSound: Sound | null = null;
16-
17-
// Keep others as expo-av for now
18-
private connectedDeviceSound: Sound | null = null;
19-
private connectToAudioRoomSound: Sound | null = null;
20-
private disconnectedFromAudioRoomSound: Sound | null = null;
8+
9+
// Use expo-av Sound objects
10+
private startTransmittingSound: Audio.Sound | null = null;
11+
private stopTransmittingSound: Audio.Sound | null = null;
12+
private connectedDeviceSound: Audio.Sound | null = null;
13+
private connectToAudioRoomSound: Audio.Sound | null = null;
14+
private disconnectedFromAudioRoomSound: Audio.Sound | null = null;
15+
2116
private isInitialized = false;
2217

2318
private constructor() {
@@ -42,15 +37,12 @@ class AudioService {
4237

4338
try {
4439
// Configure audio mode for production builds
45-
// Note: react-native-sound handles its own session category (Ambient),
46-
// ensuring expo-av doesn't conflict is still good practice if used elsewhere.
4740
await Audio.setAudioModeAsync({
4841
allowsRecordingIOS: true,
4942
staysActiveInBackground: true,
5043
playsInSilentModeIOS: true,
5144
shouldDuckAndroid: true,
52-
// For speaker, we want false (speaker). For others, simple routing.
53-
playThroughEarpieceAndroid: false,
45+
playThroughEarpieceAndroid: false,
5446
interruptionModeIOS: InterruptionModeIOS.DoNotMix,
5547
});
5648

@@ -96,60 +88,25 @@ class AudioService {
9688

9789
private async loadAudioFiles(): Promise<void> {
9890
try {
99-
// Load start transmitting sound (react-native-sound)
100-
const startTransmittingSoundAsset = Asset.fromModule(require('@assets/audio/ui/space_notification1.mp3'));
101-
await startTransmittingSoundAsset.downloadAsync();
102-
const startUri = startTransmittingSoundAsset.localUri || startTransmittingSoundAsset.uri;
103-
104-
this.startTransmittingSound = new Sound(startUri, '', (error) => {
105-
if (error) {
106-
logger.error({ message: 'Failed to load start transmitting sound', context: { error } });
107-
}
108-
});
91+
// Load start transmitting sound
92+
const { sound: startSound } = await Audio.Sound.createAsync(require('@assets/audio/ui/space_notification1.mp3'));
93+
this.startTransmittingSound = startSound;
10994

110-
// Load stop transmitting sound (react-native-sound)
111-
const stopTransmittingSoundAsset = Asset.fromModule(require('@assets/audio/ui/space_notification2.mp3'));
112-
await stopTransmittingSoundAsset.downloadAsync();
113-
const stopUri = stopTransmittingSoundAsset.localUri || stopTransmittingSoundAsset.uri;
95+
// Load stop transmitting sound
96+
const { sound: stopSound } = await Audio.Sound.createAsync(require('@assets/audio/ui/space_notification2.mp3'));
97+
this.stopTransmittingSound = stopSound;
11498

115-
this.stopTransmittingSound = new Sound(stopUri, '', (error) => {
116-
if (error) {
117-
logger.error({ message: 'Failed to load stop transmitting sound', context: { error } });
118-
}
119-
});
99+
// Load connected device sound
100+
const { sound: connectedSound } = await Audio.Sound.createAsync(require('@assets/audio/ui/positive_interface_beep.mp3'));
101+
this.connectedDeviceSound = connectedSound;
120102

121-
// Load connected device sound (react-native-sound)
122-
const connectedDeviceSoundAsset = Asset.fromModule(require('@assets/audio/ui/positive_interface_beep.mp3'));
123-
await connectedDeviceSoundAsset.downloadAsync();
124-
const connectedUri = connectedDeviceSoundAsset.localUri || connectedDeviceSoundAsset.uri;
103+
// Load connect to audio room sound
104+
const { sound: connectRoomSound } = await Audio.Sound.createAsync(require('@assets/audio/ui/software_interface_start.mp3'));
105+
this.connectToAudioRoomSound = connectRoomSound;
125106

126-
this.connectedDeviceSound = new Sound(connectedUri, '', (error) => {
127-
if (error) {
128-
logger.error({ message: 'Failed to load connected device sound', context: { error } });
129-
}
130-
});
131-
132-
// Load connect to audio room sound (react-native-sound)
133-
const connectToAudioRoomSoundAsset = Asset.fromModule(require('@assets/audio/ui/software_interface_start.mp3'));
134-
await connectToAudioRoomSoundAsset.downloadAsync();
135-
const connectRoomUri = connectToAudioRoomSoundAsset.localUri || connectToAudioRoomSoundAsset.uri;
136-
137-
this.connectToAudioRoomSound = new Sound(connectRoomUri, '', (error) => {
138-
if (error) {
139-
logger.error({ message: 'Failed to load connect to audio room sound', context: { error } });
140-
}
141-
});
142-
143-
// Load disconnect from audio room sound (react-native-sound)
144-
const disconnectedFromAudioRoomSoundAsset = Asset.fromModule(require('@assets/audio/ui/software_interface_back.mp3'));
145-
await disconnectedFromAudioRoomSoundAsset.downloadAsync();
146-
const disconnectRoomUri = disconnectedFromAudioRoomSoundAsset.localUri || disconnectedFromAudioRoomSoundAsset.uri;
147-
148-
this.disconnectedFromAudioRoomSound = new Sound(disconnectRoomUri, '', (error) => {
149-
if (error) {
150-
logger.error({ message: 'Failed to load disconnect from audio room sound', context: { error } });
151-
}
152-
});
107+
// Load disconnect from audio room sound
108+
const { sound: disconnectRoomSound } = await Audio.Sound.createAsync(require('@assets/audio/ui/software_interface_back.mp3'));
109+
this.disconnectedFromAudioRoomSound = disconnectRoomSound;
153110

154111
logger.debug({
155112
message: 'Audio files loaded successfully',
@@ -162,21 +119,10 @@ class AudioService {
162119
}
163120
}
164121

165-
// Remove old playSound helper as we are using Sound directly now
166-
// private async playSound...
167-
168-
// Updated to use react-native-sound
169122
async playStartTransmittingSound(): Promise<void> {
170123
try {
171124
if (this.startTransmittingSound) {
172-
// Stop if playing
173-
this.startTransmittingSound.stop();
174-
// Play
175-
this.startTransmittingSound.play((success) => {
176-
if (!success) {
177-
logger.warn({ message: 'Failed to play start transmitting sound (playback error)' });
178-
}
179-
});
125+
await this.startTransmittingSound.replayAsync();
180126
}
181127
} catch (error) {
182128
logger.error({
@@ -186,19 +132,11 @@ class AudioService {
186132
}
187133
}
188134

189-
// Updated to use react-native-sound
190135
async playStopTransmittingSound(): Promise<void> {
191136
try {
192-
if (this.stopTransmittingSound) {
193-
// Stop if playing
194-
this.stopTransmittingSound.stop();
195-
// Play
196-
this.stopTransmittingSound.play((success) => {
197-
if (!success) {
198-
logger.warn({ message: 'Failed to play stop transmitting sound (playback error)' });
199-
}
200-
});
201-
}
137+
if (this.stopTransmittingSound) {
138+
await this.stopTransmittingSound.replayAsync();
139+
}
202140
} catch (error) {
203141
logger.error({
204142
message: 'Failed to play stop transmitting sound',
@@ -210,14 +148,8 @@ class AudioService {
210148
async playConnectedDeviceSound(): Promise<void> {
211149
try {
212150
if (this.connectedDeviceSound) {
213-
this.connectedDeviceSound.stop();
214-
this.connectedDeviceSound.play((success) => {
215-
if (success) {
216-
logger.debug({ message: 'Sound played successfully', context: { soundName: 'connectedDevice' } });
217-
} else {
218-
logger.warn({ message: 'Failed to play connected device sound (playback error)' });
219-
}
220-
});
151+
await this.connectedDeviceSound.replayAsync();
152+
logger.debug({ message: 'Sound played successfully', context: { soundName: 'connectedDevice' } });
221153
}
222154
} catch (error) {
223155
logger.error({
@@ -229,14 +161,9 @@ class AudioService {
229161

230162
async playConnectToAudioRoomSound(): Promise<void> {
231163
try {
232-
if (this.connectToAudioRoomSound) {
233-
this.connectToAudioRoomSound.stop();
234-
this.connectToAudioRoomSound.play((success) => {
235-
if (!success) {
236-
logger.warn({ message: 'Failed to play connect to audio room sound' });
237-
}
238-
});
239-
}
164+
if (this.connectToAudioRoomSound) {
165+
await this.connectToAudioRoomSound.replayAsync();
166+
}
240167
} catch (error) {
241168
logger.error({
242169
message: 'Failed to play connected to audio room sound',
@@ -247,14 +174,9 @@ class AudioService {
247174

248175
async playDisconnectedFromAudioRoomSound(): Promise<void> {
249176
try {
250-
if (this.disconnectedFromAudioRoomSound) {
251-
this.disconnectedFromAudioRoomSound.stop();
252-
this.disconnectedFromAudioRoomSound.play((success) => {
253-
if (!success) {
254-
logger.warn({ message: 'Failed to play disconnected from audio room sound' });
255-
}
256-
});
257-
}
177+
if (this.disconnectedFromAudioRoomSound) {
178+
await this.disconnectedFromAudioRoomSound.replayAsync();
179+
}
258180
} catch (error) {
259181
logger.error({
260182
message: 'Failed to play disconnected from audio room sound',
@@ -265,35 +187,21 @@ class AudioService {
265187

266188
async cleanup(): Promise<void> {
267189
try {
268-
// Unload start transmitting sound
269-
if (this.startTransmittingSound) {
270-
this.startTransmittingSound.release();
271-
this.startTransmittingSound = null;
272-
}
190+
const sounds = [this.startTransmittingSound, this.stopTransmittingSound, this.connectedDeviceSound, this.connectToAudioRoomSound, this.disconnectedFromAudioRoomSound];
273191

274-
// Unload stop transmitting sound
275-
if (this.stopTransmittingSound) {
276-
this.stopTransmittingSound.release();
277-
this.stopTransmittingSound = null;
278-
}
279-
280-
// Unload connected device sound
281-
if (this.connectedDeviceSound) {
282-
this.connectedDeviceSound.release();
283-
this.connectedDeviceSound = null;
284-
}
285-
286-
// Unload connect to audio room sound
287-
if (this.connectToAudioRoomSound) {
288-
this.connectToAudioRoomSound.release();
289-
this.connectToAudioRoomSound = null;
290-
}
291-
292-
// Unload disconnect from audio room sound
293-
if (this.disconnectedFromAudioRoomSound) {
294-
this.disconnectedFromAudioRoomSound.release();
295-
this.disconnectedFromAudioRoomSound = null;
296-
}
192+
await Promise.all(
193+
sounds.map(async (sound) => {
194+
if (sound) {
195+
await sound.unloadAsync();
196+
}
197+
})
198+
);
199+
200+
this.startTransmittingSound = null;
201+
this.stopTransmittingSound = null;
202+
this.connectedDeviceSound = null;
203+
this.connectToAudioRoomSound = null;
204+
this.disconnectedFromAudioRoomSound = null;
297205

298206
this.isInitialized = false;
299207

0 commit comments

Comments
 (0)