feat(android): foreground-app automation condition

Make the existing Application automation rule (foreground app -> activate
scene) work on the Android-TV build. A Kotlin ForegroundAppBridge reads the
foreground app via UsageStatsManager and lists launchable apps via LauncherApps;
PlatformDetector bridges it in (ahead of the Windows-only ctypes guard) so the
existing AutomationEngine / ApplicationRule / storage / deactivation modes are
unchanged. New /system/installed-apps + /system/info endpoints feed an app picker
that stores package names (vs process names on desktop); on Android the editor
hides the match-type selector since the foreground app is the only obtainable
signal. PACKAGE_USAGE_STATS is granted via an on-device button + a web-UI banner
(no blanket prompt at capture start); detection degrades gracefully until granted.

Zero new Python/Gradle deps (UsageStatsManager + LauncherApps are in-platform;
matching only string-compares the package name, so no QUERY_ALL_PACKAGES).
assembleDebug + 1897 pytest + ruff + tsc + npm build all green; independent final
review (0 blockers) + security review (no critical issues).
This commit is contained in:
2026-06-02 14:57:29 +03:00
parent 68040173c6
commit 1c1bbe2551
24 changed files with 1044 additions and 63 deletions
+12
View File
@@ -65,6 +65,18 @@
service start. -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- PACKAGE_USAGE_STATS — read the foreground app for the "Application"
automation rule (foreground app -> activate scene) via UsageStatsManager.
A special-access permission: it can't be granted at runtime; the user
toggles it under Settings > Usage access (opened from MainActivity).
tools:ignore="ProtectedPermissions" silences the build warning that this
is a system/signature-level permission — it is honoured as a user-grantable
special access. NO QUERY_ALL_PACKAGES is needed: matching only compares the
foreground package NAME, and the app picker uses LauncherApps. -->
<uses-permission
android:name="android.permission.PACKAGE_USAGE_STATS"
tools:ignore="ProtectedPermissions" />
<!-- Autostart on boot — BootReceiver spawns CaptureService in root
mode so capture resumes without the user touching the remote. -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />