Untitled
unknown
plain_text
a year ago
6.0 kB
7
Indexable
import { NativeEventEmitter, NativeModules, Platform } from 'react-native';
import notifee from '@notifee/react-native';
export type IOSInterruptionType =
| 'phone_call'
| 'facetime'
| 'siri'
| 'system_alert'
| 'other_app'
| 'system_pressure'
| 'media_playback'
| 'unknown';
export type IOSInterruptionEvent = {
type: 'began' | 'ended';
reason: IOSInterruptionType;
options?: {
shouldResume?: boolean;
wasSuspended?: boolean;
};
};
class IOSAudioSessionManager {
private listeners: Set<(event: IOSInterruptionEvent) => void> = new Set();
private audioSessionEmitter?: NativeEventEmitter;
private memoryWarningEmitter?: NativeEventEmitter;
constructor() {
if (Platform.OS === 'ios') {
this.setupAudioSession();
this.setupInterruptionListeners();
this.setupMemoryWarningListener();
}
}
private async setupAudioSession() {
try {
const { RNAudioRecorderPlayer } = NativeModules;
await RNAudioRecorderPlayer.setAudioSession({
category: 'playAndRecord',
options: [
'allowBluetooth',
'allowBluetoothA2DP',
'mixWithOthers',
'defaultToSpeaker'
],
mode: 'spokenAudio',
categoryOptions: {
mixWithOthers: false,
duckOthers: true,
interruptSpokenAudioAndMixWithOthers: false,
allowBluetooth: true,
allowBluetoothA2DP: true,
allowAirPlay: true,
defaultToSpeaker: true,
}
});
} catch (error) {
console.error('Failed to configure audio session:', error);
}
}
private setupInterruptionListeners() {
const { RNAudioRecorderPlayer } = NativeModules;
this.audioSessionEmitter = new NativeEventEmitter(RNAudioRecorderPlayer);
// Audio Session Interruptions
this.audioSessionEmitter.addListener(
'audioSessionInterruption',
this.handleAudioInterruption
);
// Route changes (e.g., audio output changes)
this.audioSessionEmitter.addListener(
'audioRouteChange',
this.handleAudioRouteChange
);
// Media Services were reset
this.audioSessionEmitter.addListener(
'mediaServicesWereLost',
this.handleMediaServicesReset
);
this.audioSessionEmitter.addListener(
'mediaServicesWereReset',
this.handleMediaServicesReset
);
}
private setupMemoryWarningListener() {
// System memory warnings
this.memoryWarningEmitter = new NativeEventEmitter(NativeModules.RNEventEmitter);
this.memoryWarningEmitter.addListener('memoryWarning', this.handleMemoryWarning);
}
private handleAudioInterruption = (event: any) => {
const interruptionType = this.getInterruptionType(event);
this.notifyListeners({
type: event.type === 'began' ? 'began' : 'ended',
reason: interruptionType,
options: {
shouldResume: event.type === 'ended' && event.options?.shouldResume,
wasSuspended: event.wasSuspended
}
});
this.updateNotification(event.type, interruptionType);
};
private handleAudioRouteChange = (event: any) => {
// Handle audio route changes (e.g., between speaker, bluetooth)
this.notifyListeners({
type: 'began',
reason: 'other_app',
options: { shouldResume: true }
});
};
private handleMediaServicesReset = () => {
// Handle media service resets (rare but can happen)
this.notifyListeners({
type: 'began',
reason: 'system_pressure',
options: { shouldResume: true }
});
};
private handleMemoryWarning = () => {
this.notifyListeners({
type: 'began',
reason: 'system_pressure',
options: { shouldResume: false }
});
};
private getInterruptionType(event: any): IOSInterruptionType {
// iOS provides this in AVAudioSession interruption notifications
switch (event.reason) {
case 'AVAudioSessionInterruptionTypePhoneCall':
return 'phone_call';
case 'AVAudioSessionInterruptionTypeFaceTime':
return 'facetime';
case 'AVAudioSessionInterruptionTypeSiri':
return 'siri';
case 'AVAudioSessionInterruptionTypeSystemAlert':
return 'system_alert';
case 'AVAudioSessionInterruptionTypeMediaPlayback':
return 'media_playback';
default:
return 'unknown';
}
}
private async updateNotification(
state: 'began' | 'ended',
type: IOSInterruptionType
) {
const messages = {
phone_call: 'Phone Call',
facetime: 'FaceTime Call',
siri: 'Siri',
system_alert: 'System Alert',
other_app: 'Other App Audio',
system_pressure: 'System Resources',
media_playback: 'Media Playback',
unknown: 'System Interruption'
};
await notifee.displayNotification({
id: 'recording-notification',
title: state === 'began' ? 'Recording Paused' : 'Recording Resumed',
body: state === 'began'
? `Recording paused due to ${messages[type]}`
: 'Recording has resumed',
ios: {
categoryId: 'recording',
interruptionLevel: 'active'
}
});
}
public addListener(listener: (event: IOSInterruptionEvent) => void): () => void {
this.listeners.add(listener);
return () => this.listeners.delete(listener);
}
private notifyListeners(event: IOSInterruptionEvent): void {
this.listeners.forEach(listener => listener(event));
}
public cleanup(): void {
if (this.audioSessionEmitter) {
this.audioSessionEmitter.removeAllListeners('audioSessionInterruption');
this.audioSessionEmitter.removeAllListeners('audioRouteChange');
this.audioSessionEmitter.removeAllListeners('mediaServicesWereLost');
this.audioSessionEmitter.removeAllListeners('mediaServicesWereReset');
}
if (this.memoryWarningEmitter) {
this.memoryWarningEmitter.removeAllListeners('memoryWarning');
}
this.listeners.clear();
}
}
export const iOSAudioManager = new IOSAudioSessionManager();Editor is loading...
Leave a Comment