--- name: verify-dreamdroid description: Prove dreamDroid phone UI. Prefer instrumented Compose tests. Use adb dumps only for a single look or a shell path that has no test yet. --- # Verify dreamDroid dreamDroid is a phone/tablet Enigma2 remote (`net.reichholf.dreamdroid`). **Default proof is instrumented tests**, not tapping the emulator. See `AGENTS.md`. ```bash ./gradlew.bat :app:connectedGoogleDebugAndroidTest ``` Use **JDK 25** (`JAVA_HOME`). App `compileOptions` stay on Java 17; Gradle/AGP run on JDK 25. Do not pass `-Pandroid.testInstrumentationRunnerArguments...` — that sets Gradle property `android` to a String and breaks `android.applicationVariants`. Filter a class with: ```bash adb shell am instrument -w -e class net.reichholf.dreamdroid.ui.about.AboutScreenTest net.reichholf.dreamdroid.debug.test/net.reichholf.dreamdroid.testutil.HiltTestRunner ``` Tests live in `app/androidTest/java`. Add Compose UI tests next to each new screen. Prefer Compose Material 3 / Navigation `dialog` hosts (Phase **2.1g-ii**). If the UI is still Compose inside an XML dialog or `ComposeView`, host it that way in the test until that wrapper is deleted. `verify-dreamdroid.py` is for a **single look** (screenshot) or a shell-only path that has no instrumented test yet. It is not the verification loop. **On Cursor Cloud Agents, do not use launch / GUI tapping / screenshot walkthroughs** — soft-accelerated emulator + agent display is unreliable; see `AGENTS.md` Cloud Agent environment. Use `bash .cursor/cloud/connected-test.sh` only. Helper (from repo root): ```bash python .cursor/skills/verify-dreamdroid/scripts/verify-dreamdroid.py ``` On Windows use the same command. The script finds `adb` on `PATH`, in `ADB`, or in `sdk.dir` from `local.properties`. Default package: `net.reichholf.dreamdroid.debug` (googleDebug `applicationIdSuffix`). Never drive `net.reichholf.dreamdroid` (Play/F-Droid release) or the Amazon id. Set `ANDROID_SERIAL` when more than one device is attached. Two googleDebug instances cannot run side by side on one device; debug vs release can. Read [features/README.md](features/README.md) before driving. Exercise the mapped user path, not internal setters or HTTP clients. ## Launch (optional look) 1. `JAVA_HOME` at JDK 25. `gradle.properties` must not pass `-XX:MaxPermSize` or CMS flags. 2. Install if needed: `./gradlew.bat :app:installGoogleDebug` 3. Disposable session: `python .cursor/skills/verify-dreamdroid/scripts/verify-dreamdroid.py launch --clear-data` Ready when `pidof net.reichholf.dreamdroid.debug` returns a pid and the helper prints `started ... pid=...`. A fresh install with no saved profile opens the setup wizard (`Welcome!`) and stays there until a profile is saved. Bouquets, EPG, zap, and remote still need a reachable Enigma2 WebInterface unless the recipe says otherwise. Teardown is `cleanup` below. Do not `am force-stop` or `pm uninstall` by the release package name. ## Doctor ```bash python .cursor/skills/verify-dreamdroid/scripts/verify-dreamdroid.py doctor ``` Pass only when an adb device is in `device` state, `net.reichholf.dreamdroid.debug` is installed and in the foreground, and it has a pid. If doctor fails, relaunch the debug package; do not tap a user-owned release install. ## Drive (optional look) Prefer resource ids and visible text over coordinates. About lives at the bottom of Settings, not in the drawer. | User control | Handle | | --- | --- | | Open drawer | content-desc `Open navigation drawer` (English) | | Drawer profile row | `net.reichholf.dreamdroid.debug:id/drawer_profile` | | Profile name / status | `...:id/drawer_profile_name`, `...:id/drawer_profile_status` | | TV & Movies | text `TV & Movies` | | EPG / Virtual Remote / Zap | text `EPG`, `Virtual Remote`, `Zap` | | About | Settings, then text `About` (Compose dialog) | | Add Profile FAB | `...:id/fab_main` content-desc from `R.string.profile_add` (shell XML FAB) || Autodiscovery | text `Dreambox Autodiscovery` | | TV/Radio/Movies/Timer tabs | text `TV`, `Radio`, `Movies`, `Timer` | | Now strip | text `Now` (English) / `Es läuft` (German) on TV & Movies (Settings → Now-playing strip; default on) | - First-start after `--clear-data` shows Changelog. Dismiss with `back` before driving Profiles or the drawer. The changelog text contains the word `Profiles`, so `wait-text "Profiles"` is a false positive until the dialog is gone. Receiver-backed screens (TV & Movies, Zap, EPG, remote keypresses) are unverified when the profile host does not answer. Report the unmet precondition; do not call a connection-error snackbar a successful zap. ## Evidence Instrumented test output is the proof for Compose screens. Optional look artifacts live in `.cursor/skills/verify-dreamdroid/artifacts/` (gitignored). Cleanup must not delete them. ```bash python .cursor/skills/verify-dreamdroid/scripts/verify-dreamdroid.py dump --path .cursor/skills/verify-dreamdroid/artifacts//ui.xml python .cursor/skills/verify-dreamdroid/scripts/verify-dreamdroid.py screenshot --path .cursor/skills/verify-dreamdroid/artifacts//screen.png ``` ## Cleanup ```bash python .cursor/skills/verify-dreamdroid/scripts/verify-dreamdroid.py cleanup ``` Stops the debug package started by `launch` (`kill` of the recorded pid, then `am force-stop` of `net.reichholf.dreamdroid.debug` only). Removes the device-side dump file. Leaves `artifacts/` on disk. ## Helpers `scripts/verify-dreamdroid.py` subcommands: `doctor`, `launch [--clear-data]`, `dump [--path]`, `screenshot --path`, `tap (--text|--desc|--resource-id)`, `wait-text TEXT`, `contains TEXT`, `back`, `cleanup`.