1"use strict"; 2 3Object.defineProperty(exports, "__esModule", { 4 value: true 5}); 6exports.appendScheme = appendScheme; 7exports.ensureManifestHasValidIntentFilter = ensureManifestHasValidIntentFilter; 8exports.getScheme = getScheme; 9exports.getSchemesFromManifest = getSchemesFromManifest; 10exports.hasScheme = hasScheme; 11exports.removeScheme = removeScheme; 12exports.setScheme = setScheme; 13exports.withScheme = void 0; 14 15function _androidPlugins() { 16 const data = require("../plugins/android-plugins"); 17 18 _androidPlugins = function () { 19 return data; 20 }; 21 22 return data; 23} 24 25function _warnings() { 26 const data = require("../utils/warnings"); 27 28 _warnings = function () { 29 return data; 30 }; 31 32 return data; 33} 34 35const withScheme = (0, _androidPlugins().createAndroidManifestPlugin)(setScheme, 'withScheme'); 36exports.withScheme = withScheme; 37 38function getScheme(config) { 39 if (Array.isArray(config.scheme)) { 40 const validate = value => typeof value === 'string'; 41 42 return config.scheme.filter(validate); 43 } else if (typeof config.scheme === 'string') { 44 return [config.scheme]; 45 } 46 47 return []; 48} // This plugin used to remove the unused schemes but this is unpredictable because other plugins could add schemes. 49// The only way to reliably remove schemes from the project is to nuke the file and regenerate the code (`expo prebuild --clean`). 50// Regardless, having extra schemes isn't a fatal issue and therefore a tolerable compromise is to just add new schemes that aren't currently present. 51 52 53function setScheme(config, androidManifest) { 54 var _config$android, _config$android2; 55 56 const schemes = [...getScheme(config), // @ts-ignore: TODO: android.scheme is an unreleased -- harder to add to turtle v1. 57 ...getScheme((_config$android = config.android) !== null && _config$android !== void 0 ? _config$android : {})]; // Add the package name to the list of schemes for easier Google auth and parity with Turtle v1. 58 59 if ((_config$android2 = config.android) !== null && _config$android2 !== void 0 && _config$android2.package) { 60 schemes.push(config.android.package); 61 } 62 63 if (schemes.length === 0) { 64 return androidManifest; 65 } 66 67 if (!ensureManifestHasValidIntentFilter(androidManifest)) { 68 (0, _warnings().addWarningAndroid)('scheme', `Cannot add schemes because the provided manifest does not have a valid Activity with \`android:launchMode="singleTask"\``, 'https://expo.fyi/setup-android-uri-scheme'); 69 return androidManifest; 70 } // Get the current schemes and remove them from the list of schemes to add. 71 72 73 const currentSchemes = getSchemesFromManifest(androidManifest); 74 75 for (const uri of currentSchemes) { 76 const index = schemes.indexOf(uri); 77 if (index > -1) schemes.splice(index, 1); 78 } // Now add all of the remaining schemes. 79 80 81 for (const uri of schemes) { 82 androidManifest = appendScheme(uri, androidManifest); 83 } 84 85 return androidManifest; 86} 87 88function isValidRedirectIntentFilter({ 89 actions, 90 categories 91}) { 92 return actions.includes('android.intent.action.VIEW') && !categories.includes('android.intent.category.LAUNCHER'); 93} 94 95function propertiesFromIntentFilter(intentFilter) { 96 var _intentFilter$action$, _intentFilter$action, _intentFilter$categor, _intentFilter$categor2, _intentFilter$data$fi, _intentFilter$data, _intentFilter$data$fi2; 97 98 const actions = (_intentFilter$action$ = intentFilter === null || intentFilter === void 0 ? void 0 : (_intentFilter$action = intentFilter.action) === null || _intentFilter$action === void 0 ? void 0 : _intentFilter$action.map(data => { 99 var _data$$; 100 101 return data === null || data === void 0 ? void 0 : (_data$$ = data.$) === null || _data$$ === void 0 ? void 0 : _data$$['android:name']; 102 })) !== null && _intentFilter$action$ !== void 0 ? _intentFilter$action$ : []; 103 const categories = (_intentFilter$categor = intentFilter === null || intentFilter === void 0 ? void 0 : (_intentFilter$categor2 = intentFilter.category) === null || _intentFilter$categor2 === void 0 ? void 0 : _intentFilter$categor2.map(data => { 104 var _data$$2; 105 106 return data === null || data === void 0 ? void 0 : (_data$$2 = data.$) === null || _data$$2 === void 0 ? void 0 : _data$$2['android:name']; 107 })) !== null && _intentFilter$categor !== void 0 ? _intentFilter$categor : []; 108 const data = (_intentFilter$data$fi = intentFilter === null || intentFilter === void 0 ? void 0 : (_intentFilter$data = intentFilter.data) === null || _intentFilter$data === void 0 ? void 0 : (_intentFilter$data$fi2 = _intentFilter$data.filter(data => { 109 var _data$$3; 110 111 return data === null || data === void 0 ? void 0 : (_data$$3 = data.$) === null || _data$$3 === void 0 ? void 0 : _data$$3['android:scheme']; 112 })) === null || _intentFilter$data$fi2 === void 0 ? void 0 : _intentFilter$data$fi2.map(data => { 113 var _data$$4, _data$$5; 114 115 return { 116 scheme: data === null || data === void 0 ? void 0 : (_data$$4 = data.$) === null || _data$$4 === void 0 ? void 0 : _data$$4['android:scheme'], 117 host: data === null || data === void 0 ? void 0 : (_data$$5 = data.$) === null || _data$$5 === void 0 ? void 0 : _data$$5['android:host'] 118 }; 119 })) !== null && _intentFilter$data$fi !== void 0 ? _intentFilter$data$fi : []; 120 return { 121 actions, 122 categories, 123 data 124 }; 125} 126 127function getSingleTaskIntentFilters(androidManifest) { 128 if (!Array.isArray(androidManifest.manifest.application)) return []; 129 let outputSchemes = []; 130 131 for (const application of androidManifest.manifest.application) { 132 const { 133 activity 134 } = application; // @ts-ignore 135 136 const activities = Array.isArray(activity) ? activity : [activity]; 137 const singleTaskActivities = activities.filter(activity => { 138 var _activity$$; 139 140 return (activity === null || activity === void 0 ? void 0 : (_activity$$ = activity.$) === null || _activity$$ === void 0 ? void 0 : _activity$$['android:launchMode']) === 'singleTask'; 141 }); 142 143 for (const activity of singleTaskActivities) { 144 const intentFilters = activity['intent-filter']; 145 outputSchemes = outputSchemes.concat(intentFilters); 146 } 147 } 148 149 return outputSchemes; 150} 151 152function getSchemesFromManifest(androidManifest, requestedHost = null) { 153 const outputSchemes = []; 154 const singleTaskIntentFilters = getSingleTaskIntentFilters(androidManifest); 155 156 for (const intentFilter of singleTaskIntentFilters) { 157 const properties = propertiesFromIntentFilter(intentFilter); 158 159 if (isValidRedirectIntentFilter(properties) && properties.data) { 160 for (const { 161 scheme, 162 host 163 } of properties.data) { 164 if (requestedHost === null || !host || host === requestedHost) { 165 outputSchemes.push(scheme); 166 } 167 } 168 } 169 } 170 171 return outputSchemes; 172} 173 174function ensureManifestHasValidIntentFilter(androidManifest) { 175 if (!Array.isArray(androidManifest.manifest.application)) { 176 return false; 177 } 178 179 for (const application of androidManifest.manifest.application) { 180 for (const activity of application.activity || []) { 181 var _activity$$2; 182 183 if ((activity === null || activity === void 0 ? void 0 : (_activity$$2 = activity.$) === null || _activity$$2 === void 0 ? void 0 : _activity$$2['android:launchMode']) === 'singleTask') { 184 for (const intentFilter of activity['intent-filter'] || []) { 185 // Parse valid intent filters... 186 const properties = propertiesFromIntentFilter(intentFilter); 187 188 if (isValidRedirectIntentFilter(properties)) { 189 return true; 190 } 191 } 192 193 if (!activity['intent-filter']) { 194 activity['intent-filter'] = []; 195 } 196 197 activity['intent-filter'].push({ 198 action: [{ 199 $: { 200 'android:name': 'android.intent.action.VIEW' 201 } 202 }], 203 category: [{ 204 $: { 205 'android:name': 'android.intent.category.DEFAULT' 206 } 207 }, { 208 $: { 209 'android:name': 'android.intent.category.BROWSABLE' 210 } 211 }] 212 }); 213 return true; 214 } 215 } 216 } 217 218 return false; 219} 220 221function hasScheme(scheme, androidManifest) { 222 const schemes = getSchemesFromManifest(androidManifest); 223 return schemes.includes(scheme); 224} 225 226function appendScheme(scheme, androidManifest) { 227 if (!Array.isArray(androidManifest.manifest.application)) { 228 return androidManifest; 229 } 230 231 for (const application of androidManifest.manifest.application) { 232 for (const activity of application.activity || []) { 233 var _activity$$3; 234 235 if ((activity === null || activity === void 0 ? void 0 : (_activity$$3 = activity.$) === null || _activity$$3 === void 0 ? void 0 : _activity$$3['android:launchMode']) === 'singleTask') { 236 for (const intentFilter of activity['intent-filter'] || []) { 237 const properties = propertiesFromIntentFilter(intentFilter); 238 239 if (isValidRedirectIntentFilter(properties)) { 240 if (!intentFilter.data) intentFilter.data = []; 241 intentFilter.data.push({ 242 $: { 243 'android:scheme': scheme 244 } 245 }); 246 } 247 } 248 249 break; 250 } 251 } 252 } 253 254 return androidManifest; 255} 256 257function removeScheme(scheme, androidManifest) { 258 if (!Array.isArray(androidManifest.manifest.application)) { 259 return androidManifest; 260 } 261 262 for (const application of androidManifest.manifest.application) { 263 for (const activity of application.activity || []) { 264 var _activity$$4; 265 266 if ((activity === null || activity === void 0 ? void 0 : (_activity$$4 = activity.$) === null || _activity$$4 === void 0 ? void 0 : _activity$$4['android:launchMode']) === 'singleTask') { 267 for (const intentFilter of activity['intent-filter'] || []) { 268 // Parse valid intent filters... 269 const properties = propertiesFromIntentFilter(intentFilter); 270 271 if (isValidRedirectIntentFilter(properties)) { 272 for (const dataKey in (intentFilter === null || intentFilter === void 0 ? void 0 : intentFilter.data) || []) { 273 var _intentFilter$data2, _data$$6; 274 275 const data = (_intentFilter$data2 = intentFilter.data) === null || _intentFilter$data2 === void 0 ? void 0 : _intentFilter$data2[dataKey]; 276 277 if ((data === null || data === void 0 ? void 0 : (_data$$6 = data.$) === null || _data$$6 === void 0 ? void 0 : _data$$6['android:scheme']) === scheme) { 278 var _intentFilter$data3; 279 280 (_intentFilter$data3 = intentFilter.data) === null || _intentFilter$data3 === void 0 ? true : delete _intentFilter$data3[dataKey]; 281 } 282 } 283 } 284 } 285 286 break; 287 } 288 } 289 } 290 291 return androidManifest; 292} 293//# sourceMappingURL=Scheme.js.map