Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Single `:app` module. Compose, Koin, OkHttp, kotlinx.serialization and Navigatio

</details>

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.

Expand Down
Binary file modified docs/images/model-detail-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/model-detail.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/models-list-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/models-list.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/roadmap-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/roadmap.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/settings-light.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified docs/images/settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
72 changes: 72 additions & 0 deletions scripts/screenshots.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env bash
# Retakes the README screenshots. See specs/screenshots.md.
#
# scripts/screenshots.sh <adb-serial> [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 <adb-serial> [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 <mode> <arg>: reads the current UI dump.
# ui has <text|desc> -> exit 0 if a node's text or content-desc equals it
# ui tap <text|desc> -> 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 "<?xml" not in raw: sys.exit(1) # dump failed mid-animation; callers retry
nodes = list(ET.fromstring(raw[raw.index("<?xml"):raw.rindex(">") + 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 <text|desc>: 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
74 changes: 74 additions & 0 deletions specs/screenshots.md
Original file line number Diff line number Diff line change
@@ -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 <adb-serial> # all screens, dark + light
scripts/screenshots.sh <adb-serial> 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
(`<name>.png`), then in light (`<name>-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 "<text or desc>"`, `wait_for "<text>"`,
then `shot "<name>$1"`.
2. Add `<name>` 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+).
Loading