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:
@@ -92,3 +92,57 @@ class TestRootEndpoint:
|
||||
resp = client.get("/")
|
||||
assert resp.status_code == 200
|
||||
assert "text/html" in resp.headers["content-type"]
|
||||
|
||||
|
||||
class TestInstalledAppsEndpoint:
|
||||
def test_requires_auth(self, client):
|
||||
resp = client.get("/api/v1/system/installed-apps")
|
||||
assert resp.status_code == 401
|
||||
|
||||
def test_empty_off_android(self, client):
|
||||
"""Desktop test host: is_android() is False, so the bridge wrapper
|
||||
short-circuits to an empty list."""
|
||||
resp = client.get("/api/v1/system/installed-apps", headers=_auth_headers())
|
||||
assert resp.status_code == 200
|
||||
assert resp.json() == {"apps": [], "count": 0}
|
||||
|
||||
def test_returns_apps_when_available(self, client, monkeypatch):
|
||||
from ledgrab.core.automations import platform_detector as pd
|
||||
|
||||
monkeypatch.setattr(
|
||||
pd,
|
||||
"list_installed_apps",
|
||||
lambda: [{"package": "com.netflix.mediaclient", "label": "Netflix"}],
|
||||
)
|
||||
resp = client.get("/api/v1/system/installed-apps", headers=_auth_headers())
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["count"] == 1
|
||||
assert data["apps"][0] == {"package": "com.netflix.mediaclient", "label": "Netflix"}
|
||||
|
||||
|
||||
class TestSystemInfoEndpoint:
|
||||
def test_requires_auth(self, client):
|
||||
resp = client.get("/api/v1/system/info")
|
||||
assert resp.status_code == 401
|
||||
|
||||
def test_desktop_signal(self, client):
|
||||
resp = client.get("/api/v1/system/info", headers=_auth_headers())
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["is_android"] is False
|
||||
assert data["app_match_kind"] == "process"
|
||||
assert data["usage_access_granted"] is True
|
||||
|
||||
def test_android_signal(self, client, monkeypatch):
|
||||
import ledgrab.utils.platform as plat
|
||||
from ledgrab.core.automations import platform_detector as pd
|
||||
|
||||
monkeypatch.setattr(plat, "is_android", lambda: True)
|
||||
monkeypatch.setattr(pd, "has_usage_access", lambda: False)
|
||||
resp = client.get("/api/v1/system/info", headers=_auth_headers())
|
||||
assert resp.status_code == 200
|
||||
data = resp.json()
|
||||
assert data["is_android"] is True
|
||||
assert data["app_match_kind"] == "package"
|
||||
assert data["usage_access_granted"] is False
|
||||
|
||||
Reference in New Issue
Block a user