diff --git a/bun.lockb b/bun.lockb old mode 100644 new mode 100755 index c54980e..9a9be59 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 4f44e17..6a131f3 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@radix-ui/react-alert-dialog": "^1.1.2", "@radix-ui/react-slot": "^1.1.0", "@tauri-apps/api": "^2", + "@tauri-apps/plugin-os": "~2", "@tauri-apps/plugin-process": "~2", "@tauri-apps/plugin-shell": "~2", "class-variance-authority": "^0.7.0", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 9886ce7..94ee0d5 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -702,6 +702,7 @@ dependencies = [ "serde_json", "tauri", "tauri-build", + "tauri-plugin-os", "tauri-plugin-positioner", "tauri-plugin-process", "tauri-plugin-shell", @@ -732,6 +733,16 @@ dependencies = [ "typeid", ] +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "fdeflate" version = "0.3.6" @@ -1003,6 +1014,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "gethostname" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc3655aa6818d65bc620d6911f05aa7b6aeb596291e1e9f79e52df85583d1e30" +dependencies = [ + "rustix", + "windows-targets 0.52.6", +] + [[package]] name = "getrandom" version = "0.1.16" @@ -1718,6 +1739,12 @@ dependencies = [ "libc", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "litemap" version = "0.7.3" @@ -2175,6 +2202,17 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "os_info" +version = "3.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" +dependencies = [ + "log", + "serde", + "windows-sys 0.52.0", +] + [[package]] name = "os_pipe" version = "1.2.1" @@ -2709,6 +2747,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99e4ea3e1cdc4b559b8e5650f9c8e5998e3e5c1343b4eaf034565f32318d63c0" +dependencies = [ + "bitflags 2.6.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "ryu" version = "1.0.18" @@ -3137,6 +3188,15 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + [[package]] name = "system-deps" version = "6.2.2" @@ -3336,6 +3396,24 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tauri-plugin-os" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbc5f23a86f37687c7f4fecfdc706b279087bc44f7a46702f7307ff1551ee03a" +dependencies = [ + "gethostname", + "log", + "os_info", + "serde", + "serde_json", + "serialize-to-javascript", + "sys-locale", + "tauri", + "tauri-plugin", + "thiserror 1.0.69", +] + [[package]] name = "tauri-plugin-positioner" version = "2.0.2" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 5eaeae5..e080f00 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -18,9 +18,10 @@ crate-type = ["staticlib", "cdylib", "rlib"] tauri-build = { version = "2", features = [] } [dependencies] -tauri = { version = "2", features = [] } +tauri = { version = "2", features = ["macos-private-api"] } tauri-plugin-shell = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" tauri-plugin-process = "2" tauri-plugin-positioner = "2" +tauri-plugin-os = "2" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 4ce5d4e..0acb282 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -16,9 +16,15 @@ "name": "cmd.exe", "cmd": "cmd.exe", "args": true + }, + { + "name": "sh", + "cmd": "sh", + "args": true } ] }, - "process:default" + "process:default", + "os:default" ] } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index c4c7164..2f80615 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -13,6 +13,7 @@ fn get_android_home() -> Result { #[cfg_attr(mobile, tauri::mobile_entry_point)] pub fn run() { tauri::Builder::default() + .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_process::init()) .plugin(tauri_plugin_shell::init()) .invoke_handler(tauri::generate_handler![get_android_home]) diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 80312c5..d764c02 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -10,6 +10,7 @@ "frontendDist": "../dist" }, "app": { + "macOSPrivateApi": true, "windows": [ { "title": "emu", diff --git a/src/services/api/emulator.ts b/src/services/api/emulator.ts index fd77c63..2b6907d 100644 --- a/src/services/api/emulator.ts +++ b/src/services/api/emulator.ts @@ -1,4 +1,4 @@ -import { execute } from "@/services/execute"; +import { adb, emulator } from "@/services/execute"; import { OnlineEmulator, OnlineEmulatorState, @@ -6,12 +6,9 @@ import { } from "@/types/emulator"; export const getAllEmulators = async () => { - const output = await execute("cmd.exe", [ - "/c", - "%ANDROID_HOME%/emulator/emulator.exe -list-avds", - ]); + const output = await emulator("-list-avds"); return output - .split("\r\n") + .split("\n") .map((v) => v.trim()) .filter((v) => { if (!v) return false; @@ -21,11 +18,8 @@ export const getAllEmulators = async () => { }; export const getEmulatorName = async (id: string) => { - const output = await execute("cmd.exe", [ - "/c", - `%ANDROID_HOME%/platform-tools/adb.exe -s ${id} emu avd name`, - ]); - return output.split("\r\n").map((v) => v.trim())[0]; + const output = await adb(`-s ${id} emu avd name`); + return output.split("\n").map((v) => v.trim())[0]; }; export const getEmulatorState = ( @@ -36,11 +30,8 @@ export const getEmulatorState = ( }; export const getEmulatorProps = async (id: string) => { - const output = await execute("cmd.exe", [ - "/c", - `%ANDROID_HOME%/platform-tools/adb.exe -s ${id} shell getprop`, - ]); - const kv = output.split("\r\n").map((v) => { + const output = await adb(`-s ${id} shell getprop`); + const kv = output.split("\n").map((v) => { const [key, value] = v.replace(/(\[|\])/gm, "").split(": "); return [key, value]; }); @@ -48,11 +39,8 @@ export const getEmulatorProps = async (id: string) => { }; export const getEmulatorFeatures = async (id: string) => { - const output = await execute("cmd.exe", [ - "/c", - `%ANDROID_HOME%/platform-tools/adb.exe -s ${id} shell pm list features`, - ]); - return output.split("\r\n"); + const output = await adb("shell pm list features"); + return output.split("\n"); }; export const getEmulatorType = (features: string[]): OnlineEmulatorType => { @@ -63,12 +51,9 @@ export const getEmulatorType = (features: string[]): OnlineEmulatorType => { }; export const getOnlineEmulators = async () => { - const output = await execute("cmd.exe", [ - "/c", - "%ANDROID_HOME%/platform-tools/adb.exe devices -l", - ]); + const output = await adb("devices -l"); const rows = output - .split("\r\n") + .split("\n") .map((v) => v.trim()) .filter((v) => { if (!v) return false; @@ -116,15 +101,9 @@ export const startEmulator = async (name: string, cold = false) => { if (cold) args.push("-no-snapshot-load"); - await execute("cmd.exe", [ - "/c", - `%ANDROID_HOME%/emulator/emulator.exe ` + args.join(" "), - ]); + await emulator(args.join(" ")); }; export const stopEmulator = async (id: string) => { - await execute("cmd.exe", [ - "/c", - `%ANDROID_HOME%/platform-tools/adb.exe -s ${id} emu kill`, - ]); + await adb(`-s ${id} emu kill`); }; diff --git a/src/services/execute.ts b/src/services/execute.ts index 8c67343..a6894eb 100644 --- a/src/services/execute.ts +++ b/src/services/execute.ts @@ -1,13 +1,41 @@ +import { platform } from "@tauri-apps/plugin-os"; import { Command, type SpawnOptions } from "@tauri-apps/plugin-shell"; +const getAndroidHomePrefix = () => { + const currentPlatfrom = platform(); + + switch (currentPlatfrom) { + case "windows": + return "%ANDROID_HOME"; + + default: + return "$ANDROID_HOME"; + } +}; + +const makeCommand = (executable: string, ...args: string[]) => { + const currentPlatfrom = platform(); + + switch (currentPlatfrom) { + case "windows": + return Command.create("cmd.exe", [ + "/c", + executable + ".exe " + args.join(" "), + ]); + + default: + return Command.create("sh", ["-c", executable + " " + args.join(" ")]); + } +}; + export const execute = async ( - base: string, - args?: string | string[], + executable: string, + args: string[] = [], options?: SpawnOptions & { timeout?: number; verbose?: boolean } ) => { - const cmd = Command.create(base, args, options); + const cmd = makeCommand(executable, ...args); cmd.stdout.on("data", (v) => { - if (options?.verbose) console.info(base, args, v); + if (options?.verbose) console.info(executable, args, v); }); if (typeof options?.timeout === "number") { setTimeout(() => { @@ -16,5 +44,15 @@ export const execute = async ( } const process = await cmd.execute(); - return process.stdout; + return process.stdout.replace(/\r/gm, ""); +}; + +export const emulator = (command: string) => { + const prefix = getAndroidHomePrefix(); + return execute(`${prefix}/emulator/emulator`, [command]); +}; + +export const adb = (command: string) => { + const prefix = getAndroidHomePrefix(); + return execute(`${prefix}/platform-tools/adb`, [command]); };