#!/usr/bin/env python3 """Build Sleek's Relm4 APK with GTK Android Builder.""" from __future__ import annotations import argparse import os import shutil import subprocess from pathlib import Path def run(*args: str) -> None: subprocess.run(args, check=True) def find_android_home(explicit: str | None) -> Path: candidates = [ explicit, os.environ.get("ANDROID_HOME"), os.environ.get("ANDROID_SDK_ROOT"), str(Path.home() / ".local/share/android-sdk"), str(Path.home() / "Android/Sdk"), ] for candidate in candidates: if candidate and (Path(candidate).expanduser() / "platform-tools/adb").is_file(): return Path(candidate).expanduser().resolve() raise SystemExit("Android SDK not found; set ANDROID_HOME") # The freeq auth broker redirects to freeq://auth?… after ATProto sign-in. # Pixiewood only emits MAIN/LAUNCHER, so without this filter the callback has # nowhere to land and sign-in never completes. GDK forwards intents carrying # data to g_application_open, which the Relm4 frontend handles. DEEP_LINK_FILTER = """ """ def patch_manifest(manifest: Path) -> None: text = manifest.read_text() permission = ' \n' if "android.permission.INTERNET" not in text: marker = ' ' if marker not in text: raise RuntimeError("Pixiewood manifest permission marker is missing") text = text.replace(marker, permission + marker) if 'android:scheme="freeq"' not in text: marker = " \n" if marker not in text: raise RuntimeError("Pixiewood manifest intent-filter marker is missing") text = text.replace(marker, marker + DEEP_LINK_FILTER, 1) manifest.write_text(text) def main() -> None: parser = argparse.ArgumentParser() parser.add_argument("--android-home") parser.add_argument("--output", type=Path, default=Path("sleek-relm4.apk")) args = parser.parse_args() root = Path(__file__).resolve().parent.parent sdk = find_android_home(args.android_home) pixiewood = shutil.which("pixiewood") if not pixiewood: raise SystemExit("pixiewood is not on PATH; enter the Sleek Nix shell") run(pixiewood, "-C", str(root), "prepare", "-s", str(sdk), "pixiewood.xml") run(pixiewood, "-C", str(root), "generate") manifest = root / ".pixiewood/android/app/src/main/AndroidManifest.xml" patch_manifest(manifest) run(pixiewood, "-C", str(root), "build") output_root = root / ".pixiewood/android/app/build/outputs/apk" apks = sorted(output_root.glob("**/*.apk")) if len(apks) != 1: raise RuntimeError(f"expected one APK under {output_root}, found {len(apks)}") output = args.output.resolve() output.parent.mkdir(parents=True, exist_ok=True) shutil.copy2(apks[0], output) print(output) if __name__ == "__main__": main()