1import { 2 AndroidConfig, 3 withProjectBuildGradle, 4 ConfigPlugin, 5 createRunOncePlugin, 6 withInfoPlist, 7} from '@expo/config-plugins'; 8import { 9 createGeneratedHeaderComment, 10 MergeResults, 11 removeGeneratedContents, 12} from '@expo/config-plugins/build/utils/generateCode'; 13 14const pkg = require('expo-camera/package.json'); 15 16const CAMERA_USAGE = 'Allow $(PRODUCT_NAME) to access your camera'; 17const MICROPHONE_USAGE = 'Allow $(PRODUCT_NAME) to access your microphone'; 18 19// Because we need the package to be added AFTER the React and Google maven packages, we create a new allprojects. 20// It's ok to have multiple allprojects.repositories, so we create a new one since it's cheaper than tokenizing 21// the existing block to find the correct place to insert our camera maven. 22const gradleMaven = [ 23 `def expoCameraMavenPath = new File(["node", "--print", "require.resolve('expo-camera/package.json')"].execute(null, rootDir).text.trim(), "../android/maven")`, 24 `allprojects { repositories { maven { url(expoCameraMavenPath) } } }`, 25].join('\n'); 26 27const withAndroidCameraGradle: ConfigPlugin = (config) => { 28 return withProjectBuildGradle(config, (config) => { 29 if (config.modResults.language === 'groovy') { 30 config.modResults.contents = addCameraImport(config.modResults.contents).contents; 31 } else { 32 throw new Error('Cannot add camera maven gradle because the build.gradle is not groovy'); 33 } 34 return config; 35 }); 36}; 37 38export function addCameraImport(src: string): MergeResults { 39 return appendContents({ 40 tag: 'expo-camera-import', 41 src, 42 newSrc: gradleMaven, 43 comment: '//', 44 }); 45} 46 47// Fork of config-plugins mergeContents, but appends the contents to the end of the file. 48function appendContents({ 49 src, 50 newSrc, 51 tag, 52 comment, 53}: { 54 src: string; 55 newSrc: string; 56 tag: string; 57 comment: string; 58}): MergeResults { 59 const header = createGeneratedHeaderComment(newSrc, tag, comment); 60 if (!src.includes(header)) { 61 // Ensure the old generated contents are removed. 62 const sanitizedTarget = removeGeneratedContents(src, tag); 63 const contentsToAdd = [ 64 // @something 65 header, 66 // contents 67 newSrc, 68 // @end 69 `${comment} @generated end ${tag}`, 70 ].join('\n'); 71 72 return { 73 contents: sanitizedTarget ?? src + contentsToAdd, 74 didMerge: true, 75 didClear: !!sanitizedTarget, 76 }; 77 } 78 return { contents: src, didClear: false, didMerge: false }; 79} 80 81const withCamera: ConfigPlugin< 82 { 83 cameraPermission?: string; 84 microphonePermission?: string; 85 } | void 86> = (config, { cameraPermission, microphonePermission } = {}) => { 87 config = withInfoPlist(config, (config) => { 88 config.modResults.NSCameraUsageDescription = 89 cameraPermission || config.modResults.NSCameraUsageDescription || CAMERA_USAGE; 90 91 config.modResults.NSMicrophoneUsageDescription = 92 microphonePermission || config.modResults.NSMicrophoneUsageDescription || MICROPHONE_USAGE; 93 94 return config; 95 }); 96 97 config = AndroidConfig.Permissions.withPermissions(config, [ 98 'android.permission.CAMERA', 99 // Optional 100 'android.permission.RECORD_AUDIO', 101 ]); 102 103 return withAndroidCameraGradle(config); 104}; 105 106export default createRunOncePlugin(withCamera, pkg.name, pkg.version); 107