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 // NOTE: This is a guess, the developer can overwrite with `NODE_ENV`. 17 setNodeEnv(options.variant === 'release' ? 'production' : 'development'); 18 require('@expo/env').load(projectRoot); 19 20 await ensureNativeProjectAsync(projectRoot, { platform: 'android', install }); 21 22 const props = await resolveOptionsAsync(projectRoot, options); 23 24 debug('Package name: ' + props.packageName); 25 Log.log('› Building app...'); 26 27 const androidProjectRoot = path.join(projectRoot, 'android'); 28 29 await assembleAsync(androidProjectRoot, { 30 variant: props.variant, 31 port: props.port, 32 appName: props.appName, 33 buildCache: props.buildCache, 34 }); 35 36 const manager = await startBundlerAsync(projectRoot, { 37 port: props.port, 38 // If a scheme is specified then use that instead of the package name. 39 scheme: (await getSchemesForAndroidAsync(projectRoot))?.[0], 40 headless: !props.shouldStartBundler, 41 }); 42 43 await installAppAsync(androidProjectRoot, props); 44 45 await manager.getDefaultDevServer().openCustomRuntimeAsync( 46 'emulator', 47 { 48 applicationId: props.packageName, 49 }, 50 { device: props.device.device } 51 ); 52 53 if (props.shouldStartBundler) { 54 logProjectLogsLocation(); 55 } 56} 57 58async function installAppAsync(androidProjectRoot: string, props: ResolvedOptions) { 59 // Find the APK file path 60 const apkFile = await resolveInstallApkNameAsync(props.device.device, props); 61 62 if (apkFile) { 63 // Attempt to install the APK from the file path 64 const binaryPath = path.join(props.apkVariantDirectory, apkFile); 65 debug('Installing:', binaryPath); 66 await props.device.installAppAsync(binaryPath); 67 } else { 68 // If we cannot resolve the APK file path then we can attempt to install using Gradle. 69 // This offers more advanced resolution that we may not have first class support for. 70 Log.log('› Failed to locate binary file, installing with Gradle...'); 71 await installAsync(androidProjectRoot, { 72 variant: props.variant ?? 'debug', 73 appName: props.appName ?? 'app', 74 port: props.port, 75 }); 76 } 77} 78