diff --git a/CHANGELOG.md b/CHANGELOG.md index b83726e..04ef57c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -139,6 +139,10 @@ contextual feedback sheet on the other. ### Changed +- `specs/screenshots.md` and `scripts/screenshots.sh`: one command retakes + every README screenshot, dark and light, per screen by @mabd-agent +- README screenshots retaken on a phone, dark and light, showing the unified + top app bars by @mabd-agent - `specs/architecture.md` now matches the shipped data layer: the data source and repository snippets compile, sorting is documented as the data source's job, and the mapping shows the lowercased provider slug and the id-prefix diff --git a/README.md b/README.md index 5f3cbe0..1068604 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Single `:app` module. Compose, Koin, OkHttp, kotlinx.serialization and Navigatio -Captured on a tablet with a Fedo API key configured. The colours come from the +Captured on a phone (Galaxy A51) with a Fedo API key configured. The colours come from the device wallpaper: the app uses dynamic colour on API 31+ and the Material baseline schemes below that, so your build will not look identical. diff --git a/docs/images/model-detail-light.png b/docs/images/model-detail-light.png index 9b92d52..e0d7ece 100644 Binary files a/docs/images/model-detail-light.png and b/docs/images/model-detail-light.png differ diff --git a/docs/images/model-detail.png b/docs/images/model-detail.png index 339067e..9a49f5f 100644 Binary files a/docs/images/model-detail.png and b/docs/images/model-detail.png differ diff --git a/docs/images/models-list-light.png b/docs/images/models-list-light.png index af9d71d..e5f0f21 100644 Binary files a/docs/images/models-list-light.png and b/docs/images/models-list-light.png differ diff --git a/docs/images/models-list.png b/docs/images/models-list.png index 6c61690..c848128 100644 Binary files a/docs/images/models-list.png and b/docs/images/models-list.png differ diff --git a/docs/images/roadmap-light.png b/docs/images/roadmap-light.png index 6b8ceb4..105d630 100644 Binary files a/docs/images/roadmap-light.png and b/docs/images/roadmap-light.png differ diff --git a/docs/images/roadmap.png b/docs/images/roadmap.png index d41676e..505da99 100644 Binary files a/docs/images/roadmap.png and b/docs/images/roadmap.png differ diff --git a/docs/images/settings-light.png b/docs/images/settings-light.png index ac5cb31..e16c515 100644 Binary files a/docs/images/settings-light.png and b/docs/images/settings-light.png differ diff --git a/docs/images/settings.png b/docs/images/settings.png index a601215..f8c74c9 100644 Binary files a/docs/images/settings.png and b/docs/images/settings.png differ diff --git a/scripts/screenshots.sh b/scripts/screenshots.sh new file mode 100755 index 0000000..ea62f40 --- /dev/null +++ b/scripts/screenshots.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# Retakes the README screenshots. See specs/screenshots.md. +# +# scripts/screenshots.sh [screen...] +# +# Screens: models-list model-detail roadmap settings (default: all). +# Every screen is shot in dark, then light, into docs/images/. +set -euo pipefail + +SERIAL=${1:?usage: scripts/screenshots.sh [screen...]} +shift +if [ $# -eq 0 ]; then set -- models-list model-detail roadmap settings; fi +SCREENS=("$@") + +export ANDROID_SERIAL=$SERIAL +PKG=com.fedo.modelpulse +OUT=$(cd "$(dirname "$0")/.." && pwd)/docs/images + +# ui : reads the current UI dump. +# ui has -> exit 0 if a node's text or content-desc equals it +# ui tap -> prints "x y" of that node's centre +# ui card -> prints "x y" of the first model card +ui() { + adb exec-out uiautomator dump /dev/tty 2>/dev/null | python3 -c ' +import sys, re, xml.etree.ElementTree as ET +mode, arg = sys.argv[1], sys.argv[2] +raw = sys.stdin.read() +if "") + 1]).iter("node")) +def box(n): return list(map(int, re.findall(r"\d+", n.get("bounds")))) +def centre(n): l, t, r, b = box(n); print((l + r) // 2, (t + b) // 2) +if mode == "card": + width = max(box(n)[2] for n in nodes) + wide = [n for n in nodes if n.get("clickable") == "true" and box(n)[2] - box(n)[0] > width * 0.9] + centre(wide[1]) # wide[0] is the search field + sys.exit(0) +match = [n for n in nodes if arg in (n.get("text"), n.get("content-desc"))] +if not match: sys.exit(1) +if mode == "tap": centre(match[0]) +' "$1" "${2:-}" +} + +# wait_for : polls up to 20 s, then fails the run. +wait_for() { + for _ in $(seq 20); do ui has "$1" && { sleep 1; return; }; sleep 1; done + echo "timeout waiting for '$1'" >&2; exit 1 +} + +tap() { adb shell input tap $(ui tap "$1"); } + +launch() { + adb shell am force-stop $PKG + adb shell am start -W -n $PKG/.MainActivity >/dev/null + wait_for "Search models" +} + +shot() { adb exec-out screencap -p > "$OUT/$1.png"; echo "$1.png"; } + +# One function per screen; each starts from a fresh launch on Models. +models-list() { launch; shot "models-list$1"; } +model-detail() { launch; adb shell input tap $(ui card); wait_for "Copy"; shot "model-detail$1"; } +roadmap() { launch; tap "Roadmap"; wait_for "Feedback"; sleep 2; shot "roadmap$1"; } +settings() { launch; tap "Settings"; wait_for "Fedo SDK"; shot "settings$1"; } + +ORIGINAL_NIGHT=$(adb shell cmd uimode night | awk '{print $3}') +trap 'adb shell cmd uimode night "$ORIGINAL_NIGHT" >/dev/null' EXIT + +for theme in dark light; do + if [ $theme = dark ]; then adb shell cmd uimode night yes >/dev/null; suffix="" + else adb shell cmd uimode night no >/dev/null; suffix="-light"; fi + for s in "${SCREENS[@]}"; do "$s" "$suffix"; done +done diff --git a/specs/screenshots.md b/specs/screenshots.md new file mode 100644 index 0000000..2f5b491 --- /dev/null +++ b/specs/screenshots.md @@ -0,0 +1,74 @@ +# README Screenshots + +How to retake the eight images in `docs/images/` that `README.md` shows. +One script does it: `scripts/screenshots.sh`. Run it and do nothing else by +hand, so every run and every agent produces the same images. + +## Run + +```bash +./gradlew :app:installDebug # with ANDROID_SERIAL set to the same device +scripts/screenshots.sh # all screens, dark + light +scripts/screenshots.sh roadmap settings # only these screens +``` + +Get the serial from `adb devices -l`. Prefer a physical phone (constitution). +The script needs `adb` and `python3` (stdlib only), and nothing else. + +## Preconditions + +- Debug build installed with a Fedo API key in `local.properties`. Without a + key, `roadmap` times out because the board never renders. +- Demo user signed in (Settings → **Sign in as demo user**), so Settings + shows the signed-in card. +- Device unlocked, screen on, network up, app language English. The script + finds nodes by their English text. + +## Screens + +Every screen starts from a fresh launch (`force-stop` + `am start`) on Models, +so no screen depends on the one before it. Each screen is shot in dark +(`.png`), then in light (`-light.png`). The device's night mode +is restored when the script exits. + +| Screen | Steps | Ready when | File | +|--------|-------|------------|------| +| `models-list` | launch | `Search models` shown | `models-list.png` | +| `model-detail` | launch, tap the first model card | `Copy` shown | `model-detail.png` | +| `roadmap` | launch, tap the nav item with desc `Roadmap` | `Feedback` shown, +2 s for the SDK list | `roadmap.png` | +| `settings` | launch, tap the nav item with desc `Settings` | `Fedo SDK` shown | `settings.png` | + +Nodes are found in `uiautomator dump` by text or content-desc, never by +fixed coordinates. The nav bar changes width when you select an item, so +fixed taps land on the wrong target. The first model card is the second +clickable node that is at least 90% of the screen width. The first such node +is the search field. + +Every wait polls for 20 s and then fails the run. It never shoots a +half-loaded screen. + +## Adding a screen + +1. Add a one-line function to `scripts/screenshots.sh` named after the file: + `launch`, then navigate with `tap ""`, `wait_for ""`, + then `shot "$1"`. +2. Add `` to the default list at the top of the script. +3. Add a column to both tables in `README.md`. + +Use the text from `strings.xml`, which is the text the user sees. That is +the same rule the UI tests follow. + +## After a run + +- Check the eight images by eye. The data is live OpenRouter and Fedo + content, so it changes between runs. +- If you changed the device, update the "Captured on …" line under the + README screenshots. +- Add a line under `### Changed` in `CHANGELOG.md`. + +## Known limits + +- The status bar shows the real clock, battery and notification icons. + `com.android.systemui.demo` has no effect on Samsung devices. On a Pixel, + demo mode works if you want a clean bar. +- Colours follow the device wallpaper (dynamic colour on API 31+).