// Copyright 2015-present 650 Industries. All rights reserved. 'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.runShellAppModificationsAsync = exports.copyInitialShellAppFilesAsync = undefined; let regexFileAsync = (() => { var _ref = _asyncToGenerator(function* (regex, replace, filename) { let file = yield fs.readFile(filename); let fileString = file.toString(); yield fs.writeFile(filename, fileString.replace(regex, replace)); }); return function regexFileAsync(_x, _x2, _x3) { return _ref.apply(this, arguments); }; })(); // Matches sed /d behavior let deleteLinesInFileAsync = (() => { var _ref2 = _asyncToGenerator(function* (startRegex, endRegex, filename) { let file = yield fs.readFile(filename); let fileString = file.toString(); let lines = fileString.split(/\r?\n/); let filteredLines = []; let inDeleteRange = false; for (let i = 0; i < lines.length; i++) { if (lines[i].match(startRegex)) { inDeleteRange = true; } if (!inDeleteRange) { filteredLines.push(lines[i]); } if (inDeleteRange && lines[i].match(endRegex)) { inDeleteRange = false; } } yield fs.writeFile(filename, filteredLines.join('\n')); }); return function deleteLinesInFileAsync(_x4, _x5, _x6) { return _ref2.apply(this, arguments); }; })(); let copyInitialShellAppFilesAsync = exports.copyInitialShellAppFilesAsync = (() => { var _ref4 = _asyncToGenerator(function* (androidSrcPath, shellPath, isDetached = false) { let _exponentDirectory = exponentDirectory(); if (_exponentDirectory) { yield spawnAsync(`../../tools-public/generate-dynamic-macros-android.sh`, [], { stdio: 'inherit', cwd: path.join(_exponentDirectory, 'android', 'app') }); // populate android template files now since we take out the prebuild step later on } let copyToShellApp = (() => { var _ref5 = _asyncToGenerator(function* (fileName) { try { yield fs.copy(path.join(androidSrcPath, fileName), path.join(shellPath, fileName)); } catch (e) { // android.iml is only available locally, not on the builders, so don't crash when this happens console.warn(`Warning: Could not copy ${fileName} to shell app directory.`); } }); return function copyToShellApp(_x10) { return _ref5.apply(this, arguments); }; })(); if (!isDetached) { yield copyToShellApp('expoview'); yield copyToShellApp('ReactCommon'); yield copyToShellApp('ReactAndroid'); } yield copyToShellApp('android.iml'); yield copyToShellApp('app'); yield copyToShellApp('build.gradle'); yield copyToShellApp('gradle'); yield copyToShellApp('gradle.properties'); yield copyToShellApp('gradlew'); yield copyToShellApp('settings.gradle'); yield copyToShellApp('maven'); yield copyToShellApp('debug.keystore'); yield copyToShellApp('run.sh'); yield copyToShellApp('detach-scripts'); }); return function copyInitialShellAppFilesAsync(_x8, _x9) { return _ref4.apply(this, arguments); }; })(); let copyIconsToResSubfolders = (() => { var _ref7 = _asyncToGenerator(function* (resDirPath, folderPrefix, fileName, iconUrl, isLocalUrl) { return Promise.all(imageKeys.map((() => { var _ref8 = _asyncToGenerator(function* (key) { try { const dirPath = path.join(resDirPath, `${folderPrefix}-${key}`); fs.accessSync(dirPath, fs.constants.F_OK); if (isLocalUrl) { return fs.copyFileSync(iconUrl, path.join(dirPath, fileName)); } return yield saveUrlToPathAsync(iconUrl, path.join(dirPath, fileName)); } catch (e) { // directory does not exist, so ignore } }); return function (_x17) { return _ref8.apply(this, arguments); }; })())); }); return function copyIconsToResSubfolders(_x12, _x13, _x14, _x15, _x16) { return _ref7.apply(this, arguments); }; })(); let runShellAppModificationsAsync = exports.runShellAppModificationsAsync = (() => { var _ref9 = _asyncToGenerator(function* (context, isDetached = false) { let shellPath = shellPathForContext(context); let url = context.published.url; let manifest = context.config; // manifest or app.json let releaseChannel = context.published.releaseChannel; if (!context.data.privateConfig) { console.warn('Warning: No config file specified.'); } let fullManifestUrl = `${url.replace('exp://', 'https://')}/index.exp`; let versionCode = 1; let javaPackage = manifest.android.package; if (manifest.android.versionCode) { versionCode = manifest.android.versionCode; } if (!javaPackage) { throw new Error('Must specify androidPackage option (either from manifest or on command line).'); } let name = manifest.name; let iconUrl = manifest.android && manifest.android.iconUrl ? manifest.android.iconUrl : manifest.iconUrl; let scheme = manifest.scheme || manifest.detach && manifest.detach.scheme; let bundleUrl = manifest.bundleUrl; let isFullManifest = !!bundleUrl; let notificationIconUrl = manifest.notification ? manifest.notification.iconUrl : null; let version = manifest.version ? manifest.version : '0.0.0'; let backgroundImages = backgroundImagesForApp(shellPath, manifest); let splashBackgroundColor = getSplashScreenBackgroundColor(manifest); if (isDetached) { // manifest is actually just app.json in this case, so iconUrl fields don't exist iconUrl = manifest.android && manifest.android.icon ? manifest.android.icon : manifest.icon; notificationIconUrl = manifest.notification ? manifest.notification.icon : null; } // Clean build directories yield fs.remove(path.join(shellPath, 'app', 'build')); yield fs.remove(path.join(shellPath, 'ReactAndroid', 'build')); yield fs.remove(path.join(shellPath, 'expoview', 'build')); yield fs.remove(path.join(shellPath, 'app', 'src', 'androidTest')); if (isDetached) { let appBuildGradle = path.join(shellPath, 'app', 'build.gradle'); yield regexFileAsync(/\/\* UNCOMMENT WHEN DISTRIBUTING/g, '', appBuildGradle); yield regexFileAsync(/END UNCOMMENT WHEN DISTRIBUTING \*\//g, '', appBuildGradle); yield regexFileAsync(`compile project(path: ':expoview')`, '', appBuildGradle); // Don't need to compile expoview or ReactAndroid // react-native link looks for a \n so we need that. See https://github.com/facebook/react-native/blob/master/local-cli/link/android/patches/makeSettingsPatch.js yield fs.writeFile(path.join(shellPath, 'settings.gradle'), `include ':app'\n`); yield regexFileAsync('TEMPLATE_INITIAL_URL', url, path.join(shellPath, 'app', 'src', 'main', 'java', 'host', 'exp', 'exponent', 'MainActivity.java')); } // Package yield regexFileAsync(`applicationId 'host.exp.exponent'`, `applicationId '${javaPackage}'`, path.join(shellPath, 'app', 'build.gradle')); yield regexFileAsync(`android:name="host.exp.exponent"`, `android:name="${javaPackage}"`, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); // Versions let buildGradleFile = yield fs.readFileSync(path.join(shellPath, 'app', 'build.gradle'), 'utf8'); let androidVersion = buildGradleFile.match(/versionName '(\S+)'/)[1]; yield regexFileAsync('VERSION_NAME = null', `VERSION_NAME = "${androidVersion}"`, path.join(shellPath, 'app', 'src', 'main', 'java', 'host', 'exp', 'exponent', 'generated', 'AppConstants.java')); yield deleteLinesInFileAsync(`BEGIN\ VERSIONS`, `END\ VERSIONS`, path.join(shellPath, 'app', 'build.gradle')); yield regexFileAsync('// ADD VERSIONS HERE', `versionCode ${versionCode} versionName '${version}'`, path.join(shellPath, 'app', 'build.gradle')); // Remove Exponent build script if (!isDetached) { yield regexFileAsync(`preBuild.dependsOn generateDynamicMacros`, ``, path.join(shellPath, 'expoview', 'build.gradle')); } // change javaMaxHeapSize yield regexFileAsync(`javaMaxHeapSize "8g"`, `javaMaxHeapSize "6g"`, path.join(shellPath, 'app', 'build.gradle')); // Push notifications yield regexFileAsync('"package_name": "host.exp.exponent"', `"package_name": "${javaPackage}"`, path.join(shellPath, 'app', 'google-services.json')); // TODO: actually use the correct file // TODO: probably don't need this in both places yield regexFileAsync(/host\.exp\.exponent\.permission\.C2D_MESSAGE/g, `${javaPackage}.permission.C2D_MESSAGE`, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); if (!isDetached) { yield regexFileAsync(/host\.exp\.exponent\.permission\.C2D_MESSAGE/g, `${javaPackage}.permission.C2D_MESSAGE`, path.join(shellPath, 'expoview', 'src', 'main', 'AndroidManifest.xml')); } // Set INITIAL_URL, SHELL_APP_SCHEME and SHOW_LOADING_VIEW yield regexFileAsync('INITIAL_URL = null', `INITIAL_URL = "${url}"`, path.join(shellPath, 'app', 'src', 'main', 'java', 'host', 'exp', 'exponent', 'generated', 'AppConstants.java')); if (scheme) { yield regexFileAsync('SHELL_APP_SCHEME = null', `SHELL_APP_SCHEME = "${scheme}"`, path.join(shellPath, 'app', 'src', 'main', 'java', 'host', 'exp', 'exponent', 'generated', 'AppConstants.java')); } if (shouldShowLoadingView(manifest)) { yield regexFileAsync('SHOW_LOADING_VIEW_IN_SHELL_APP = false', 'SHOW_LOADING_VIEW_IN_SHELL_APP = true', path.join(shellPath, 'app', 'src', 'main', 'java', 'host', 'exp', 'exponent', 'generated', 'AppConstants.java')); } if (isDetached) { yield regexFileAsync('IS_DETACHED = false', `IS_DETACHED = true`, path.join(shellPath, 'app', 'src', 'main', 'java', 'host', 'exp', 'exponent', 'generated', 'AppConstants.java')); } // App name yield regexFileAsync('"app_name">Expo', `"app_name">${xmlWeirdAndroidEscape(name)}`, path.join(shellPath, 'app', 'src', 'main', 'res', 'values', 'strings.xml')); // Splash Screen background color yield regexFileAsync('"splashBackground">#FFFFFF', `"splashBackground">${splashBackgroundColor}`, path.join(shellPath, 'app', 'src', 'main', 'res', 'values', 'colors.xml')); // show only background color if LoadingView will appear if (shouldShowLoadingView(manifest)) { yield regexFileAsync(/.*<\/item>/, '', path.join(shellPath, 'app', 'src', 'main', 'res', 'drawable', 'splash_background.xml')); } // Remove exp:// scheme from LauncherActivity yield deleteLinesInFileAsync(`START\ LAUNCHER\ INTENT\ FILTERS`, `END\ LAUNCHER\ INTENT\ FILTERS`, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); // Remove LAUNCHER category from HomeActivity yield deleteLinesInFileAsync(`START\ HOME\ INTENT\ FILTERS`, `END\ HOME\ INTENT\ FILTERS`, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); if (isDetached) { // Add LAUNCHER category to MainActivity yield regexFileAsync('', ` `, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); } else { // Add LAUNCHER category to ShellAppActivity yield regexFileAsync('', ` `, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); } // Add shell app scheme if (scheme) { yield regexFileAsync('', ` `, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); } // Add permissions if (manifest.android && manifest.android.permissions) { const content = yield fs.readFileSync(path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml'), 'utf-8'); // Get the list of optional permissions form manifest const permissions = content.replace(/(([\s\S]*)|([\s\S]*))/g, '').match(/android:name=".+"/g).map(function (p) { return p.replace(/(android:name=|")/g, ''); }); const whitelist = []; manifest.android.permissions.forEach(function (s) { if (s.includes('.')) { whitelist.push(s); } else { permissions.forEach(function (identifier) { if (identifier.split('.').pop() === s) { whitelist.push(identifier); } }); } }); // Permissions we need to remove from the generated manifest const blacklist = ['android.permission.ACCESS_COARSE_LOCATION', 'android.permission.ACCESS_FINE_LOCATION', 'android.permission.CAMERA', 'android.permission.MANAGE_DOCUMENTS', 'android.permission.READ_CONTACTS', 'android.permission.READ_CALENDAR', 'android.permission.WRITE_CALENDAR', 'android.permission.READ_EXTERNAL_STORAGE', 'android.permission.READ_INTERNAL_STORAGE', 'android.permission.READ_PHONE_STATE', 'android.permission.RECORD_AUDIO', 'android.permission.USE_FINGERPRINT', 'android.permission.VIBRATE', 'android.permission.WRITE_EXTERNAL_STORAGE', 'com.anddoes.launcher.permission.UPDATE_COUNT', 'com.android.launcher.permission.INSTALL_SHORTCUT', 'com.google.android.gms.permission.ACTIVITY_RECOGNITION', 'com.google.android.providers.gsf.permission.READ_GSERVICES', 'com.htc.launcher.permission.READ_SETTINGS', 'com.htc.launcher.permission.UPDATE_SHORTCUT', 'com.majeur.launcher.permission.UPDATE_BADGE', 'com.sec.android.provider.badge.permission.READ', 'com.sec.android.provider.badge.permission.WRITE', 'com.sonyericsson.home.permission.BROADCAST_BADGE'].filter(function (p) { return !whitelist.includes(p); }); yield deleteLinesInFileAsync(`BEGIN\ OPTIONAL\ PERMISSIONS`, `END\ OPTIONAL\ PERMISSIONS`, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); yield regexFileAsync('', ` ${whitelist.map(function (p) { return ``; }).join('\n')} ${blacklist.map(function (p) { return ``; }).join('\n')} `, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); } // OAuth redirect scheme yield regexFileAsync('', ``, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); // Embed manifest and bundle if (isFullManifest) { yield fs.writeFileSync(path.join(shellPath, 'app', 'src', 'main', 'assets', 'shell-app-manifest.json'), JSON.stringify(manifest)); yield saveUrlToPathAsync(bundleUrl, path.join(shellPath, 'app', 'src', 'main', 'assets', 'shell-app.bundle')); yield regexFileAsync('// START EMBEDDED RESPONSES', ` // START EMBEDDED RESPONSES embeddedResponses.add(new Constants.EmbeddedResponse("${fullManifestUrl}", "assets://shell-app-manifest.json", "application/json")); embeddedResponses.add(new Constants.EmbeddedResponse("${bundleUrl}", "assets://shell-app.bundle", "application/javascript"));`, path.join(shellPath, 'app', 'src', 'main', 'java', 'host', 'exp', 'exponent', 'generated', 'AppConstants.java')); } yield regexFileAsync('RELEASE_CHANNEL = "default"', `RELEASE_CHANNEL = "${releaseChannel}"`, path.join(shellPath, 'app', 'src', 'main', 'java', 'host', 'exp', 'exponent', 'generated', 'AppConstants.java')); // Icon if (iconUrl) { (yield globby(['**/ic_launcher.png'], { cwd: path.join(shellPath, 'app', 'src', 'main', 'res'), absolute: true })).forEach(function (filePath) { fs.removeSync(filePath); }); yield copyIconsToResSubfolders(path.join(shellPath, 'app', 'src', 'main', 'res'), 'mipmap', 'ic_launcher.png', iconUrl, isDetached); } if (notificationIconUrl) { (yield globby(['**/shell_notification_icon.png'], { cwd: path.join(shellPath, 'app', 'src', 'main', 'res'), absolute: true })).forEach(function (filePath) { fs.removeSync(filePath); }); yield copyIconsToResSubfolders(path.join(shellPath, 'app', 'src', 'main', 'res'), 'drawable', 'shell_notification_icon.png', notificationIconUrl, isDetached); } // Splash Background if (backgroundImages && backgroundImages.length > 0) { // Delete the placeholder images (yield globby(['**/shell_launch_background_image.png'], { cwd: path.join(shellPath, 'app', 'src', 'main', 'res'), absolute: true })).forEach(function (filePath) { fs.removeSync(filePath); }); _.forEach(backgroundImages, (() => { var _ref10 = _asyncToGenerator(function* (image) { yield saveUrlToPathAsync(image.url, image.path); }); return function (_x19) { return _ref10.apply(this, arguments); }; })()); } if (manifest.bundledAssets) { yield downloadAssetsAsync(manifest.bundledAssets, `${shellPath}/app/src/main/assets`); } let certificateHash = ''; let googleAndroidApiKey = ''; let privateConfig = context.data.privateConfig; if (privateConfig) { let branch = privateConfig.branch; let fabric = privateConfig.fabric; let googleMaps = privateConfig.googleMaps; let googleSignIn = privateConfig.googleSignIn; // Branch if (branch) { yield regexFileAsync('', ``, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); } // Fabric if (fabric) { yield fs.remove(path.join(shellPath, 'app', 'fabric.properties')); yield fs.writeFileSync(path.join(shellPath, 'app', 'fabric.properties'), `apiSecret=${fabric.buildSecret}\n`); yield deleteLinesInFileAsync(`BEGIN\ FABRIC\ CONFIG`, `END\ FABRIC\ CONFIG`, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); yield regexFileAsync('', ``, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); } // Google Maps if (googleMaps) { yield deleteLinesInFileAsync(`BEGIN\ GOOGLE\ MAPS\ CONFIG`, `END\ GOOGLE\ MAPS\ CONFIG`, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); yield regexFileAsync('', ``, path.join(shellPath, 'app', 'src', 'main', 'AndroidManifest.xml')); } // Google Login if (googleSignIn) { certificateHash = googleSignIn.certificateHash; googleAndroidApiKey = googleSignIn.apiKey; } } // Google sign in yield regexFileAsync(/"current_key": "(.*?)"/, `"current_key": "${googleAndroidApiKey}"`, path.join(shellPath, 'app', 'google-services.json')); yield regexFileAsync(/"certificate_hash": "(.*?)"/, `"certificate_hash": "${certificateHash}"`, path.join(shellPath, 'app', 'google-services.json')); }); return function runShellAppModificationsAsync(_x18) { return _ref9.apply(this, arguments); }; })(); let buildShellAppAsync = (() => { var _ref11 = _asyncToGenerator(function* (context) { let shellPath = shellPathForContext(context); if (context.build.android) { let androidBuildConfiguration = context.build.android; try { yield fs.remove(`shell-unaligned.apk`); yield fs.remove(`shell.apk`); } catch (e) {} const gradleArgs = [`assembleProdRelease`]; if (process.env.GRADLE_DAEMON_DISABLED) { gradleArgs.unshift('--no-daemon'); } yield spawnAsyncThrowError(`./gradlew`, gradleArgs, { stdio: 'inherit', cwd: shellPath }); yield fs.copy(path.join(shellPath, 'app', 'build', 'outputs', 'apk', 'app-prod-release-unsigned.apk'), `shell-unaligned.apk`); yield spawnAsync(`jarsigner`, ['-verbose', '-sigalg', 'SHA1withRSA', '-digestalg', 'SHA1', '-storepass', androidBuildConfiguration.keystorePassword, '-keypass', androidBuildConfiguration.keyPassword, '-keystore', androidBuildConfiguration.keystore, 'shell-unaligned.apk', androidBuildConfiguration.keyAlias]); yield spawnAsync(`zipalign`, ['-v', '4', 'shell-unaligned.apk', 'shell.apk']); try { yield fs.remove('shell-unaligned.apk'); } catch (e) {} yield spawnAsync(`jarsigner`, ['-verify', '-verbose', '-certs', '-keystore', androidBuildConfiguration.keystore, 'shell.apk']); yield fs.copy('shell.apk', androidBuildConfiguration.outputFile || '/tmp/shell-signed.apk'); } else { try { yield fs.remove('shell-debug.apk'); } catch (e) {} yield spawnAsyncThrowError(`./gradlew`, ['assembleDevRemoteKernelDebug'], { stdio: 'inherit', cwd: shellPath }); yield fs.copy(path.join(shellPath, 'app', 'build', 'outputs', 'apk', 'app-devRemoteKernel-debug.apk'), `/tmp/shell-debug.apk`); } }); return function buildShellAppAsync(_x20) { return _ref11.apply(this, arguments); }; })(); let downloadAssetsAsync = (() => { var _ref12 = _asyncToGenerator(function* (assets, dest) { // Compat with exp 46.x.x, can remove when this version is phasing out. if (typeof assets[0] === 'object') { assets = assets.reduce(function (res, cur) { return res.concat(cur.fileHashes.map(function (h) { return 'asset_' + h + (cur.type ? '.' + cur.type : ''); })); }, []); } yield fs.ensureDir(dest); const batches = _.chunk(assets, 5); for (const batch of batches) { yield Promise.all(batch.map((() => { var _ref13 = _asyncToGenerator(function* (asset) { const extensionIndex = asset.lastIndexOf('.'); const prefixLength = 'asset_'.length; const hash = extensionIndex >= 0 ? asset.substring(prefixLength, extensionIndex) : asset.substring(prefixLength); yield saveUrlToPathAsync('https://d1wp6m56sqw74a.cloudfront.net/~assets/' + hash, path.join(dest, asset)); }); return function (_x23) { return _ref13.apply(this, arguments); }; })())); } }); return function downloadAssetsAsync(_x21, _x22) { return _ref12.apply(this, arguments); }; })(); var _ExponentTools; function _load_ExponentTools() { return _ExponentTools = _interopRequireWildcard(require('./ExponentTools')); } var _StandaloneBuildFlags; function _load_StandaloneBuildFlags() { return _StandaloneBuildFlags = _interopRequireDefault(require('./StandaloneBuildFlags')); } var _StandaloneContext; function _load_StandaloneContext() { return _StandaloneContext = _interopRequireDefault(require('./StandaloneContext')); } function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) newObj[key] = obj[key]; } } newObj.default = obj; return newObj; } } function _asyncToGenerator(fn) { return function () { var gen = fn.apply(this, arguments); return new Promise(function (resolve, reject) { function step(key, arg) { try { var info = gen[key](arg); var value = info.value; } catch (error) { reject(error); return; } if (info.done) { resolve(value); } else { return Promise.resolve(value).then(function (value) { step("next", value); }, function (err) { step("throw", err); }); } } return step("next"); }); }; } const fs = require('fs-extra'); const path = require('path'); const JsonFile = require('@expo/json-file'); const replaceString = require('replace-string'); const _ = require('lodash'); const globby = require('globby'); const { getManifestAsync, saveUrlToPathAsync, spawnAsyncThrowError, spawnAsync } = _ExponentTools || _load_ExponentTools(); const imageKeys = ['ldpi', 'mdpi', 'hdpi', 'xhdpi', 'xxhdpi', 'xxxhdpi']; // Do not call this from anything used by detach function exponentDirectory() { if (process.env.TURTLE_WORKING_DIR_PATH) { return process.env.TURTLE_WORKING_DIR_PATH; } else if (process.env.EXPO_UNIVERSE_DIR) { return path.join(process.env.EXPO_UNIVERSE_DIR, 'exponent'); } else { return null; } } function xmlWeirdAndroidEscape(original) { let noAmps = replaceString(original, '&', '&'); let noLt = replaceString(noAmps, '<', '<'); let noGt = replaceString(noLt, '>', '>'); let noApos = replaceString(noGt, '"', '\\"'); return replaceString(noApos, "'", "\\'"); } exports.updateAndroidShellAppAsync = (() => { var _ref3 = _asyncToGenerator(function* (args) { let { url, sdkVersion, androidPackage, privateConfigFile, keystore, alias, keystorePassword, keyPassword, releaseChannel, outputFile } = args; releaseChannel = releaseChannel ? releaseChannel : 'default'; let manifest = yield getManifestAsync(url, { 'Exponent-SDK-Version': sdkVersion, 'Exponent-Platform': 'android', 'Expo-Release-Channel': releaseChannel }); let fullManifestUrl = `${url.replace('exp://', 'https://')}/index.exp`; let bundleUrl = manifest.bundleUrl; let shellPath = path.join(exponentDirectory(), 'android-shell-app'); yield fs.remove(path.join(shellPath, 'app', 'src', 'main', 'assets', 'shell-app-manifest.json')); yield fs.writeFileSync(path.join(shellPath, 'app', 'src', 'main', 'assets', 'shell-app-manifest.json'), JSON.stringify(manifest)); yield fs.remove(path.join(shellPath, 'app', 'src', 'main', 'assets', 'shell-app.bundle')); yield saveUrlToPathAsync(bundleUrl, path.join(shellPath, 'app', 'src', 'main', 'assets', 'shell-app.bundle')); yield deleteLinesInFileAsync(`START\ EMBEDDED\ RESPONSES`, `END\ EMBEDDED\ RESPONSES`, path.join(shellPath, 'app', 'src', 'main', 'java', 'host', 'exp', 'exponent', 'generated', 'AppConstants.java')); yield regexFileAsync('// ADD EMBEDDED RESPONSES HERE', ` // ADD EMBEDDED RESPONSES HERE // START EMBEDDED RESPONSES embeddedResponses.add(new Constants.EmbeddedResponse("${fullManifestUrl}", "assets://shell-app-manifest.json", "application/json")); embeddedResponses.add(new Constants.EmbeddedResponse("${bundleUrl}", "assets://shell-app.bundle", "application/javascript")); // END EMBEDDED RESPONSES`, path.join(shellPath, 'app', 'src', 'main', 'java', 'host', 'exp', 'exponent', 'generated', 'AppConstants.java')); yield regexFileAsync('RELEASE_CHANNEL = "default"', `RELEASE_CHANNEL = "${releaseChannel}"`, path.join(shellPath, 'app', 'src', 'main', 'java', 'host', 'exp', 'exponent', 'generated', 'AppConstants.java')); }); function updateAndroidShellAppAsync(_x7) { return _ref3.apply(this, arguments); } return updateAndroidShellAppAsync; })(); function backgroundImagesForApp(shellPath, manifest) { // returns an array like: // [ // {url: 'urlToDownload', path: 'pathToSaveTo'}, // {url: 'anotherURlToDownload', path: 'anotherPathToSaveTo'}, // ] let basePath = path.join(shellPath, 'app', 'src', 'main', 'res'); if (_.get(manifest, 'android.splash')) { var splash = _.get(manifest, 'android.splash'); return _.reduce(imageKeys, function (acc, imageKey) { let url = _.get(splash, `${imageKey}Url`); if (url) { acc.push({ url, path: path.join(basePath, `drawable-${imageKey}`, 'shell_launch_background_image.png') }); } return acc; }, []); } if (_.get(manifest, 'splash.imageUrl')) { let url = _.get(manifest, 'splash.imageUrl'); return [{ url, path: path.join(basePath, 'drawable-xxxhdpi', 'shell_launch_background_image.png') }]; } return []; } function getSplashScreenBackgroundColor(manifest) { let backgroundColor; if (manifest.android && manifest.android.splash && manifest.android.splash.backgroundColor) { backgroundColor = manifest.android.splash.backgroundColor; } else if (manifest.splash && manifest.splash.backgroundColor) { backgroundColor = manifest.splash.backgroundColor; } // Default to white if (!backgroundColor) { backgroundColor = '#FFFFFF'; } return backgroundColor; } /* if resizeMode is 'cover' we should show LoadingView: using an ImageView, unlike having a BitmapDrawable provides a fullscreen image without distortions */ function shouldShowLoadingView(manifest) { return manifest.android && manifest.android.splash && manifest.android.splash.resizeMode && manifest.android.splash.resizeMode === 'cover' || manifest.splash && manifest.splash.resizeMode && manifest.splash.resizeMode === 'cover'; } exports.createAndroidShellAppAsync = (() => { var _ref6 = _asyncToGenerator(function* (args) { let { url, sdkVersion, releaseChannel, privateConfigFile, configuration, keystore, alias, keystorePassword, keyPassword, outputFile } = args; let androidSrcPath = path.join(exponentDirectory(), 'android'); let shellPath = path.join(exponentDirectory(), 'android-shell-app'); yield fs.remove(shellPath); yield fs.ensureDir(shellPath); releaseChannel = releaseChannel ? releaseChannel : 'default'; let manifest = yield getManifestAsync(url, { 'Exponent-SDK-Version': sdkVersion, 'Exponent-Platform': 'android', 'Expo-Release-Channel': releaseChannel }); configuration = configuration ? configuration : 'Release'; let privateConfig; if (privateConfigFile) { let privateConfigContents = yield fs.readFile(privateConfigFile, 'utf8'); privateConfig = JSON.parse(privateConfigContents); } let androidBuildConfiguration; if (keystore && alias && keystorePassword && keyPassword) { androidBuildConfiguration = { keystore, keystorePassword, keyAlias: alias, keyPassword, outputFile }; } let buildFlags = (_StandaloneBuildFlags || _load_StandaloneBuildFlags()).default.createAndroid(configuration, androidBuildConfiguration); let context = (_StandaloneContext || _load_StandaloneContext()).default.createServiceContext(androidSrcPath, null, manifest, privateConfig, buildFlags, url, releaseChannel); yield copyInitialShellAppFilesAsync(androidSrcPath, shellPath); yield runShellAppModificationsAsync(context); if (!args.skipBuild) { yield buildShellAppAsync(context); } }); function createAndroidShellAppAsync(_x11) { return _ref6.apply(this, arguments); } return createAndroidShellAppAsync; })(); function shellPathForContext(context) { if (context.type === 'user') { return path.join(context.data.projectPath, 'android'); } else { return path.join(exponentDirectory(), 'android-shell-app'); } } //# sourceMappingURL=../__sourcemaps__/detach/AndroidShellApp.js.map