import { AndroidConfig, AndroidManifest, ConfigPlugin, createRunOncePlugin, withAndroidManifest, withInfoPlist, withStringsXml, } from '@expo/config-plugins'; const MICROPHONE = 'Allow $(PRODUCT_NAME) to access the microphone'; const CAMERA = 'Allow $(PRODUCT_NAME) to access the camera'; const LIBRARY = 'Allow $(PRODUCT_NAME) to access the files'; export type Props = { /** * `NSMicrophoneUsageDescription` message. */ microphonePermission?: string | false; /** * `NSCameraUsageDescription` message. */ cameraPermission?: string | false; /** * `NSBluetoothPeripheralUsageDescription` message. */ bluetoothPermission?: string | false; /** * `NSBluetoothPeripheralUsageDescription` message. */ bluetoothAlwaysPermission?: string | false; /** * `NSPhotoLibraryUsageDescription` message. */ libraryPermission?: string | false; }; /** * Adds `NSMicrophoneUsageDescription` and `NSSpeechRecognitionUsageDescription` to the `Info.plist`. * * @param props.speechRecognitionPermission speech recognition message * @param props.microphonePermission microphone permission message * @returns */ const withIosPermissions: ConfigPlugin = ( c, { microphonePermission, cameraPermission, libraryPermission } = {} ) => { return withInfoPlist(c, (config) => { if (microphonePermission !== false) { config.modResults.NSMicrophoneUsageDescription = microphonePermission || config.modResults.NSMicrophoneUsageDescription || MICROPHONE; } if (cameraPermission !== false) { config.modResults.NSCameraUsageDescription = cameraPermission || config.modResults.NSCameraUsageDescription || CAMERA; } if (libraryPermission !== false) { config.modResults.NSPhotoLibraryUsageDescription = libraryPermission || config.modResults.NSPhotoLibraryUsageDescription || LIBRARY; } if (config.modResults.UIBackgroundModes === undefined) { config.modResults.UIBackgroundModes = ['audio', 'voip']; } return config; }); }; /** * Adds the following to the `AndroidManifest.xml`: * */ const withAndroidPermissions: ConfigPlugin = (config) => { return AndroidConfig.Permissions.withPermissions(config, [ 'android.permission.ACCESS_NETWORK_STATE', 'android.permission.BLUETOOTH', 'android.permission.CAMERA', 'android.permission.INTERNET', 'android.permission.MODIFY_AUDIO_SETTINGS', 'android.permission.RECORD_AUDIO', 'android.permission.SYSTEM_ALERT_WINDOW', 'android.permission.WAKE_LOCK', 'android.permission.FOREGROUND_SERVICE', 'android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION', 'android.permission.WRITE_EXTERNAL_STORAGE', 'android.permission.READ_EXTERNAL_STORAGE', 'android.permission.MICROPHONE', ]); }; const withAndroidScreenshare: ConfigPlugin = (config) => { return withAndroidManifest(config, (_config) => { const manifest = _config.modResults as AndroidManifest; const app = manifest.manifest.application?.[0]; if (app) { // Avoid duplicate entries const exists = (app.service || []).some( (service) => service.$['android:name'] === 'com.cloudflare.realtimekit.ForegroundService' ); if (!exists) { app.service = [ ...(app.service || []), { $: { 'android:enabled': 'true', 'android:foregroundServiceType': 'mediaProjection', 'android:name': 'com.cloudflare.realtimekit.ForegroundService', }, }, ]; } } return _config; }); }; const withAndroidBlobProviderAuthority: ConfigPlugin = (config) => { const uuid = require('uuid').v4(); return withStringsXml(config, async (_config) => { // Avoid duplicates const existing = _config.modResults.resources.string?.find( (item) => item.$.name === 'blob_provider_authority' ); if (!existing) { _config.modResults.resources.string = [ ...(_config.modResults.resources.string || []), { $: { name: 'blob_provider_authority' }, _: 'com.cloudflare.realtimekit.expo.blobs-' + uuid, }, ]; } return _config; }); }; const withVoice: ConfigPlugin = (config, props = {}) => { const _props = props ? props : {}; config = withIosPermissions(config, _props); if (_props.microphonePermission !== false) { config = withAndroidPermissions(config); } config = withAndroidScreenshare(config); config = withAndroidBlobProviderAuthority(config); return config; }; export default createRunOncePlugin( withVoice, '@cloudflare/realtimekit-react-native' );