diff --git a/.eslintrc.json b/.eslintrc.json
index 90ec3d5..be4c1f8 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -21,6 +21,9 @@
"quotes": ["error", "single"],
"semi": ["error", "always"],
"@typescript-eslint/ban-types": "off",
- "@typescript-eslint/prefer-nullish-coalescing": "off"
+ "@typescript-eslint/prefer-nullish-coalescing": "off",
+ "@typescript-eslint/no-unsafe-assignment": "off",
+ "@typescript-eslint/no-unsafe-call": "off",
+ "@typescript-eslint/no-unsafe-member-access": "off"
}
}
diff --git a/.github/workflows/test-publish.yml b/.github/workflows/test-publish.yml
index 0d275e3..2d1fabe 100644
--- a/.github/workflows/test-publish.yml
+++ b/.github/workflows/test-publish.yml
@@ -2,8 +2,6 @@ name: 'TestPublish'
on:
pull_request:
push:
- branches:
- - main
tags:
- 'v*'
diff --git a/README-ZH_CN.md b/README-ZH_CN.md
index 549141d..630bcbe 100644
--- a/README-ZH_CN.md
+++ b/README-ZH_CN.md
@@ -134,14 +134,14 @@ android
});
```
-| field | type | required | default | note |
-| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | ----------------------------------------------------------------------------------- |
-| apiLevel | number | false | - | 平台系统镜像的API级别 - 例如,Android Marshmallow的级别为23,Android 10的级别为29。 |
-| target | 'default'
'google_apis'
'playstore'
'android-wear'
'android-wear-cn'
'android-tv'
'google-tv'
'aosp_atd '
'google_atd' | false | 'default' | 系统镜像的目标。 |
-| arch | 'x86_64'
'x86'
'arm64-v8a'
'armeabi-v7a' | false | 'x86_64' | 系统镜像的CPU架构 |
-| package | string | true | - | 此模拟器的系统镜像的路径(例如 'system-images;android-19;google_apis;x86')。 |
-| name | string | false | - | 新模拟器的名称。 |
-| force | boolean | false | - | 创建虚拟设备(覆盖现有的模拟器)。 |
+| field | type | required | default | note |
+| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------------- | ----------------------------------------------------------------------------------- |
+| apiLevel | number | false | - | 平台系统镜像的API级别 - 例如,Android Marshmallow的级别为23,Android 10的级别为29。 |
+| target | 'default'
'google_apis'
'playstore'
'android-wear'
'android-wear-cn'
'android-tv'
'google-tv'
'aosp_atd '
'google_atd' | false | 'default' | 系统镜像的目标。 |
+| arch | 'x86_64'
'x86'
'arm64-v8a'
'armeabi-v7a' | false | 当前系统CPU架构 | 系统镜像的CPU架构 |
+| package | string | true | - | 此模拟器的系统镜像的路径(例如 'system-images;android-19;google_apis;x86')。 |
+| name | string | false | - | 新模拟器的名称。 |
+| force | boolean | false | - | 创建虚拟设备(覆盖现有的模拟器)。 |
- 如果你传了`package`,则参数`apiLevel`,`target`,`arch`将被会略。如果你没有传参`package`,则`apiLevel`参数是必须的。
@@ -361,7 +361,7 @@ android
.then((res) => {
res.forEach((item) => {
console.log(
- `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, vendor: ${item.vendor}, arch: ${item.arch}`
+ `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, target: ${item.target}, arch: ${item.arch}`
);
});
})
@@ -380,7 +380,7 @@ android
.then((res) => {
res.forEach((item) => {
console.log(
- `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, vendor: ${item.vendor}, arch: ${item.arch}`
+ `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, target: ${item.target}, arch: ${item.arch}`
);
});
})
diff --git a/README.md b/README.md
index ac028e4..aacf92c 100644
--- a/README.md
+++ b/README.md
@@ -134,14 +134,14 @@ android
});
```
-| field | type | required | default | note |
-| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | ------------------------------------------------------------------------------------------------ |
-| apiLevel | number | false | - | API level of the platform system image - e.g. 23 for Android Marshmallow, 29 for Android 10. |
-| target | 'default'
'google_apis'
'playstore'
'android-wear'
'android-wear-cn'
'android-tv'
'google-tv'
'aosp_atd '
'google_atd' | false | 'default' | Target of the system image . |
-| arch | 'x86_64'
'x86'
'arm64-v8a'
'armeabi-v7a' | false | 'x86_64' | CPU architecture of the system image |
-| package | string | false | - | Package path of the system image for this AVD (e.g. 'system-images;android-19;google_apis;x86'). |
-| name | string | false | - | Name of the new AVD. |
-| force | boolean | false | - | Forces creation (overwrites an existing AVD) |
+| field | type | required | default | note |
+| -------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ------------------------------- | ------------------------------------------------------------------------------------------------ |
+| apiLevel | number | false | - | API level of the platform system image - e.g. 23 for Android Marshmallow, 29 for Android 10. |
+| target | 'default'
'google_apis'
'playstore'
'android-wear'
'android-wear-cn'
'android-tv'
'google-tv'
'aosp_atd '
'google_atd' | false | 'default' | Target of the system image . |
+| arch | 'x86_64'
'x86'
'arm64-v8a'
'armeabi-v7a' | false | Current system CPU architecture | CPU architecture of the system image |
+| package | string | false | - | Package path of the system image for this AVD (e.g. 'system-images;android-19;google_apis;x86'). |
+| name | string | false | - | Name of the new AVD. |
+| force | boolean | false | - | Forces creation (overwrites an existing AVD) |
- If you pass a `package`, the parameters `apiLevel`, `target`, and `arch` will be ignored. If you don't pass a `package`, the `apiLevel` parameter is required.
@@ -361,7 +361,7 @@ android
.then((res) => {
res.forEach((item) => {
console.log(
- `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, vendor: ${item.vendor}, arch: ${item.arch}`
+ `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, target: ${item.target}, arch: ${item.arch}`
);
});
})
@@ -380,7 +380,7 @@ android
.then((res) => {
res.forEach((item) => {
console.log(
- `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, vendor: ${item.vendor}, arch: ${item.arch}`
+ `name: ${item.name}, type: ${item.type}, sdk: ${item.sdk}, target: ${item.target}, arch: ${item.arch}`
);
});
})
diff --git a/package.json b/package.json
index bd2682a..e0eede8 100644
--- a/package.json
+++ b/package.json
@@ -1,21 +1,16 @@
{
"name": "@wtto00/android-tools",
- "version": "1.0.1",
+ "version": "1.0.2",
"description": "Node module for managing and controlling the Android Devices.",
"type": "module",
- "main": "dist/lib/index.js",
- "module": "dist/esm/index.js",
+ "main": "dist/lib/index.cjs",
+ "module": "dist/esm/index.mjs",
"types": "dist/index.d.ts",
"exports": {
".": {
- "import": "./dist/esm/index.js",
- "require": "./dist/lib/index.js",
+ "import": "./dist/esm/index.mjs",
+ "require": "./dist/lib/index.cjs",
"types": "./dist/index.d.ts"
- },
- "./util": {
- "import": "./dist/esm/util.js",
- "require": "./dist/lib/util.js",
- "types": "./dist/util.d.ts"
}
},
"files": [
@@ -53,10 +48,12 @@
"@rollup/plugin-commonjs": "^25.0.4",
"@rollup/plugin-node-resolve": "^15.2.1",
"@rollup/plugin-typescript": "^11.1.3",
+ "@types/debug": "^4.1.9",
"@types/jest": "^29.5.4",
"@types/node": "^20.6.1",
"@typescript-eslint/eslint-plugin": "^6.7.0",
"@typescript-eslint/parser": "^6.7.0",
+ "debug": "^4.3.4",
"eslint": "^8.49.0",
"husky": "^8.0.3",
"jest": "^29.7.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 04e9f12..d9aeb81 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,6 +14,9 @@ devDependencies:
'@rollup/plugin-typescript':
specifier: ^11.1.3
version: 11.1.3(rollup@3.29.1)(tslib@2.6.2)(typescript@5.2.2)
+ '@types/debug':
+ specifier: ^4.1.9
+ version: 4.1.9
'@types/jest':
specifier: ^29.5.4
version: 29.5.4
@@ -26,6 +29,9 @@ devDependencies:
'@typescript-eslint/parser':
specifier: ^6.7.0
version: 6.7.0(eslint@8.49.0)(typescript@5.2.2)
+ debug:
+ specifier: ^4.3.4
+ version: 4.3.4
eslint:
specifier: ^8.49.0
version: 8.49.0
@@ -975,6 +981,13 @@ packages:
'@babel/types': 7.22.19
dev: true
+ /@types/debug@4.1.9:
+ resolution:
+ { integrity: sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow== }
+ dependencies:
+ '@types/ms': 0.7.31
+ dev: true
+
/@types/estree@1.0.1:
resolution:
{ integrity: sha512-LG4opVs2ANWZ1TJoKc937iMmNstM/d0ae1vNbnBvBhqCSezgVUOzcLCqbI5elV8Vy6WKwKjaqR+zO9VKirBBCA== }
@@ -1019,6 +1032,11 @@ packages:
{ integrity: sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ== }
dev: true
+ /@types/ms@0.7.31:
+ resolution:
+ { integrity: sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA== }
+ dev: true
+
/@types/node@20.6.1:
resolution:
{ integrity: sha512-4LcJvuXQlv4lTHnxwyHQZ3uR9Zw2j7m1C9DfuwoTFQQP4Pmu04O6IfLYgMmHoOCt0nosItLLZAH+sOrRE0Bo8g== }
diff --git a/rollup.config.mjs b/rollup.config.mjs
index 5201c8f..4ac2257 100644
--- a/rollup.config.mjs
+++ b/rollup.config.mjs
@@ -6,10 +6,10 @@ import commonjs from '@rollup/plugin-commonjs';
export default defineConfig([
{
- input: ['src/index.ts', 'src/util.ts'],
+ input: 'src/index.ts',
output: [
- { dir: 'dist/esm', format: 'esm' },
- { dir: 'dist/lib', format: 'cjs' }
+ { file: 'dist/esm/index.mjs', format: 'esm' },
+ { file: 'dist/lib/index.cjs', format: 'cjs' }
],
plugins: [typescript(), resolve(), commonjs()]
},
@@ -17,10 +17,5 @@ export default defineConfig([
input: 'src/index.ts',
output: [{ file: 'dist/index.d.ts', format: 'esm' }],
plugins: [resolve(), commonjs(), dts({ respectExternal: true })]
- },
- {
- input: 'src/util.ts',
- output: [{ file: 'dist/util.d.ts', format: 'esm' }],
- plugins: [resolve(), commonjs(), dts({ respectExternal: true })]
}
]);
diff --git a/spec/afterStart.test.ts b/spec/afterStart.test.ts
index d0a5b87..4d697f1 100644
--- a/spec/afterStart.test.ts
+++ b/spec/afterStart.test.ts
@@ -1,6 +1,5 @@
import path from 'path';
import Android from '../src/index.js';
-import { EmulatorGpu } from '../src/emulator.js';
const android = new Android();
@@ -10,7 +9,7 @@ beforeAll(async () => {
let avdName = '';
if (avds.length === 0) {
avdName = `TestCreate_${Math.random().toString().substring(2)}`;
- const images = (await android.listImages()).filter((item) => item.vendor === 'default' && item.arch === 'x86_64');
+ const images = (await android.listImages()).filter((item) => item.target === 'default' && item.arch === 'x86_64');
if (images.length === 0) return;
await android.createAVD({ name: avdName, apiLevel: 31, force: false });
} else {
@@ -18,13 +17,12 @@ beforeAll(async () => {
}
const res = await android.start({
avd: avdName,
- verbose: true,
noaudio: true,
noBootAnim: true,
noSnapshot: true,
noSnapshotSave: true,
noWindow: true,
- gpu: EmulatorGpu.SWIFTSHADER_INDIRECT
+ gpu: 'swiftshader_indirect'
});
emulatorId = res.id;
if (emulatorId) {
diff --git a/spec/emulatorAVD.test.ts b/spec/emulatorAVD.test.ts
index 15cead6..2253424 100644
--- a/spec/emulatorAVD.test.ts
+++ b/spec/emulatorAVD.test.ts
@@ -5,7 +5,7 @@ describe('emulator AVD', () => {
test('create AVD', async () => {
const avdName = `TestCreate_${Math.random()}`;
- const images = (await android.listInstalledImages()).filter((item) => item.vendor === 'default');
+ const images = (await android.listInstalledImages()).filter((item) => item.target === 'default');
if (images.length === 0) return;
await android.createAVD({ name: avdName, package: images[images.length - 1].name, force: false });
const res = await android.hasAVD(avdName);
@@ -16,7 +16,7 @@ describe('emulator AVD', () => {
test('create an existing AVD', async () => {
const avds = await android.listAVDs();
if (avds.length === 0) return;
- const images = (await android.listInstalledImages()).filter((item) => item.vendor === 'default');
+ const images = (await android.listInstalledImages()).filter((item) => item.target === 'default');
if (images.length === 0) return;
await expect(async () => {
await android.createAVD({ name: avds[0].Name, package: images[images.length - 1].name });
@@ -26,7 +26,7 @@ describe('emulator AVD', () => {
test('force create an existing AVD', async () => {
const avds = await android.listAVDs();
if (avds.length === 0) return;
- const images = (await android.listInstalledImages()).filter((item) => item.vendor === 'default');
+ const images = (await android.listInstalledImages()).filter((item) => item.target === 'default');
if (images.length === 0) return;
await android.createAVD({ name: avds[0].Name, package: images[images.length - 1].name, force: true });
const res = await android.hasAVD(avds[0].Name);
diff --git a/spec/start.test.ts b/spec/start.test.ts
index 9211f5f..98ed745 100644
--- a/spec/start.test.ts
+++ b/spec/start.test.ts
@@ -6,7 +6,6 @@ describe('start', () => {
test('start a non-existent AVD.', async () => {
await expect(async () => {
await android.start({
- verbose: true,
avd: `${Math.random()}`,
noaudio: true,
noBootAnim: true,
diff --git a/src/emulator.ts b/src/emulator.ts
index 0bbe84d..07c1e8a 100644
--- a/src/emulator.ts
+++ b/src/emulator.ts
@@ -45,24 +45,18 @@ export interface EmulatorOptions {
noAccel?: boolean;
}
-export enum SystemImageTarget {
- DEFAULT = 'default',
- GOOGLE_APIS = 'google_apis',
- PLAYSTORE = 'playstore',
- ANDROID_WEAR = 'android-wear',
- android_wear_cn = 'android-wear-cn',
- android_tv = 'android-tv',
- google_tv = 'google-tv',
- AOSP_ATD = 'aosp_atd',
- GOOGLE_ATD = 'google_atd'
-}
+export type SystemImageTarget =
+ | 'default'
+ | 'google_apis'
+ | 'playstore'
+ | 'android-wear'
+ | 'android-wear-cn'
+ | 'android-tv'
+ | 'google-tv'
+ | 'aosp_atd'
+ | 'google_atd';
-export enum Arch {
- X86 = 'x86',
- X86_64 = 'x86_64',
- ARM64_V8A = 'arm64-v8a',
- ARMEABI_V7A = 'armeabi-v7a'
-}
+export type Arch = 'x86' | 'x86_64' | 'arm64-v8a' | 'armeabi-v7a';
/**
* Use -camera-back to control emulation of a camera facing back.
@@ -92,42 +86,14 @@ export type CameraFront = 'emulated' | 'none' | (string & {});
* The 'auto' mode is the default. In this mode, the hw.gpu.enabled setting
* in the AVD's hardware-qemu.ini file will determine whether GPU emulation
* is enabled.
+ * @value **auto** Auto-select the renderer.
+ * @value **auto-no-window** Auto-select the renderer when running headless. This will use the same gpu selection mechanism as running without the "-no-window" flag and the "-gpu auto" option. See auto for details on the behavior.
+ * @value **host** Use the host system's OpenGL driver.
+ * @value **swiftshader_indirect** Use SwiftShader software renderer on the host, which can be beneficial if you are experiencing issues with your GPU drivers or need to run on systems without GPUs.
+ * @value **angle_indirect** Use ANGLE, an OpenGL ES to D3D11 renderer (Windows 7 SP1 + Platform update, Windows 8.1+, or Windows 10 only).
+ * @value **guest** Use guest-side software rendering. For advanced users only. Warning: slow! In API 28 and later, guest rendering is not supported, and will fall back automatically to swiftshader_indirect.
*/
-export enum EmulatorGpu {
- /** Auto-select the renderer. */
- AUTO = 'auto',
- /**
- * Auto-select the renderer when
- * running headless. This will use the same
- * gpu selection mechanism as running without
- * the "-no-window" flag and the "-gpu auto"
- * option. See auto for details on the behavior.
- */
- AUTO_NO_WINDOW = 'auto-no-window',
- /** Use the host system's OpenGL driver. */
- HOST = 'host',
- /**
- * Use SwiftShader software renderer on the
- * host, which can be beneficial if you are
- * experiencing issues with your GPU drivers
- * or need to run on systems without GPUs.
- */
- SWIFTSHADER_INDIRECT = 'swiftshader_indirect',
- /**
- * Use ANGLE, an OpenGL ES to D3D11 renderer
- * (Windows 7 SP1 + Platform update,
- * Windows 8.1+, or Windows 10 only).
- */
- ANGLE_INDIRECT = 'angle_indirect',
- /**
- * Use guest-side software rendering. For
- * advanced users only. Warning: slow!
- * In API 28 and later, guest rendering
- * is not supported, and will fall back
- * automatically to swiftshader_indirect.
- */
- GUEST = 'guest'
-}
+export type EmulatorGpu = 'auto' | 'auto-no-window' | 'host' | 'swiftshader_indirect' | 'angle_indirect' | 'guest';
/**
* Android Emulator usage: emulator [options] [-qemu args]
diff --git a/src/index.ts b/src/index.ts
index f1ecc8f..6f7d052 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,6 +1,7 @@
import path from 'path';
import {
filterGroupImages,
+ getLocalArch,
isExecExpectedResult,
processKeyValueGroups,
retry,
@@ -8,6 +9,12 @@ import {
spawnWaitFor
} from './util.js';
import { Arch, EmulatorOptions, SystemImageTarget } from './emulator.js';
+import Debug from 'debug';
+
+export * from './util.js';
+export * from './emulator.js';
+
+const log = Debug('android-tools');
export interface AndroidOptions {
/** The location of the `adb` executable file relative to `ANDROID_HOME` */
@@ -18,6 +25,8 @@ export interface AndroidOptions {
sdkmanager?: string;
/** The location of the `emulator` executable file relative to `ANDROID_HOME` */
emulator?: string;
+ /** Print debug output or not */
+ debug?: boolean;
}
export type AndroidExecBin = 'adbBin' | 'avdmanagerBin' | 'sdkmanagerBin' | 'emulatorBin';
@@ -76,6 +85,7 @@ class Android {
emulatorBin: string = process.env.emulatorBin ?? '';
constructor(props?: AndroidOptions) {
+ if (!props?.debug) Debug.disable();
this.setAdbBinPath(props?.adb);
this.setAvdmanagerBinPath(props?.avdmanager);
this.setSdkmanagerBinPath(props?.sdkmanager);
@@ -87,6 +97,7 @@ class Android {
* @param execPath The location of the `adb` executable file relative to `ANDROID_HOME`
*/
setAdbBinPath(execPath?: string) {
+ log('setAdbBinPath: %s', JSON.stringify({ execPath }));
if (process.env.ANDROID_HOME) {
if (execPath) {
this.adbBin = path.resolve(process.env.ANDROID_HOME, execPath);
@@ -107,6 +118,7 @@ class Android {
* @param execPath The location of the `avdmanager` executable file relative to `ANDROID_HOME`
*/
setAvdmanagerBinPath(execPath?: string) {
+ log('setAvdmanagerBinPath: %s', JSON.stringify({ execPath }));
if (process.env.ANDROID_HOME) {
if (execPath) {
this.avdmanagerBin = path.resolve(process.env.ANDROID_HOME, execPath);
@@ -128,6 +140,7 @@ class Android {
* @param execPath The location of the `sdkmanager` executable file relative to `ANDROID_HOME`
*/
setSdkmanagerBinPath(execPath?: string) {
+ log('setSdkmanagerBinPath: %s', JSON.stringify({ execPath }));
if (process.env.ANDROID_HOME) {
if (execPath) {
this.sdkmanagerBin = path.resolve(process.env.ANDROID_HOME, execPath);
@@ -148,6 +161,7 @@ class Android {
* @param execPath The location of the `emulator` executable file relative to `ANDROID_HOME`
*/
setEmulatorBinPath(execPath?: string) {
+ log('setEmulatorBinPath: %s', JSON.stringify({ execPath }));
if (process.env.ANDROID_HOME) {
if (execPath) {
this.emulatorBin = path.resolve(process.env.ANDROID_HOME, execPath);
@@ -168,8 +182,10 @@ class Android {
* Returns a promise that is resolved with an object that has the following properties.
* @param options
*/
- async start(options: Required> & Omit) {
+ async start(options: Required> & Omit) {
+ log('start: %s', JSON.stringify(options));
const opts = [];
+ options.verbose = true;
for (const key in options) {
const val = options[key as keyof EmulatorOptions];
const cliKey = '-' + key.replace(/[A-Z]/g, (matched) => `-${matched.toLocaleLowerCase()}`);
@@ -194,6 +210,7 @@ class Android {
* @param emulatorId id of emulator
*/
async waitForDevice(emulatorId: string) {
+ log('waitForDevice: %s', JSON.stringify({ emulatorId }));
await this.adb(emulatorId, 'wait-for-device', 300000);
}
@@ -202,6 +219,7 @@ class Android {
* @param emulatorId id of emulator
*/
async ensureReady(emulatorId: string) {
+ log('ensureReady: %s', JSON.stringify({ emulatorId }));
await this.waitForDevice(emulatorId);
await retry(async () => {
const proc = await this.adb(emulatorId, 'shell getprop sys.boot_completed');
@@ -215,14 +233,13 @@ class Android {
* @param name name of AVD
*/
async createAVD(options: CreateAVDOptions) {
+ log('createAVD: %s', JSON.stringify(options));
if (!options.name) return Promise.reject(Error('Missing name parameter.'));
if (!options.package && !options.apiLevel)
return Promise.reject(Error('Either the parameter "package" or "apiLevel" must be present.'));
const systemImage =
options.package ||
- `system-images;android-${options.apiLevel};${options.target || SystemImageTarget.DEFAULT};${
- options.arch || Arch.X86_64
- }`;
+ `system-images;android-${options.apiLevel};${options.target || 'default'};${options.arch || getLocalArch()}`;
// Downloading the SDK takes a lot of time, so it's best to download it in advance.
await this.sdkmanager(`--install ${systemImage}`, 600000);
let cmdParams = `-n ${options.name} -k ${systemImage}`;
@@ -235,6 +252,7 @@ class Android {
* @param avdName Name of AVD.
*/
async hasAVD(avdName: string) {
+ log('hasAVD: %s', JSON.stringify({ avdName }));
const avds = await this.listAVDs();
return (
avds.filter((avd) => {
@@ -248,6 +266,7 @@ class Android {
* @param emulatorId Id of emulator.
*/
async stop(emulatorId: string) {
+ log('stop: %s', JSON.stringify({ emulatorId }));
return await this.adb(emulatorId, 'emu kill');
}
@@ -255,6 +274,7 @@ class Android {
* Wait until the device is stopped.
*/
async waitForStop(emulatorId: string) {
+ log('waitForStop: %s', JSON.stringify({ emulatorId }));
await retry(async () => {
await this.stop(emulatorId);
const devices = await this.devices();
@@ -268,6 +288,7 @@ class Android {
* @param packageName name of package
*/
async isInstalled(emulatorId: string, packageName: string) {
+ log('isInstalled: %s, %s', JSON.stringify({ emulatorId, packageName }));
const packages = await this.listPackages(emulatorId);
const isInstalled = packages.includes(packageName);
return isInstalled;
@@ -279,6 +300,7 @@ class Android {
* @param apkPath path of apk file
*/
async install(emulatorId: string, apkPath: string) {
+ log('install: %s, %s', JSON.stringify({ emulatorId, apkPath }));
const process = await this.adb(emulatorId, `install ${apkPath}`);
if (process.output.match(/Success/)) return;
throw new Error('Could not parse output of adb command');
@@ -290,6 +312,7 @@ class Android {
* @param key https://developer.android.com/reference/android/view/KeyEvent
*/
async inputKeyEvent(emulatorId: string, key: number) {
+ log('inputKeyEvent: %s, %d', JSON.stringify({ emulatorId, key }));
return await this.adb(emulatorId, 'shell input keyevent ' + key);
}
@@ -297,6 +320,7 @@ class Android {
* List connected devices
*/
async devices() {
+ log('devices: ');
const proc = await this.adb('devices');
const lines = proc.output.split('\n');
lines.shift();
@@ -317,6 +341,7 @@ class Android {
* @param emulatorId id of emulator
*/
async listPackages(emulatorId: string) {
+ log('listPackages: %s', JSON.stringify({ emulatorId }));
const proc = await this.adb(emulatorId, 'shell pm list packages');
const lines = proc.output.split('\n');
return lines
@@ -335,6 +360,7 @@ class Android {
* List the available device list for creating emulators in the current system.
*/
async listDevices() {
+ log('listDevices: ');
const proc = await this.avdmanager('list device');
return processKeyValueGroups(proc.output);
}
@@ -343,6 +369,7 @@ class Android {
* List all AVDs created on this machine.
*/
async listAVDs() {
+ log('listAVDs: ');
const proc = await this.avdmanager('list avd');
return processKeyValueGroups(proc.output);
}
@@ -351,6 +378,7 @@ class Android {
* List available Android targets.
*/
async listTargets() {
+ log('listTargets: ');
const proc = await this.avdmanager('list target');
return processKeyValueGroups(proc.output);
}
@@ -359,6 +387,7 @@ class Android {
* List available Android images on this machine.
*/
async listImages() {
+ log('listImages: ');
const proc = await this.sdkmanager('--list');
return filterGroupImages(proc.output);
}
@@ -367,6 +396,7 @@ class Android {
* List installed Android images on this machine.
*/
async listInstalledImages() {
+ log('listInstalledImages: ');
const proc = await this.sdkmanager('--list_installed');
return filterGroupImages(proc.output);
}
@@ -378,6 +408,7 @@ class Android {
* @param timeout Execution timeout
*/
async adb(emulatorId: string, cmd?: string, timeout?: number) {
+ log('adb: %s', JSON.stringify({ emulatorId, cmd, timeout }));
if (cmd) return await spawnExec(`${this.adbBin} -s ${emulatorId} ${cmd}`, timeout);
return await spawnExec(`${this.adbBin} ${emulatorId}`, timeout);
}
@@ -388,6 +419,7 @@ class Android {
* @param timeout Execution timeout
*/
async avdmanager(cmd: string, timeout?: number) {
+ log('avdmanager: %s', JSON.stringify({ cmd, timeout }));
return await spawnExec(`${this.avdmanagerBin} ${cmd}`, timeout);
}
@@ -397,6 +429,7 @@ class Android {
* @param timeout Execution timeout
*/
async sdkmanager(cmd: string, timeout?: number) {
+ log('sdkmanager: %s', JSON.stringify({ cmd, timeout }));
return await spawnExec(`${this.sdkmanagerBin} ${cmd}`, timeout);
}
@@ -406,6 +439,7 @@ class Android {
* @param timeout Execution timeout
*/
async emulator(cmd: string, timeout?: number) {
+ log('emulator: %s', JSON.stringify({ cmd, timeout }));
return await spawnExec(`${this.emulatorBin} ${cmd}`, timeout);
}
}
diff --git a/src/util.ts b/src/util.ts
index 80ac779..881f320 100644
--- a/src/util.ts
+++ b/src/util.ts
@@ -1,6 +1,7 @@
import { spawn, spawnSync } from 'node:child_process';
import type { ChildProcessByStdio, ChildProcessWithoutNullStreams } from 'node:child_process';
import type internal from 'node:stream';
+import { Arch, SystemImageTarget } from './emulator.js';
export function winBatAdapt(cmd: string) {
if (process.platform === 'win32' && (cmd.endsWith('sdkmanager') || cmd.endsWith('avdmanager'))) {
@@ -166,18 +167,29 @@ export function processKeyValueGroups(str: string) {
export function filterGroupImages(output: string) {
const lines = output.split('\n');
+ const localArch = getLocalArch();
return lines
.map((line) => line.split('|')[0].trim())
.filter((line) => line.startsWith('system-images;'))
.map((name) => {
- const [type, sdk, vendor, arch] = name.split(';');
- return { name, type, sdk, vendor, arch };
+ const [type, sdk, target, arch] = name.split(';');
+ return { name, type, sdk, target, arch } as {
+ name: string;
+ type: string;
+ sdk: string;
+ target: SystemImageTarget;
+ arch: Arch;
+ };
})
.filter((item) => {
- if (process.arch === 'arm') return item.arch === 'armeabi-v7a';
- if (process.arch === 'arm64') return item.arch === 'arm64-v8a';
- if (process.arch === 'ia32') return item.arch === 'x86';
- if (process.arch === 'x64') return item.arch === 'x86_64';
- return false;
+ return item.arch === localArch;
});
}
+
+export function getLocalArch() {
+ if (process.arch === 'arm') return 'armeabi-v7a';
+ if (process.arch === 'arm64') return 'arm64-v8a';
+ if (process.arch === 'ia32') return 'x86';
+ if (process.arch === 'x64') return 'x86_64';
+ return '';
+}