1f1bb9346SEvan Baconimport { 2f1bb9346SEvan Bacon AndroidConfig, 3f1bb9346SEvan Bacon AndroidManifest, 4f1bb9346SEvan Bacon ConfigPlugin, 5f1bb9346SEvan Bacon withAndroidManifest, 6f1bb9346SEvan Bacon} from '@expo/config-plugins'; 78f4283e8Sandyimport { 88f4283e8Sandy ManifestActivity, 98f4283e8Sandy ManifestIntentFilter, 108f4283e8Sandy} from '@expo/config-plugins/build/android/Manifest'; 11*435bbba8SBrent Vatneimport { ExpoConfig } from 'expo/config'; 12dd3fc24cSVille Immonen 134813bc2eSVille Immonenimport getDefaultScheme from './getDefaultScheme'; 14dd3fc24cSVille Immonen 15f1bb9346SEvan Baconexport const withGeneratedAndroidScheme: ConfigPlugin = (config) => { 16f1bb9346SEvan Bacon return withAndroidManifest(config, (config) => { 17f1bb9346SEvan Bacon config.modResults = setGeneratedAndroidScheme(config, config.modResults); 188f4283e8Sandy config.modResults = removeExpoSchemaFromVerifiedIntentFilters(config, config.modResults); 19f1bb9346SEvan Bacon return config; 20f1bb9346SEvan Bacon }); 21f1bb9346SEvan Bacon}; 22dd3fc24cSVille Immonen 23dd3fc24cSVille Immonenexport function setGeneratedAndroidScheme( 24dd3fc24cSVille Immonen config: Pick<ExpoConfig, 'scheme' | 'slug'>, 25dd3fc24cSVille Immonen androidManifest: AndroidManifest 26dd3fc24cSVille Immonen): AndroidManifest { 27dd3fc24cSVille Immonen // Generate a cross-platform scheme used to launch the dev client. 284813bc2eSVille Immonen const scheme = getDefaultScheme(config); 29f1bb9346SEvan Bacon if (!AndroidConfig.Scheme.hasScheme(scheme, androidManifest)) { 30f1bb9346SEvan Bacon androidManifest = AndroidConfig.Scheme.appendScheme(scheme, androidManifest); 31f1bb9346SEvan Bacon } 328f4283e8Sandy 33f1bb9346SEvan Bacon return androidManifest; 34dd3fc24cSVille Immonen} 358f4283e8Sandy 368f4283e8Sandy/** 378f4283e8Sandy * Remove the custom Expo dev client scheme from intent filters, which are set to `autoVerify=true`. 388f4283e8Sandy * The custom scheme `<data android:scheme="exp+<slug>"/>` seems to block verification for these intent filters. 398f4283e8Sandy * This plugin makes sure there is no scheme in the autoVerify intent filters, that starts with `exp+`. 408f4283e8Sandy 418f4283e8Sandy * Iterate over all `autoVerify=true` intent filters, and pull out schemes matching with `exp+<slug>`. 428f4283e8Sandy * 438f4283e8Sandy * @param {AndroidManifest} androidManifest 448f4283e8Sandy */ 458f4283e8Sandyexport function removeExpoSchemaFromVerifiedIntentFilters( 468f4283e8Sandy config: Pick<ExpoConfig, 'scheme' | 'slug'>, 478f4283e8Sandy androidManifest: AndroidManifest 488f4283e8Sandy) { 498f4283e8Sandy // Generate a cross-platform scheme used to launch the dev client. 508f4283e8Sandy const defaultScheme = getDefaultScheme(config); 518f4283e8Sandy // see: https://github.com/expo/expo-cli/blob/f1624c75b52cc1c4f99354ec4021494e0eff74aa/packages/config-plugins/src/android/Scheme.ts#L164-L179 528f4283e8Sandy for (const application of androidManifest.manifest.application || []) { 538f4283e8Sandy for (const activity of application.activity || []) { 548f4283e8Sandy if (activityHasSingleTaskLaunchMode(activity)) { 558f4283e8Sandy for (const intentFilter of activity['intent-filter'] || []) { 568f4283e8Sandy if (intentFilterHasAutoVerification(intentFilter) && intentFilter?.data) { 578f4283e8Sandy intentFilter.data = intentFilterRemoveSchemeFromData( 588f4283e8Sandy intentFilter, 598f4283e8Sandy (scheme: string) => scheme === defaultScheme 608f4283e8Sandy ); 618f4283e8Sandy } 628f4283e8Sandy } 638f4283e8Sandy break; 648f4283e8Sandy } 658f4283e8Sandy } 668f4283e8Sandy } 678f4283e8Sandy 688f4283e8Sandy return androidManifest; 698f4283e8Sandy} 708f4283e8Sandy 718f4283e8Sandy/** 728f4283e8Sandy * Determine if the activity should contain the intent filters to clean. 738f4283e8Sandy * 748f4283e8Sandy * @see https://github.com/expo/expo-cli/blob/f1624c75b52cc1c4f99354ec4021494e0eff74aa/packages/config-plugins/src/android/Scheme.ts#L166 758f4283e8Sandy */ 768f4283e8Sandyfunction activityHasSingleTaskLaunchMode(activity: ManifestActivity) { 778f4283e8Sandy return activity?.$?.['android:launchMode'] === 'singleTask'; 788f4283e8Sandy} 798f4283e8Sandy 808f4283e8Sandy/** 818f4283e8Sandy * Determine if the intent filter has `autoVerify=true`. 828f4283e8Sandy */ 838f4283e8Sandyfunction intentFilterHasAutoVerification(intentFilter: ManifestIntentFilter) { 848f4283e8Sandy return intentFilter?.$?.['android:autoVerify'] === 'true'; 858f4283e8Sandy} 868f4283e8Sandy 878f4283e8Sandy/** 888f4283e8Sandy * Remove schemes from the intent filter that matches the function. 898f4283e8Sandy */ 908f4283e8Sandyfunction intentFilterRemoveSchemeFromData(intentFilter: ManifestIntentFilter, schemeMatcher: any) { 918f4283e8Sandy return intentFilter?.data?.filter((entry) => !schemeMatcher(entry?.$['android:scheme'] || '')); 928f4283e8Sandy} 93