1import { 2 AndroidConfig, 3 AndroidManifest, 4 ConfigPlugin, 5 withAndroidManifest, 6} from '@expo/config-plugins'; 7import { 8 ManifestActivity, 9 ManifestIntentFilter, 10} from '@expo/config-plugins/build/android/Manifest'; 11import { ExpoConfig } from 'expo/config'; 12 13import getDefaultScheme from './getDefaultScheme'; 14 15export const withGeneratedAndroidScheme: ConfigPlugin = (config) => { 16 return withAndroidManifest(config, (config) => { 17 config.modResults = setGeneratedAndroidScheme(config, config.modResults); 18 config.modResults = removeExpoSchemaFromVerifiedIntentFilters(config, config.modResults); 19 return config; 20 }); 21}; 22 23export function setGeneratedAndroidScheme( 24 config: Pick<ExpoConfig, 'scheme' | 'slug'>, 25 androidManifest: AndroidManifest 26): AndroidManifest { 27 // Generate a cross-platform scheme used to launch the dev client. 28 const scheme = getDefaultScheme(config); 29 if (!AndroidConfig.Scheme.hasScheme(scheme, androidManifest)) { 30 androidManifest = AndroidConfig.Scheme.appendScheme(scheme, androidManifest); 31 } 32 33 return androidManifest; 34} 35 36/** 37 * Remove the custom Expo dev client scheme from intent filters, which are set to `autoVerify=true`. 38 * The custom scheme `<data android:scheme="exp+<slug>"/>` seems to block verification for these intent filters. 39 * This plugin makes sure there is no scheme in the autoVerify intent filters, that starts with `exp+`. 40 41 * Iterate over all `autoVerify=true` intent filters, and pull out schemes matching with `exp+<slug>`. 42 * 43 * @param {AndroidManifest} androidManifest 44 */ 45export function removeExpoSchemaFromVerifiedIntentFilters( 46 config: Pick<ExpoConfig, 'scheme' | 'slug'>, 47 androidManifest: AndroidManifest 48) { 49 // Generate a cross-platform scheme used to launch the dev client. 50 const defaultScheme = getDefaultScheme(config); 51 // see: https://github.com/expo/expo-cli/blob/f1624c75b52cc1c4f99354ec4021494e0eff74aa/packages/config-plugins/src/android/Scheme.ts#L164-L179 52 for (const application of androidManifest.manifest.application || []) { 53 for (const activity of application.activity || []) { 54 if (activityHasSingleTaskLaunchMode(activity)) { 55 for (const intentFilter of activity['intent-filter'] || []) { 56 if (intentFilterHasAutoVerification(intentFilter) && intentFilter?.data) { 57 intentFilter.data = intentFilterRemoveSchemeFromData( 58 intentFilter, 59 (scheme: string) => scheme === defaultScheme 60 ); 61 } 62 } 63 break; 64 } 65 } 66 } 67 68 return androidManifest; 69} 70 71/** 72 * Determine if the activity should contain the intent filters to clean. 73 * 74 * @see https://github.com/expo/expo-cli/blob/f1624c75b52cc1c4f99354ec4021494e0eff74aa/packages/config-plugins/src/android/Scheme.ts#L166 75 */ 76function activityHasSingleTaskLaunchMode(activity: ManifestActivity) { 77 return activity?.$?.['android:launchMode'] === 'singleTask'; 78} 79 80/** 81 * Determine if the intent filter has `autoVerify=true`. 82 */ 83function intentFilterHasAutoVerification(intentFilter: ManifestIntentFilter) { 84 return intentFilter?.$?.['android:autoVerify'] === 'true'; 85} 86 87/** 88 * Remove schemes from the intent filter that matches the function. 89 */ 90function intentFilterRemoveSchemeFromData(intentFilter: ManifestIntentFilter, schemeMatcher: any) { 91 return intentFilter?.data?.filter((entry) => !schemeMatcher(entry?.$['android:scheme'] || '')); 92} 93