mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-05 10:49:24 +02:00
feat(bin): add wrapper for am start
This commit is contained in:
parent
9c2a069658
commit
e7827b011a
6 changed files with 214 additions and 39 deletions
69
libraries/android-bin/src/am.ts
Normal file
69
libraries/android-bin/src/am.ts
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import type { Adb } from "@yume-chan/adb";
|
||||||
|
import { AdbCommandBase } from "@yume-chan/adb";
|
||||||
|
import { ConcatStringStream, DecodeUtf8Stream } from "@yume-chan/stream-extra";
|
||||||
|
|
||||||
|
import { Cmd } from "./cmd.js";
|
||||||
|
import type { IntentBuilder } from "./intent.js";
|
||||||
|
import type { SingleUser } from "./utils.js";
|
||||||
|
import { buildArguments } from "./utils.js";
|
||||||
|
|
||||||
|
export interface ActivityManagerStartActivityOptions {
|
||||||
|
displayId?: number;
|
||||||
|
windowingMode?: number;
|
||||||
|
forceStop?: boolean;
|
||||||
|
user?: SingleUser;
|
||||||
|
intent: IntentBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
const START_ACTIVITY_OPTIONS_MAP: Partial<
|
||||||
|
Record<keyof ActivityManagerStartActivityOptions, string>
|
||||||
|
> = {
|
||||||
|
displayId: "--display",
|
||||||
|
windowingMode: "--windowingMode",
|
||||||
|
forceStop: "-S",
|
||||||
|
user: "--user",
|
||||||
|
};
|
||||||
|
|
||||||
|
export class ActivityManager extends AdbCommandBase {
|
||||||
|
#cmd: Cmd;
|
||||||
|
|
||||||
|
constructor(adb: Adb) {
|
||||||
|
super(adb);
|
||||||
|
this.#cmd = new Cmd(adb);
|
||||||
|
}
|
||||||
|
|
||||||
|
async #cmdOrSubprocess(args: string[]) {
|
||||||
|
if (this.#cmd.supportsCmd) {
|
||||||
|
args.shift();
|
||||||
|
return await this.#cmd.spawn(false, "activity", ...args);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.adb.subprocess.spawn(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
async startActivity(options: ActivityManagerStartActivityOptions) {
|
||||||
|
let args = buildArguments(
|
||||||
|
["am", "start-activity", "-W"],
|
||||||
|
options,
|
||||||
|
START_ACTIVITY_OPTIONS_MAP,
|
||||||
|
);
|
||||||
|
|
||||||
|
args = args.concat(options.intent.build());
|
||||||
|
|
||||||
|
const process = await this.#cmdOrSubprocess(args);
|
||||||
|
|
||||||
|
const output = await process.stdout
|
||||||
|
.pipeThrough(new DecodeUtf8Stream())
|
||||||
|
.pipeThrough(new ConcatStringStream())
|
||||||
|
.then((output) => output.trim());
|
||||||
|
|
||||||
|
for (const line of output) {
|
||||||
|
if (line.startsWith("Error:")) {
|
||||||
|
throw new Error(line.substring("Error:".length).trim());
|
||||||
|
}
|
||||||
|
if (line === "Complete") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,10 +1,12 @@
|
||||||
// cspell: ignore logcat
|
// cspell: ignore logcat
|
||||||
|
|
||||||
|
export * from "./am.js";
|
||||||
export * from "./bu.js";
|
export * from "./bu.js";
|
||||||
export * from "./bug-report.js";
|
export * from "./bug-report.js";
|
||||||
export * from "./cmd.js";
|
export * from "./cmd.js";
|
||||||
export * from "./demo-mode.js";
|
export * from "./demo-mode.js";
|
||||||
export * from "./dumpsys.js";
|
export * from "./dumpsys.js";
|
||||||
|
export * from "./intent.js";
|
||||||
export * from "./logcat.js";
|
export * from "./logcat.js";
|
||||||
export * from "./overlay-display.js";
|
export * from "./overlay-display.js";
|
||||||
export * from "./pm.js";
|
export * from "./pm.js";
|
||||||
|
|
63
libraries/android-bin/src/intent.ts
Normal file
63
libraries/android-bin/src/intent.ts
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
export class IntentBuilder {
|
||||||
|
#action: string | undefined;
|
||||||
|
#categories: string[] = [];
|
||||||
|
#packageName: string | undefined;
|
||||||
|
#component: string | undefined;
|
||||||
|
#data: string | undefined;
|
||||||
|
#type: string | undefined;
|
||||||
|
|
||||||
|
setAction(action: string): this {
|
||||||
|
this.#action = action;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
addCategory(category: string): this {
|
||||||
|
this.#categories.push(category);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setPackage(packageName: string): this {
|
||||||
|
this.#packageName = packageName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setComponent(component: string): this {
|
||||||
|
this.#component = component;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
setData(data: string): this {
|
||||||
|
this.#data = data;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
build(): string[] {
|
||||||
|
const result: string[] = [];
|
||||||
|
|
||||||
|
if (this.#action) {
|
||||||
|
result.push("-a", this.#action);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const category of this.#categories) {
|
||||||
|
result.push("-c", category);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#packageName) {
|
||||||
|
result.push("-p", this.#packageName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#component) {
|
||||||
|
result.push("-n", this.#component);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#data) {
|
||||||
|
result.push("-d", this.#data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.#type) {
|
||||||
|
result.push("-t", this.#type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,6 +17,9 @@ import {
|
||||||
} from "@yume-chan/stream-extra";
|
} from "@yume-chan/stream-extra";
|
||||||
|
|
||||||
import { Cmd } from "./cmd.js";
|
import { Cmd } from "./cmd.js";
|
||||||
|
import type { IntentBuilder } from "./intent.js";
|
||||||
|
import type { SingleUserOrAll } from "./utils.js";
|
||||||
|
import { buildArguments } from "./utils.js";
|
||||||
|
|
||||||
export enum PackageManagerInstallLocation {
|
export enum PackageManagerInstallLocation {
|
||||||
Auto,
|
Auto,
|
||||||
|
@ -101,7 +104,7 @@ export interface PackageManagerInstallOptions {
|
||||||
/**
|
/**
|
||||||
* `--user`
|
* `--user`
|
||||||
*/
|
*/
|
||||||
userId: number;
|
user: SingleUserOrAll;
|
||||||
/**
|
/**
|
||||||
* `--install-location`
|
* `--install-location`
|
||||||
*/
|
*/
|
||||||
|
@ -168,7 +171,7 @@ export const PACKAGE_MANAGER_INSTALL_OPTIONS_MAP: Record<
|
||||||
instantApp: "--instant",
|
instantApp: "--instant",
|
||||||
full: "--full",
|
full: "--full",
|
||||||
preload: "--preload",
|
preload: "--preload",
|
||||||
userId: "--user",
|
user: "--user",
|
||||||
installLocation: "--install-location",
|
installLocation: "--install-location",
|
||||||
installReason: "--install-reason",
|
installReason: "--install-reason",
|
||||||
forceUuid: "--force-uuid",
|
forceUuid: "--force-uuid",
|
||||||
|
@ -192,7 +195,7 @@ export interface PackageManagerListPackagesOptions {
|
||||||
listThirdParty: boolean;
|
listThirdParty: boolean;
|
||||||
showVersionCode: boolean;
|
showVersionCode: boolean;
|
||||||
listApexOnly: boolean;
|
listApexOnly: boolean;
|
||||||
user: "all" | "current" | number;
|
user: SingleUserOrAll;
|
||||||
uid: number;
|
uid: number;
|
||||||
filter: string;
|
filter: string;
|
||||||
}
|
}
|
||||||
|
@ -225,7 +228,7 @@ export interface PackageManagerListPackagesResult {
|
||||||
|
|
||||||
export interface PackageManagerUninstallOptions {
|
export interface PackageManagerUninstallOptions {
|
||||||
keepData: boolean;
|
keepData: boolean;
|
||||||
user: "all" | "current" | number;
|
user: SingleUserOrAll;
|
||||||
versionCode: number;
|
versionCode: number;
|
||||||
splitNames: string[];
|
splitNames: string[];
|
||||||
}
|
}
|
||||||
|
@ -240,6 +243,17 @@ const PACKAGE_MANAGER_UNINSTALL_OPTIONS_MAP: Record<
|
||||||
splitNames: "",
|
splitNames: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface PackageManagerResolveActivityOptions {
|
||||||
|
user?: SingleUserOrAll;
|
||||||
|
intent: IntentBuilder;
|
||||||
|
}
|
||||||
|
|
||||||
|
const PACKAGE_MANAGER_RESOLVE_ACTIVITY_OPTIONS_MAP: Partial<
|
||||||
|
Record<keyof PackageManagerResolveActivityOptions, string>
|
||||||
|
> = {
|
||||||
|
user: "--user",
|
||||||
|
};
|
||||||
|
|
||||||
export class PackageManager extends AdbCommandBase {
|
export class PackageManager extends AdbCommandBase {
|
||||||
#cmd: Cmd;
|
#cmd: Cmd;
|
||||||
|
|
||||||
|
@ -248,38 +262,11 @@ export class PackageManager extends AdbCommandBase {
|
||||||
this.#cmd = new Cmd(adb);
|
this.#cmd = new Cmd(adb);
|
||||||
}
|
}
|
||||||
|
|
||||||
#buildArguments<T>(
|
|
||||||
commands: string[],
|
|
||||||
options: Partial<T> | undefined,
|
|
||||||
map: Record<keyof T, string>,
|
|
||||||
): string[] {
|
|
||||||
const args = ["pm", ...commands];
|
|
||||||
if (options) {
|
|
||||||
for (const [key, value] of Object.entries(options)) {
|
|
||||||
if (value) {
|
|
||||||
const option = map[key as keyof T];
|
|
||||||
if (option) {
|
|
||||||
args.push(option);
|
|
||||||
switch (typeof value) {
|
|
||||||
case "number":
|
|
||||||
args.push(value.toString());
|
|
||||||
break;
|
|
||||||
case "string":
|
|
||||||
args.push(value);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
|
|
||||||
#buildInstallArguments(
|
#buildInstallArguments(
|
||||||
options: Partial<PackageManagerInstallOptions> | undefined,
|
options: Partial<PackageManagerInstallOptions> | undefined,
|
||||||
): string[] {
|
): string[] {
|
||||||
return this.#buildArguments(
|
return buildArguments(
|
||||||
["install"],
|
["pm", "install"],
|
||||||
options,
|
options,
|
||||||
PACKAGE_MANAGER_INSTALL_OPTIONS_MAP,
|
PACKAGE_MANAGER_INSTALL_OPTIONS_MAP,
|
||||||
);
|
);
|
||||||
|
@ -313,7 +300,7 @@ export class PackageManager extends AdbCommandBase {
|
||||||
await sync.dispose();
|
await sync.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Starting from Android 7, `pm` is a only wrapper for `cmd package`,
|
// Starting from Android 7, `pm` is only a wrapper for `cmd package`,
|
||||||
// and `cmd package` launches faster than `pm`.
|
// and `cmd package` launches faster than `pm`.
|
||||||
// But `cmd package` can't read `/data/local/tmp` folder due to SELinux policy,
|
// But `cmd package` can't read `/data/local/tmp` folder due to SELinux policy,
|
||||||
// so installing a file must use `pm`.
|
// so installing a file must use `pm`.
|
||||||
|
@ -434,8 +421,8 @@ export class PackageManager extends AdbCommandBase {
|
||||||
async *listPackages(
|
async *listPackages(
|
||||||
options?: Partial<PackageManagerListPackagesOptions>,
|
options?: Partial<PackageManagerListPackagesOptions>,
|
||||||
): AsyncGenerator<PackageManagerListPackagesResult, void, void> {
|
): AsyncGenerator<PackageManagerListPackagesResult, void, void> {
|
||||||
const args = this.#buildArguments(
|
const args = buildArguments(
|
||||||
["list", "packages"],
|
["pm", "list", "packages"],
|
||||||
options,
|
options,
|
||||||
PACKAGE_MANAGER_LIST_PACKAGES_OPTIONS_MAP,
|
PACKAGE_MANAGER_LIST_PACKAGES_OPTIONS_MAP,
|
||||||
);
|
);
|
||||||
|
@ -461,8 +448,8 @@ export class PackageManager extends AdbCommandBase {
|
||||||
packageName: string,
|
packageName: string,
|
||||||
options?: Partial<PackageManagerUninstallOptions>,
|
options?: Partial<PackageManagerUninstallOptions>,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
const args = this.#buildArguments(
|
const args = buildArguments(
|
||||||
["uninstall"],
|
["pm", "uninstall"],
|
||||||
options,
|
options,
|
||||||
PACKAGE_MANAGER_UNINSTALL_OPTIONS_MAP,
|
PACKAGE_MANAGER_UNINSTALL_OPTIONS_MAP,
|
||||||
);
|
);
|
||||||
|
@ -480,4 +467,28 @@ export class PackageManager extends AdbCommandBase {
|
||||||
throw new Error(output);
|
throw new Error(output);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async resolveActivity(
|
||||||
|
options: PackageManagerResolveActivityOptions,
|
||||||
|
): Promise<string | undefined> {
|
||||||
|
let args = buildArguments(
|
||||||
|
["pm", "resolve-activity", "--components"],
|
||||||
|
options,
|
||||||
|
PACKAGE_MANAGER_RESOLVE_ACTIVITY_OPTIONS_MAP,
|
||||||
|
);
|
||||||
|
|
||||||
|
args = args.concat(options.intent.build());
|
||||||
|
|
||||||
|
const process = await this.#cmdOrSubprocess(args);
|
||||||
|
const output = await process.stdout
|
||||||
|
.pipeThrough(new DecodeUtf8Stream())
|
||||||
|
.pipeThrough(new ConcatStringStream())
|
||||||
|
.then((output) => output.trim());
|
||||||
|
|
||||||
|
if (output === "No activity found") {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ import type { Adb, AdbSubprocessWaitResult } from "@yume-chan/adb";
|
||||||
import { AdbCommandBase } from "@yume-chan/adb";
|
import { AdbCommandBase } from "@yume-chan/adb";
|
||||||
|
|
||||||
import { Cmd } from "./cmd.js";
|
import { Cmd } from "./cmd.js";
|
||||||
|
import type { SingleUser } from "./utils.js";
|
||||||
|
|
||||||
export type SettingsNamespace = "system" | "secure" | "global";
|
export type SettingsNamespace = "system" | "secure" | "global";
|
||||||
|
|
||||||
|
@ -12,7 +13,7 @@ export enum SettingsResetMode {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SettingsOptions {
|
export interface SettingsOptions {
|
||||||
user?: number | "current";
|
user?: SingleUser;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface SettingsPutOptions extends SettingsOptions {
|
export interface SettingsPutOptions extends SettingsOptions {
|
||||||
|
|
29
libraries/android-bin/src/utils.ts
Normal file
29
libraries/android-bin/src/utils.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
export function buildArguments<T>(
|
||||||
|
commands: string[],
|
||||||
|
options: Partial<T> | undefined,
|
||||||
|
map: Partial<Record<keyof T, string>>,
|
||||||
|
): string[] {
|
||||||
|
const args = commands;
|
||||||
|
if (options) {
|
||||||
|
for (const [key, value] of Object.entries(options)) {
|
||||||
|
if (value) {
|
||||||
|
const option = map[key as keyof T];
|
||||||
|
if (option) {
|
||||||
|
args.push(option);
|
||||||
|
switch (typeof value) {
|
||||||
|
case "number":
|
||||||
|
args.push(value.toString());
|
||||||
|
break;
|
||||||
|
case "string":
|
||||||
|
args.push(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SingleUser = number | "current";
|
||||||
|
export type SingleUserOrAll = SingleUser | "all";
|
Loading…
Add table
Add a link
Reference in a new issue