Add a generated demo photo library for store screenshots
The results screenshot is a screenshot *of a photo library*, so the committed capture showed the developer's own photos. tools/make-demo-photos.py draws the library instead: 22 procedural scenes (sunset, mountains, beach, conifers in haze, dunes, city skyline, night sky, bokeh) with grain and a vignette, so there are no people, no third-party content and nothing to license. The duplicate structure is deliberate, because a folder of unrelated pictures demonstrates nothing: - headline group: one scene re-saved at falling quality and size until its reclaimable size reaches --target-mb (45 MB). Results are ranked by reclaimable bytes, so this group has to out-rank whatever is already on the device or a real photo is the first thing back in frame. - exact pair: two byte-identical files, for the SHA-256 path. - singles: the other scenes, once each, so the library looks lived-in. Verified with the app's own engine on the desktop before pushing anything: 22 images -> 2 groups (PERCEPTUAL 14 copies, EXACT 2 copies) with the six singles left alone. Regeneration is byte-identical, the output path resolves from __file__ so it runs from any cwd, and nothing is written outside the repo. demo-photos/ is git-ignored, like fixtures/ and store-assets/raw-screenshots/. STORE_LISTING.md documents the library, why the headline group has a target size, the MediaProvider scan_file recipe and the cleanup step; ROADMAP, README and the release checklist point at it.
This commit is contained in:
+70
-7
@@ -17,6 +17,7 @@ release they belong to. Only the uncropped captures the screenshots are cut from
|
||||
```bash
|
||||
./tools/make-store-assets.sh # icon + feature graphic -> store-assets/
|
||||
./tools/make-store-screenshots.sh # crop the adb captures -> store-assets/screenshots/
|
||||
python3 tools/make-demo-photos.py # synthetic photo library -> demo-photos/ (git-ignored)
|
||||
```
|
||||
|
||||
| Asset | Play's spec | Result | Source |
|
||||
@@ -45,19 +46,81 @@ Captured on the test device (1080×2400, Android 12 / MIUI), cropped to 1080×21
|
||||
| `02-scanning.png` | Scanning progress — *“Looking for visually similar photos…”* 5341 / 5363 |
|
||||
| `03-results.png` | Results — 15 duplicate groups, 39.8 MB reclaimable, `BEST` badge, selection bar |
|
||||
|
||||
⚠️ **Two caveats before these go live.**
|
||||
⚠️ **Both caveats below are outstanding for the files committed today.** One re-shoot fixes both.
|
||||
|
||||
1. All three still show the *“Photo access needed”* banner. The test device (Xiaomi M2101K7AG
|
||||
/ MIUI) refuses `pm grant`, `install -g` and synthetic taps, so the runtime permission was
|
||||
never actually granted there — the scan was driven with a debug-only
|
||||
`MANAGE_EXTERNAL_STORAGE` app-op, which the app's permission check does not honour (see
|
||||
[EDGE_CASE_TESTING](EDGE_CASE_TESTING.md)). **Re-shoot on a clean install after tapping
|
||||
`MANAGE_EXTERNAL_STORAGE` app-op. That app-op gets the scan through MediaStore but does
|
||||
**not** satisfy the app's own `checkSelfPermission`, which is why the banner — and the
|
||||
disabled *Start scan* — are genuinely in frame. **Re-shoot on a clean install after tapping
|
||||
“Grant access”**: that also closes the one remaining device-only verification gap.
|
||||
2. The results screenshot shows real photos from the test library. They are the developer's
|
||||
own, so they can be published, but check that nothing personal is recognisable first.
|
||||
|
||||
Screens still worth capturing once a device grants access through the real flow — each needs
|
||||
one tap, which is exactly what the MIUI test device cannot be driven to do:
|
||||
Enabling *USB debugging (Security settings)* in the MIUI developer options turns synthetic
|
||||
taps and `pm grant` back on. That is what would let the capture run from a script instead of
|
||||
by hand.
|
||||
2. `03-results.png` currently shows real photos from the test library. They are the developer's
|
||||
own, so nothing is licensed against us, but the library is not ours to publish. The next
|
||||
capture must be taken over the generated demo library below.
|
||||
|
||||
### Demo library: generated photos, so none of anyone's real ones are published
|
||||
|
||||
`03-results.png` is a screenshot *of a photo library*, so whatever is on the test device ends up
|
||||
in the store listing along with it. `tools/make-demo-photos.py` exists so that library can be
|
||||
generated instead of borrowed: every scene is drawn procedurally — sunset, mountains, beach,
|
||||
conifers in haze, dunes, city skyline, night sky, bokeh — so there are no people, no third-party
|
||||
content and nothing to license. It writes to `demo-photos/foto-dup-demo/`, which is git-ignored
|
||||
and regenerated on demand, exactly like `fixtures/`.
|
||||
|
||||
The duplicate structure is deliberate, because a folder of unrelated pictures demonstrates
|
||||
nothing:
|
||||
|
||||
- **headline group** — one scene re-saved at falling quality and size until its reclaimable
|
||||
size reaches `--target-mb` (default 45 MB),
|
||||
- **exact pair** — two byte-identical files, for the SHA-256 path,
|
||||
- **singles** — the other scenes, once each, so the library looks lived-in.
|
||||
|
||||
```bash
|
||||
python3 tools/make-demo-photos.py
|
||||
adb push demo-photos/foto-dup-demo /sdcard/DCIM/foto-dup-demo
|
||||
adb shell content call --uri content://media --method scan_file \
|
||||
--arg /sdcard/DCIM/foto-dup-demo
|
||||
```
|
||||
|
||||
Two non-obvious details, both load-bearing:
|
||||
|
||||
- **Why the headline group has a target size.** Results are ranked by reclaimable bytes, so the
|
||||
first group on screen is the largest one. A demo group smaller than anything already on the
|
||||
device would be pushed below the fold and a real photo would be back in frame. 45 MB clears
|
||||
the test device's own 39.8 MB with room to spare; raise `--target-mb` if you capture on a
|
||||
library whose biggest group is larger.
|
||||
- **`scan_file` on the folder, not on the volume.** It indexes just that path (API 30+) instead
|
||||
of forcing a MediaStore-wide rescan on a 5,000-photo device. It does recurse into the folder,
|
||||
and re-running it after the folder is deleted drops those rows again — both verified on the
|
||||
test device.
|
||||
|
||||
Check the grouping *before* pushing, rather than reading it off the phone: the desktop harness
|
||||
runs the app's real engine over a scaled copy of the library and reports the two intended groups
|
||||
and nothing else.
|
||||
|
||||
```bash
|
||||
magick mogrify -resize 1024x -quality 90 -path build/demo-validate-raw demo-photos/foto-dup-demo/*.jpg
|
||||
python3 tools/normalise-for-local-grouping.py build/demo-validate-raw build/demo-validate
|
||||
./gradlew :app:testDebugUnitTest --tests '*LocalGroupingTest*' \
|
||||
-DfotoLocal.dir="$PWD/build/demo-validate"
|
||||
# images=22 groups=2 -> PERCEPTUAL 14 copies, EXACT 2 copies; the six singles stay ungrouped
|
||||
```
|
||||
|
||||
Cleanup, once the captures are done:
|
||||
|
||||
```bash
|
||||
adb shell rm -rf /sdcard/DCIM/foto-dup-demo
|
||||
adb shell content call --uri content://media --method scan_file --arg /sdcard/DCIM/foto-dup-demo
|
||||
```
|
||||
|
||||
Screens still worth capturing once a device grants access through the real flow (the capture run
|
||||
needs two taps on the phone — *Grant access*, then *Scan* — which is the one thing the MIUI test
|
||||
device will not accept from a script):
|
||||
|
||||
- the **Options** sheet with the three sensitivity presets,
|
||||
- the staged state (*“All duplicates are ready to go”*) after selecting duplicates,
|
||||
|
||||
Reference in New Issue
Block a user