13d6e487dSEvan Baconimport path from 'path'; 23d6e487dSEvan Bacon 38a424bebSJames Ideimport { resolveInstallApkNameAsync } from './resolveInstallApkName'; 48a424bebSJames Ideimport { Options, ResolvedOptions, resolveOptionsAsync } from './resolveOptions'; 53d6e487dSEvan Baconimport { Log } from '../../log'; 63d6e487dSEvan Baconimport { assembleAsync, installAsync } from '../../start/platforms/android/gradle'; 72dd43328SEvan Baconimport { setNodeEnv } from '../../utils/nodeEnv'; 8*75a300d7SEvan Baconimport { ensurePortAvailabilityAsync } from '../../utils/port'; 93d6e487dSEvan Baconimport { getSchemesForAndroidAsync } from '../../utils/scheme'; 103d6e487dSEvan Baconimport { ensureNativeProjectAsync } from '../ensureNativeProject'; 113d6e487dSEvan Baconimport { logProjectLogsLocation } from '../hints'; 123d6e487dSEvan Baconimport { startBundlerAsync } from '../startBundler'; 133d6e487dSEvan Bacon 143d6e487dSEvan Baconconst debug = require('debug')('expo:run:android'); 153d6e487dSEvan Bacon 163d6e487dSEvan Baconexport async function runAndroidAsync(projectRoot: string, { install, ...options }: Options) { 176a750d06SEvan Bacon // NOTE: This is a guess, the developer can overwrite with `NODE_ENV`. 186a750d06SEvan Bacon setNodeEnv(options.variant === 'release' ? 'production' : 'development'); 196a750d06SEvan Bacon require('@expo/env').load(projectRoot); 202dd43328SEvan Bacon 213d6e487dSEvan Bacon await ensureNativeProjectAsync(projectRoot, { platform: 'android', install }); 223d6e487dSEvan Bacon 233d6e487dSEvan Bacon const props = await resolveOptionsAsync(projectRoot, options); 243d6e487dSEvan Bacon 253d6e487dSEvan Bacon debug('Package name: ' + props.packageName); 263d6e487dSEvan Bacon Log.log('› Building app...'); 273d6e487dSEvan Bacon 283d6e487dSEvan Bacon const androidProjectRoot = path.join(projectRoot, 'android'); 293d6e487dSEvan Bacon 303d6e487dSEvan Bacon await assembleAsync(androidProjectRoot, { 313d6e487dSEvan Bacon variant: props.variant, 323d6e487dSEvan Bacon port: props.port, 333d6e487dSEvan Bacon appName: props.appName, 343d6e487dSEvan Bacon buildCache: props.buildCache, 353d6e487dSEvan Bacon }); 363d6e487dSEvan Bacon 37*75a300d7SEvan Bacon // Ensure the port hasn't become busy during the build. 38*75a300d7SEvan Bacon if (props.shouldStartBundler && !(await ensurePortAvailabilityAsync(projectRoot, props))) { 39*75a300d7SEvan Bacon props.shouldStartBundler = false; 40*75a300d7SEvan Bacon } 41*75a300d7SEvan Bacon 423d6e487dSEvan Bacon const manager = await startBundlerAsync(projectRoot, { 433d6e487dSEvan Bacon port: props.port, 44c4ef02aeSEvan Bacon // If a scheme is specified then use that instead of the package name. 45c4ef02aeSEvan Bacon scheme: (await getSchemesForAndroidAsync(projectRoot))?.[0], 463d6e487dSEvan Bacon headless: !props.shouldStartBundler, 473d6e487dSEvan Bacon }); 483d6e487dSEvan Bacon 493d6e487dSEvan Bacon await installAppAsync(androidProjectRoot, props); 503d6e487dSEvan Bacon 513d6e487dSEvan Bacon await manager.getDefaultDevServer().openCustomRuntimeAsync( 523d6e487dSEvan Bacon 'emulator', 533d6e487dSEvan Bacon { 543d6e487dSEvan Bacon applicationId: props.packageName, 553d6e487dSEvan Bacon }, 563d6e487dSEvan Bacon { device: props.device.device } 573d6e487dSEvan Bacon ); 583d6e487dSEvan Bacon 593d6e487dSEvan Bacon if (props.shouldStartBundler) { 603d6e487dSEvan Bacon logProjectLogsLocation(); 613d6e487dSEvan Bacon } 623d6e487dSEvan Bacon} 633d6e487dSEvan Bacon 643d6e487dSEvan Baconasync function installAppAsync(androidProjectRoot: string, props: ResolvedOptions) { 653d6e487dSEvan Bacon // Find the APK file path 663d6e487dSEvan Bacon const apkFile = await resolveInstallApkNameAsync(props.device.device, props); 673d6e487dSEvan Bacon 683d6e487dSEvan Bacon if (apkFile) { 693d6e487dSEvan Bacon // Attempt to install the APK from the file path 703d6e487dSEvan Bacon const binaryPath = path.join(props.apkVariantDirectory, apkFile); 713d6e487dSEvan Bacon debug('Installing:', binaryPath); 723d6e487dSEvan Bacon await props.device.installAppAsync(binaryPath); 733d6e487dSEvan Bacon } else { 743d6e487dSEvan Bacon // If we cannot resolve the APK file path then we can attempt to install using Gradle. 753d6e487dSEvan Bacon // This offers more advanced resolution that we may not have first class support for. 763d6e487dSEvan Bacon Log.log('› Failed to locate binary file, installing with Gradle...'); 773d6e487dSEvan Bacon await installAsync(androidProjectRoot, { 783d6e487dSEvan Bacon variant: props.variant ?? 'debug', 793d6e487dSEvan Bacon appName: props.appName ?? 'app', 803d6e487dSEvan Bacon port: props.port, 813d6e487dSEvan Bacon }); 823d6e487dSEvan Bacon } 833d6e487dSEvan Bacon} 84