1import path from 'path'; 2 3import { Log } from '../../log'; 4import { assembleAsync, installAsync } from '../../start/platforms/android/gradle'; 5import { setNodeEnv } from '../../utils/nodeEnv'; 6import { getSchemesForAndroidAsync } from '../../utils/scheme'; 7import { ensureNativeProjectAsync } from '../ensureNativeProject'; 8import { logProjectLogsLocation } from '../hints'; 9import { startBundlerAsync } from '../startBundler'; 10import { resolveInstallApkNameAsync } from './resolveInstallApkName'; 11import { Options, ResolvedOptions, resolveOptionsAsync } from './resolveOptions'; 12 13const debug = require('debug')('expo:run:android'); 14 15export async function runAndroidAsync(projectRoot: string, { install, ...options }: Options) { 16 // TODO: Add support for setting as production. 17 setNodeEnv('development'); 18 19 await ensureNativeProjectAsync(projectRoot, { platform: 'android', install }); 20 21 const props = await resolveOptionsAsync(projectRoot, options); 22 23 debug('Package name: ' + props.packageName); 24 Log.log('› Building app...'); 25 26 const androidProjectRoot = path.join(projectRoot, 'android'); 27 28 await assembleAsync(androidProjectRoot, { 29 variant: props.variant, 30 port: props.port, 31 appName: props.appName, 32 buildCache: props.buildCache, 33 }); 34 35 const manager = await startBundlerAsync(projectRoot, { 36 port: props.port, 37 // If a scheme is specified then use that instead of the package name. 38 scheme: (await getSchemesForAndroidAsync(projectRoot))?.[0], 39 headless: !props.shouldStartBundler, 40 }); 41 42 await installAppAsync(androidProjectRoot, props); 43 44 await manager.getDefaultDevServer().openCustomRuntimeAsync( 45 'emulator', 46 { 47 applicationId: props.packageName, 48 }, 49 { device: props.device.device } 50 ); 51 52 if (props.shouldStartBundler) { 53 logProjectLogsLocation(); 54 } 55} 56 57async function installAppAsync(androidProjectRoot: string, props: ResolvedOptions) { 58 // Find the APK file path 59 const apkFile = await resolveInstallApkNameAsync(props.device.device, props); 60 61 if (apkFile) { 62 // Attempt to install the APK from the file path 63 const binaryPath = path.join(props.apkVariantDirectory, apkFile); 64 debug('Installing:', binaryPath); 65 await props.device.installAppAsync(binaryPath); 66 } else { 67 // If we cannot resolve the APK file path then we can attempt to install using Gradle. 68 // This offers more advanced resolution that we may not have first class support for. 69 Log.log('› Failed to locate binary file, installing with Gradle...'); 70 await installAsync(androidProjectRoot, { 71 variant: props.variant ?? 'debug', 72 appName: props.appName ?? 'app', 73 port: props.port, 74 }); 75 } 76} 77