Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ async function buildAndroid(
}

export function build(gradleArgs: string[], sourceDir: string) {
process.chdir(sourceDir);
const cmd = process.platform.startsWith('win') ? 'gradlew.bat' : './gradlew';
logger.info('Building the app...');
logger.debug(`Running command "${cmd} ${gradleArgs.join(' ')}"`);
Expand All @@ -103,7 +102,7 @@ export const options = [
{
name: '--tasks <list>',
description:
'Run custom Gradle tasks. By default it\'s "assembleDebug". Will override passed mode and variant arguments.',
"Run custom Gradle tasks instead of the command's default tasks. Will override passed mode and variant arguments.",
parse: (val: string) => val.split(','),
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
*
*/

import {execSync, execFileSync} from 'child_process';
import {execFileSync} from 'child_process';

/**
* Parses the output of the 'adb devices' command
Expand Down Expand Up @@ -34,7 +34,7 @@ function parseDevicesResult(result: string): Array<string> {
*/
function getDevices(adbPath: string): Array<string> {
try {
const devicesResult = execSync(`"${adbPath}" devices`);
const devicesResult = execFileSync(adbPath, ['devices']);
return parseDevicesResult(devicesResult.toString());
} catch (e) {
return [];
Expand All @@ -61,7 +61,7 @@ function getAvailableCPUs(adbPath: string, device: string): Array<string> {
).toString();
}

return (cpus || '').trim().split(',');
return cpus.trim().split(',').filter(Boolean);
} catch (e) {
return [];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ export function getTaskNames(
tasks && tasks.length ? tasks : [taskPrefix + toPascalCase(mode)];

return appName
? appTasks.map((command) => `${appName}:${command}`)
? appTasks.map((command) =>
command.includes(':') ? command : `${appName}:${command}`,
)
: appTasks;
}
98 changes: 47 additions & 51 deletions packages/cli-platform-android/src/commands/runAndroid/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,21 @@ async function runAndroid(_argv: Array<string>, config: Config, args: Flags) {

let {packager, port} = args;

if (args.binaryPath) {
if (args.tasks) {
throw new CLIError(
'binary-path and tasks were specified, but they are not compatible. Specify only one',
);
}

args.binaryPath = path.resolve(config.root, args.binaryPath);
if (!fs.existsSync(args.binaryPath)) {
throw new CLIError(
'binary-path was specified, but the file was not found.',
);
}
}

if (packager) {
const {port: newPort, startPackager} = await findDevServerPort(
port,
Expand All @@ -80,25 +95,7 @@ async function runAndroid(_argv: Array<string>, config: Config, args: Flags) {
link.setVersion(config.reactNativeVersion);
}

if (args.binaryPath) {
if (args.tasks) {
throw new CLIError(
'binary-path and tasks were specified, but they are not compatible. Specify only one',
);
}

args.binaryPath = path.isAbsolute(args.binaryPath)
? args.binaryPath
: path.join(config.root, args.binaryPath);

if (args.binaryPath && !fs.existsSync(args.binaryPath)) {
throw new CLIError(
'binary-path was specified, but the file was not found.',
);
}
}

let androidProject = getAndroidProject(config);
const androidProject = {...getAndroidProject(config)};

if (args.mainActivity) {
androidProject.mainActivity = args.mainActivity;
Expand All @@ -107,7 +104,7 @@ async function runAndroid(_argv: Array<string>, config: Config, args: Flags) {
return buildAndRun(args, androidProject);
}

const defaultPort = 5552;
const defaultPort = 5554;
async function getAvailableDevicePort(
port: number = defaultPort,
): Promise<number> {
Expand All @@ -119,7 +116,7 @@ async function getAvailableDevicePort(
if (port > 5682) {
throw new CLIError('Failed to launch emulator...');
}
if (devices.some((d) => d.includes(port.toString()))) {
if (devices.includes(`emulator-${port}`)) {
return await getAvailableDevicePort(port + 2);
}
return port;
Expand All @@ -134,7 +131,6 @@ async function buildAndRun(args: Flags, androidProject: AndroidProject) {
args.device = args.deviceId;
}

process.chdir(androidProject.sourceDir);
const cmd = process.platform.startsWith('win') ? 'gradlew.bat' : './gradlew';

const adbPath = getAdbPath();
Expand Down Expand Up @@ -169,8 +165,26 @@ async function buildAndRun(args: Flags, androidProject: AndroidProject) {
);
}

let deviceId = device.deviceId;
if (!device.connected) {
const port = await getAvailableDevicePort();
deviceId = `emulator-${port}`;
logger.info('Launching emulator...');
const result = await tryLaunchEmulator(
adbPath,
device.readableName,
port,
);
if (!result.success) {
throw new CLIError(
`Failed to launch emulator. Reason: ${pico.dim(result.error || '')}`,
);
}
logger.info('Successfully launched emulator.');
}

if (args.interactive) {
const users = checkUsers(device.deviceId as string, adbPath);
const users = checkUsers(deviceId as string, adbPath);
if (users && users.length > 1) {
const user = await promptForUser(users);

Expand All @@ -180,30 +194,11 @@ async function buildAndRun(args: Flags, androidProject: AndroidProject) {
}
}

if (device.connected) {
return runOnSpecificDevice(
{...args, device: device.deviceId},
adbPath,
androidProject,
selectedTask,
);
}

const port = await getAvailableDevicePort();
const emulator = `emulator-${port}`;
logger.info('Launching emulator...');
const result = await tryLaunchEmulator(adbPath, device.readableName, port);
if (result.success) {
logger.info('Successfully launched emulator.');
return runOnSpecificDevice(
{...args, device: emulator},
adbPath,
androidProject,
selectedTask,
);
}
throw new CLIError(
`Failed to launch emulator. Reason: ${pico.dim(result.error || '')}`,
return runOnSpecificDevice(
{...args, device: deviceId},
adbPath,
androidProject,
selectedTask,
);
}

Expand Down Expand Up @@ -273,13 +268,14 @@ function runOnSpecificDevice(
selectedTask,
);
} else {
logger.error(
`Could not find device: "${device}". Please choose one of the following:`,
...devices,
throw new CLIError(
`Could not find device: "${device}". Please choose one of the following: ${devices.join(
', ',
)}`,
);
}
} else {
logger.error('No Android device or emulator connected.');
throw new CLIError('No Android device or emulator connected.');
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import {execSync} from 'child_process';
import {execFileSync} from 'child_process';
import adb from './adb';
import getAdbPath from './getAdbPath';
import {getEmulators} from './tryLaunchEmulator';
import {toPascalCase} from './toPascalCase';
import os from 'os';
import pico from 'picocolors';
import {CLIError, prompt} from '@react-native-community/cli-tools';

Expand All @@ -21,14 +20,10 @@ type DeviceData = {
*/
function getEmulatorName(deviceId: string) {
const adbPath = getAdbPath();
const buffer = execSync(`${adbPath} -s ${deviceId} emu avd name`);
const buffer = execFileSync(adbPath, ['-s', deviceId, 'emu', 'avd', 'name']);

// 1st line should get us emu name
return buffer
.toString()
.split(os.EOL)[0]
.replace(/(\r\n|\n|\r)/gm, '')
.trim();
return buffer.toString().split(/\r?\n/)[0].trim();
}

/**
Expand All @@ -38,13 +33,14 @@ function getEmulatorName(deviceId: string) {
*/
function getPhoneName(deviceId: string) {
const adbPath = getAdbPath();
const buffer = execSync(
`${adbPath} -s ${deviceId} shell getprop | grep ro.product.model`,
);
return buffer
.toString()
.replace(/\[ro\.product\.model\]:\s*\[(.*)\]/, '$1')
.trim();
const buffer = execFileSync(adbPath, [
'-s',
deviceId,
'shell',
'getprop',
'ro.product.model',
]);
return buffer.toString().trim();
}

async function promptForDeviceSelection(
Expand Down Expand Up @@ -75,7 +71,7 @@ async function listAndroidDevices() {
const adbPath = getAdbPath();
const devices = adb.getDevices(adbPath);

let allDevices: Array<DeviceData> = [];
const allDevices: Array<DeviceData> = [];

devices.forEach((deviceId) => {
if (deviceId.includes('emulator')) {
Expand All @@ -85,24 +81,29 @@ async function listAndroidDevices() {
connected: true,
type: 'emulator',
};
allDevices = [...allDevices, emulatorData];
allDevices.push(emulatorData);
} else {
const phoneData: DeviceData = {
deviceId,
readableName: getPhoneName(deviceId),
type: 'phone',
connected: true,
};
allDevices = [...allDevices, phoneData];
allDevices.push(phoneData);
}
});

const emulators = getEmulators();
const emulatorNames = new Set(
allDevices
.filter((device) => device.type === 'emulator')
.map((device) => device.readableName),
);

// Find not booted ones:
emulators.forEach((emulatorName) => {
// skip those already booted
if (allDevices.some((device) => device.readableName === emulatorName)) {
if (emulatorNames.has(emulatorName)) {
return;
}
const emulatorData: DeviceData = {
Expand All @@ -111,7 +112,8 @@ async function listAndroidDevices() {
type: 'emulator',
connected: false,
};
allDevices = [...allDevices, emulatorData];
allDevices.push(emulatorData);
emulatorNames.add(emulatorName);
});

const selectedDevice = await promptForDeviceSelection(allDevices);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ async function runOnAllDevices(
androidProject.appName,
args.mode,
args.tasks,
'install',
args.user !== undefined ? 'assemble' : 'install',
);

if (args.extraParams) {
Expand Down Expand Up @@ -101,7 +101,7 @@ async function runOnAllDevices(
(devices.length > 0 ? devices : [undefined]).forEach(
(device: string | void) => {
tryRunAdbReverse(args.port, device);
if (args.binaryPath && device) {
if ((args.binaryPath || args.user !== undefined) && device) {
tryInstallAppOnDevice(args, adbPath, device, androidProject);
}
tryLaunchAppOnDevice(device, androidProject, adbPath, args);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ function tryInstallAppOnDevice(

// handle if selected task from interactive mode, or mode from arguments, includes build flavour as well, eg. installProductionDebug should create ['production','debug'] array
const variantFromSelectedTask = (selectedTask ?? args.mode)
?.replace('install', '')
?.replace(/^install/, '')
.replace(/^./, (letter) => letter.toLowerCase())
.split(/(?=[A-Z])/);

// create path to output file, eg. `production/debug`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ function tryLaunchAppOnDevice(
'android.intent.category.LAUNCHER',
];

if (args.user !== undefined) {
adbArgs.push('--user', `${args.user}`);
}

if (device) {
adbArgs.unshift('-s', device);
logger.info(`Starting the app on "${device}"...`);
Expand Down
Loading