import { spawnSync } from "node:child_process"; import { chmodSync, copyFileSync, mkdirSync, mkdtempSync, rmSync, } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { fileURLToPath } from "node:url"; const source = fileURLToPath(new URL(".", import.meta.url)); const temp = mkdtempSync(join(tmpdir(), "pi-os-notifier-focus-build-")); const classes = join(temp, "classes"); const dex = join(temp, "dex"); function run(command, args) { const result = spawnSync(command, args, { stdio: "inherit", timeout: 30_000, }); if (result.error) throw result.error; if (result.status !== 0) throw new Error(`${command} failed: ${result.status ?? result.signal}`); } try { mkdirSync(classes); mkdirSync(dex); run("javac", [ "--release", "8", "-g:none", "-Xlint:-options", "-d", classes, join(source, "stubs/android/app/ActivityManager.java"), join(source, "stubs/android/app/IActivityManager.java"), join(source, "F.java"), ]); // Compile-only Android signatures are deliberately excluded from the DEX. run("d8", [ "--release", "--min-api", "26", "--lib", classes, "--output", dex, join(classes, "F.class"), ]); run("jar", [ "--create", "--no-manifest", "--date=2000-01-01T00:00:00Z", "--file", join(temp, "focus.jar"), "-C", dex, "classes.dex", ]); const target = join(source, "focus.jar"); // Normal resource permissions; runtime removes write bits before Android loads it. try { chmodSync(target, 0o644); } catch (error) { if (error.code !== "ENOENT") throw error; } copyFileSync(join(temp, "focus.jar"), target); } finally { rmSync(temp, { recursive: true, force: true }); }