UNPKG

@siteed/expo-audio-studio

Version:

Comprehensive audio processing library for React Native and Expo with recording, analysis, visualization, and streaming capabilities across iOS, Android, and web

192 lines (191 loc) 9.13 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const config_plugins_1 = require("@expo/config-plugins"); const MICROPHONE_USAGE = 'Allow $(PRODUCT_NAME) to access your microphone'; const NOTIFICATION_USAGE = 'Show recording notifications and controls'; const LOG_PREFIX = '[@siteed/expo-audio-studio]'; function debugLog(message, ...args) { if (process.env.EXPO_DEBUG) { console.log(`${LOG_PREFIX} ${message}`, ...args); } } const withRecordingPermission = (config, props) => { const options = { enablePhoneStateHandling: true, // Default to true for backward compatibility enableNotifications: true, enableBackgroundAudio: true, iosBackgroundModes: { useVoIP: false, useAudio: false, useProcessing: false, useLocation: false, useExternalAccessory: false, }, iosConfig: { microphoneUsageDescription: MICROPHONE_USAGE, notificationUsageDescription: NOTIFICATION_USAGE, }, ...(props || {}), }; const { enablePhoneStateHandling, enableNotifications, enableBackgroundAudio, } = options; debugLog('📱 Configuring Recording Permissions Plugin...', options); // iOS Configuration config = (0, config_plugins_1.withInfoPlist)(config, (config) => { // Always set the microphone usage description from options first config.modResults['NSMicrophoneUsageDescription'] = options.iosConfig?.microphoneUsageDescription || config.modResults['NSMicrophoneUsageDescription'] || MICROPHONE_USAGE; if (enableNotifications) { config.modResults['NSUserNotificationsUsageDescription'] = options.iosConfig?.notificationUsageDescription || config.modResults['NSUserNotificationsUsageDescription'] || NOTIFICATION_USAGE; config.modResults['NSUserNotificationAlertStyle'] = 'alert'; } const existingBackgroundModes = config.modResults.UIBackgroundModes || []; // Only add background modes if explicitly enabled and set to true if (options.iosBackgroundModes?.useAudio === true && enableBackgroundAudio === true && !existingBackgroundModes.includes('audio')) { // Don't automatically add 'audio' background mode as it's only for playback // existingBackgroundModes.push('audio') // Instead, ensure processing mode is used for background recording if (options.iosBackgroundModes?.useProcessing !== true) { console.warn(`${LOG_PREFIX} Warning: Background audio recording requires 'processing' background mode. Please enable 'useProcessing' in iosBackgroundModes.`); } } if (options.iosBackgroundModes?.useVoIP === true && enablePhoneStateHandling === true) { if (!existingBackgroundModes.includes('voip')) { existingBackgroundModes.push('voip'); } const existingCapabilities = (config.modResults .UIRequiredDeviceCapabilities || []); if (!existingCapabilities.includes('telephony')) { existingCapabilities.push('telephony'); } config.modResults.UIRequiredDeviceCapabilities = existingCapabilities; } // Add additional background modes only if explicitly set to true if (options.iosBackgroundModes?.useProcessing === true) { if (!existingBackgroundModes.includes('processing')) { existingBackgroundModes.push('processing'); } // Add processing info if enabled // Note: We keep the 'audiostream' namespace for native modules to maintain compatibility config.modResults.BGTaskSchedulerPermittedIdentifiers = [ 'com.siteed.audiostream.processing', ]; } if (options.iosBackgroundModes?.useLocation === true) { if (!existingBackgroundModes.includes('location')) { existingBackgroundModes.push('location'); } } if (options.iosBackgroundModes?.useExternalAccessory === true) { if (!existingBackgroundModes.includes('external-accessory')) { existingBackgroundModes.push('external-accessory'); } } // Configure background processing info if enabled if (options.iosConfig?.backgroundProcessingTitle) { config.modResults.BGProcessingTaskTitle = options.iosConfig.backgroundProcessingTitle; } // Configure audio session behavior if (options.iosConfig?.allowBackgroundAudioControls) { config.modResults.UIBackgroundModes = [ ...existingBackgroundModes, 'remote-notification', ]; config.modResults.MPNowPlayingInfoPropertyPlaybackRate = true; } config.modResults.UIBackgroundModes = existingBackgroundModes; return config; }); // Android Configuration config = (0, config_plugins_1.withAndroidManifest)(config, (config) => { const basePermissions = [ 'android.permission.RECORD_AUDIO', 'android.permission.WAKE_LOCK', ]; const optionalPermissions = [ enableNotifications && 'android.permission.POST_NOTIFICATIONS', enablePhoneStateHandling && 'android.permission.READ_PHONE_STATE', // Only add if enabled enableBackgroundAudio && 'android.permission.FOREGROUND_SERVICE', enableBackgroundAudio && 'android.permission.FOREGROUND_SERVICE_MICROPHONE', ].filter(Boolean); const permissionsToAdd = [...basePermissions, ...optionalPermissions]; debugLog('📋 Existing Android permissions:', config.modResults.manifest['uses-permission']?.map((p) => p.$?.['android:name']) || []); debugLog('➕ Adding Android permissions:', permissionsToAdd); const { addPermission } = config_plugins_1.AndroidConfig.Permissions; // Add each permission only if it doesn't exist permissionsToAdd.forEach((permission) => { const existingPermission = config.modResults.manifest['uses-permission']?.find((p) => p.$?.['android:name'] === permission); if (!existingPermission) { addPermission(config.modResults, permission); } }); // Get the main application node const mainApplication = config.modResults.manifest.application?.[0]; if (mainApplication) { debugLog('📱 Configuring Android application components...'); // Add RecordingActionReceiver if (!mainApplication.receiver) { mainApplication.receiver = []; } const receiverConfig = { $: { 'android:name': '.RecordingActionReceiver', 'android:exported': 'false', }, 'intent-filter': [ { action: [ { $: { 'android:name': 'PAUSE_RECORDING' } }, { $: { 'android:name': 'RESUME_RECORDING' } }, { $: { 'android:name': 'STOP_RECORDING' } }, ], }, ], }; const receiverIndex = mainApplication.receiver.findIndex((receiver) => receiver.$?.['android:name'] === '.RecordingActionReceiver'); if (receiverIndex >= 0) { mainApplication.receiver[receiverIndex] = receiverConfig; } else { mainApplication.receiver.push(receiverConfig); } debugLog('✅ RecordingActionReceiver configured'); // Add AudioRecordingService if (!mainApplication.service) { mainApplication.service = []; } const serviceConfig = { $: { 'android:name': '.AudioRecordingService', 'android:enabled': 'true', 'android:exported': 'false', 'android:foregroundServiceType': 'microphone', }, }; const serviceIndex = mainApplication.service.findIndex((service) => service.$?.['android:name'] === '.AudioRecordingService'); if (serviceIndex >= 0) { mainApplication.service[serviceIndex] = serviceConfig; } else { mainApplication.service.push(serviceConfig); } debugLog('✅ AudioRecordingService configured'); } else { console.error(`${LOG_PREFIX} ❌ Main application node not found in Android Manifest`); } return config; }); debugLog('✨ Recording Permissions Plugin configuration completed'); return config; }; exports.default = withRecordingPermission;