Files
ledgrab/android/build-scripts/setup-ndk.sh
T
alexei.dolgolyov 8574424fb7
Lint & Test / test (push) Successful in 2m10s
feat: Android TV app embedding Python server via Chaquopy
Adds a native Android TV application that runs the full LedGrab Python
server in-process via Chaquopy. Captures the TV box screen using the
MediaProjection API and exposes the existing web UI on the device's
local network — users configure via phone/tablet browser.

Android (new /android/ module):
- Kotlin shell: MainActivity, CaptureService (foreground service),
  ScreenCapture (MediaProjection + ImageReader), PythonBridge (Chaquopy).
- Polished Leanback-themed UI with QR code for easy web UI access.
- AGP 8.9 + Chaquopy 17 + Gradle 8.11 (avoids the AGP 8.7 thread-lock bug).
- Pre-built pydantic-core wheels for arm64-v8a, x86_64, x86 cross-compiled
  with maturin + Android NDK, linked against Chaquopy's libpython3.11.so.

Python server platform guards:
- New utils/platform.py with is_android()/is_windows()/is_linux() helpers.
- Guard every top-level import of desktop-only packages (mss, psutil,
  sounddevice, pyserial, PyAudioWPatch, etc.) with try/except ImportError.
- Android-incompatible calls gated with None-checks so the server runs on
  reduced capabilities on Android (no CPU/RAM metrics, no mss displays).
- utils/image_codec.py gains a Pillow fallback for resize + JPEG encode
  when cv2 is unavailable; all internal cv2.resize callers migrated.
- New android_entry.py start_server/stop_server invoked from Kotlin.
- get_displays API falls back to best available engine when mss fails.

New capture engines:
- MediaProjectionEngine: receives RGBA frames pushed from Kotlin through
  a thread-safe queue; caches last frame for static-screen previews.
- ScrcpyClientEngine: optional H.264 streaming via scrcpy-client library
  (priority 10, overrides the ADB-screencap engine when installed).

Frontend:
- Tab loaders previously required an apiKey; now correctly treat
  "auth disabled" as authenticated (Android has no auth by default).
- Re-trigger the active tab's loader after loadServerInfo resolves
  authRequired, since initTabs runs earlier.
- Add i18n keys for the demo / mediaprojection / scrcpy_client engines.

Docs:
- TODO.md: follow-ups for multi-ABI wheel rebuilds, CI pipeline, USB
  serial LED controllers, root-only capture, perf metrics abstraction.
- CLAUDE.md: Android dependency sync policy (pip --exclude doesn't exist).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-14 03:11:43 +03:00

75 lines
2.5 KiB
Bash

#!/usr/bin/env bash
#
# Set up the cross-compilation environment for building Python native
# extensions targeting Android ARM64.
#
# This script:
# 1. Verifies Android NDK is installed
# 2. Installs the Rust aarch64-linux-android target
# 3. Installs maturin (Python wheel builder for Rust extensions)
# 4. Runs a quick test compile to verify the toolchain works
#
set -euo pipefail
echo "=== LedGrab Android NDK Setup ==="
# ── Check prerequisites ─────────────────────────────────────────────
if ! command -v rustc &>/dev/null; then
echo "ERROR: Rust is not installed."
echo "Install from: https://rustup.rs/"
exit 1
fi
echo "Rust: $(rustc --version)"
if ! command -v cargo &>/dev/null; then
echo "ERROR: Cargo is not installed."
exit 1
fi
echo "Cargo: $(cargo --version)"
if ! command -v python3.11 &>/dev/null && ! command -v python3 &>/dev/null; then
echo "WARNING: Python 3.11 not found. Needed for maturin builds."
fi
# ── Install Rust target ─────────────────────────────────────────────
echo ""
echo "Installing Rust Android target..."
rustup target add aarch64-linux-android
echo "Installed targets:"
rustup target list --installed | grep android
# ── Install maturin ─────────────────────────────────────────────────
echo ""
echo "Installing maturin..."
pip install maturin 2>/dev/null || pip3 install maturin 2>/dev/null || {
echo "WARNING: Could not install maturin. Install manually: pip install maturin"
}
if command -v maturin &>/dev/null; then
echo "maturin: $(maturin --version)"
fi
# ── Verify NDK ──────────────────────────────────────────────────────
echo ""
if [ -n "${ANDROID_NDK_HOME:-}" ]; then
echo "ANDROID_NDK_HOME: $ANDROID_NDK_HOME"
else
echo "ANDROID_NDK_HOME is not set."
echo "Set it to your NDK installation path, e.g.:"
echo " export ANDROID_NDK_HOME=\$HOME/Android/Sdk/ndk/26.1.10909125"
echo ""
echo "Or install NDK via Android Studio:"
echo " SDK Manager → SDK Tools → NDK (Side by side)"
fi
echo ""
echo "=== Setup complete ==="
echo ""
echo "Next steps:"
echo " 1. Ensure ANDROID_NDK_HOME is set"
echo " 2. Run: ./build-pydantic-core.sh"