Boot-time startup so LedGrab has display capture and control without user
interaction on rooted TV boxes. Also folds in a batch of review findings
from the Android package audit.
Autostart
- BootReceiver fires on BOOT_COMPLETED / LOCKED_BOOT_COMPLETED /
MY_PACKAGE_REPLACED, gated by AutostartPrefs and Root.looksRooted().
Dispatches CaptureService.createRootIntent via
ContextCompat.startForegroundService. Unrooted devices are a no-op
because MediaProjection consent cannot be bypassed silently.
- AutostartPrefs: thin SharedPreferences wrapper, defaults to enabled.
Exposed as a CheckBox on the stopped panel; greyed out when not rooted.
- Manifest: RECEIVE_BOOT_COMPLETED, REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
WAKE_LOCK permissions + the new BootReceiver.
- MainActivity prompts for battery-optimization exemption on first opt-in
so Doze/App Standby doesn't kill the FG service on phones.
Service stability
- onStartCommand now flips isRunning only after startForeground succeeds
(was stuck=true forever if the FG transition threw) and resets on
exception. Returns START_REDELIVER_INTENT for root mode so the OS can
restart the service with the original intent (no consent token to
invalidate); MediaProjection mode keeps START_NOT_STICKY.
- Watchdog coroutine monitors RootScreenrecord.framesDelivered. Respawns
the pipeline on stall (reusing the existing Python bridge — no server
restart), caps at 3 consecutive restarts before giving up.
- RootScreenrecord.framesDelivered is now an AtomicInteger, exposed as a
public property for the watchdog.
- ScreenCapture takes an onProjectionStopped lambda; when the user taps
the system Cast/Screen-capture stop banner, the whole service is torn
down instead of leaving a stale FG notification.
- MainActivity's two startForegroundService calls switch to
ContextCompat.startForegroundService, clearing pre-existing NewApi lint
errors (minSdk=24 < API 26 native method).
Build
- versionCode derived from git rev-list --count HEAD (or the
ANDROID_VERSION_CODE env var for CI). Was pinned to 1 — sideload
upgrades were silently refusing to install.
- New i18n strings (autostart_label, autostart_unavailable, version_prefix)
in en/ru/zh; version_text now uses the resource instead of string
concat.
TODO.md: new "Android Autostart on Boot" section tracking done/pending
items; real-hardware verification on a Magisk'd TV box is the remaining
checkbox.
End-to-end BLE streaming: provider + client + per-protocol wire encoders
with whole-strip averaging, desktop (bleak) and Android (Kotlin BleBridge
via Chaquopy) transports, discovery with protocol-family detection that
auto-fills the UI, throttled not-connected warning + 10 s reconnect
cooldown so a dropped link no longer stalls the pipeline at ~30 s/frame,
and an explicit asyncio.wait_for wrapper around bleak connect() since
the WinRT backend doesn't always honor the timeout kwarg.
Also rewrites server/restart.ps1 to be parameterized (-Port / -Module /
-PythonVersion / timeouts / -Quiet), pick the right interpreter via the
py launcher, pre-flight the target module, poll port readiness on both
shutdown and startup, redirect child stdout/stderr so Start-Process
doesn't hang on inherited Git-Bash handles, and return proper exit codes.
Rolls in concurrent work: Android BLE permissions + launcher icons + ru/zh
resources, Chaquopy-safe value_stream psutil fallback, setup-required
modal, asset-store test coverage, and misc system/config touch-ups.