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