docs: record the store screenshot re-shoot over the demo library

The three captures no longer show the "Photo access needed" banner, and the results
screen is a picture of the generated demo library instead of the developer's own.
Both caveats in the file are closed, so replace them with a short note on how the set
was actually taken:

- 01-empty: the grant went through the app's own "Grant access" button, so
  checkSelfPermission is genuinely satisfied. This one needed no tap at all: results
  and the selection live in ScannerViewModel and only the hash index is persisted, so
  force-stop + relaunch returns to the empty state with the grant still in place.
- 02-scanning / 03-results: shot after tapping Scan over the demo library. The headline
  group is 43.1 MB reclaimable, which out-ranks the biggest group already on the device
  (39.8 MB), so the first group in frame is generated.
- 04-options is new: the Options DropdownMenu with the three sensitivity presets (it is
  not a bottom sheet) and the trash-vs-permanent toggle.

The tick marks and the "37 selected" bar are the screen's default state, not a staged
tap -- a finished scan auto-selects all but best. MIUI still refuses pm grant and
synthetic taps (INJECT_EVENTS), so Grant access and Scan remain the two taps a human
has to make; the two screens still missing (the staged state and the system consent
dialog) are captured by watching for UI text changes instead, rather than by timing a
scripted tap.
This commit is contained in:
2026-10-13 23:46:07 +02:00
parent ca8fdd414d
commit b43718aca8
+41 -28
View File
@@ -26,7 +26,7 @@ python3 tools/make-demo-photos.py # synthetic photo library -> demo-photos/ (gi
|---|---|---|---| |---|---|---|---|
| App icon | 512×512, 32-bit PNG with alpha, ≤ 1 MB | `store-assets/icon-512.png` — 512×512, RGBA, 6 KB | `tools/store-assets/icon.svg`, mirrored from `app/src/main/res/drawable/ic_launcher_background.xml` + `ic_launcher_foreground.xml` (paths and group transforms are identical, so the store icon *is* the launcher icon) | | App icon | 512×512, 32-bit PNG with alpha, ≤ 1 MB | `store-assets/icon-512.png` — 512×512, RGBA, 6 KB | `tools/store-assets/icon.svg`, mirrored from `app/src/main/res/drawable/ic_launcher_background.xml` + `ic_launcher_foreground.xml` (paths and group transforms are identical, so the store icon *is* the launcher icon) |
| Feature graphic | 1024×500, JPEG or 24-bit PNG without alpha | `store-assets/feature-graphic-1024x500.png` — 1024×500, RGB | `tools/store-assets/feature-graphic.svg` | | Feature graphic | 1024×500, JPEG or 24-bit PNG without alpha | `store-assets/feature-graphic-1024x500.png` — 1024×500, RGB | `tools/store-assets/feature-graphic.svg` |
| Phone screenshots | JPEG or 24-bit PNG without alpha, shorter side ≥ 320 px, longer side ≤ 3840 px, **longer side ≤ 2× shorter side** | `store-assets/screenshots/0{1,2,3}-*.png` — 1080×2160, RGB | `tools/make-store-screenshots.sh`, from raw `adb exec-out screencap` captures in `store-assets/raw-screenshots/` | | Phone screenshots | JPEG or 24-bit PNG without alpha, shorter side ≥ 320 px, longer side ≤ 3840 px, **longer side ≤ 2× shorter side** | `store-assets/screenshots/0{1,2,3,4}-*.png` — 1080×2160, RGB | `tools/make-store-screenshots.sh`, from raw `adb exec-out screencap` captures in `store-assets/raw-screenshots/` |
Two details worth keeping in mind: Two details worth keeping in mind:
@@ -44,27 +44,37 @@ Captured on the test device (1080×2400, Android 12 / MIUI), cropped to 1080×21
| File | Screen | | File | Screen |
|---|---| |---|---|
| `01-empty.png` | Intro / empty state before the first scan | | `01-empty.png` | Intro / empty state before the first scan — access already granted, so there is no banner and *Start scan* is enabled |
| `02-scanning.png` | Scanning progress — *“Looking for visually similar photos…”* 5341 / 5363 | | `02-scanning.png` | Scanning progress — *“Looking for visually similar photos…”* 5353 / 5385, live *Cancel* |
| `03-results.png` | Results — 15 duplicate groups, 39.8 MB reclaimable, `BEST` badge, selection bar | | `03-results.png` | Results over the demo library — 17 duplicate groups, 54 of 5385 photos, 83.5 MB reclaimable, the generated 14-copy group first, `BEST` badge, selection bar |
| `04-options.png` | Options menu (`DropdownMenu` off the top bar) — the three sensitivity presets with the ✓ on the active *Balanced (recommended)*, and the trash-vs-permanent toggle |
⚠️ **Both caveats below are outstanding for the three captures currently in The tick marks and the *37 selected · 83.5 MB* bar in `03-results.png` are not a staged tap: a
`store-assets/screenshots/`.** One re-shoot fixes both. finished scan auto-selects *all but best* across every group (`ScannerViewModel.autoSelection`),
so that **is** the screen's default state.
1. All three still show the *“Photo access needed”* banner. The test device (Xiaomi M2101K7AG The two earlier caveats are closed. The current captures were re-shot through the app's own
/ MIUI) refuses `pm grant`, `install -g` and synthetic taps, so the runtime permission was permission flow and over the generated demo library:
never actually granted there — the scan was driven with a debug-only
`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.
Enabling *USB debugging (Security settings)* in the MIUI developer options turns synthetic 1. **No *“Photo access needed”* banner.** The runtime grant went through the app's *Grant access*
taps and `pm grant` back on. That is what would let the capture run from a script instead of button, so `checkSelfPermission` is genuinely satisfied. The test device (Xiaomi M2101K7AG /
by hand. MIUI) still refuses `pm grant`, `install -g` and synthetic taps (`SecurityException:
2. `03-results.png` currently shows real photos from the test library. They are the developer's Injecting to another application requires INJECT_EVENTS`), so *Grant access* and *Scan* are
own, so nothing is licensed against us, but the library is not ours to publish. The next the two taps a human has to make. Enabling *USB debugging (Security settings)* in the MIUI
capture must be taken over the generated demo library below. developer options is what would make the whole capture run scriptable.
2. **No real photos in frame.** `03-results.png` was shot over the demo library below, and its
headline group (43.1 MB reclaimable) outranks the biggest group already on the device
(39.8 MB), so the first thing on screen is a generated sunset instead of somebody's library.
Only *Scan* needed a tap in the end. `01-empty.png` did not: scan results and the selection live
in `ScannerViewModel` (only the hash index is persisted), so force-stopping and relaunching the app
returns to the empty state with the grant still in place:
```bash
adb shell am force-stop de.softwareapp_hb.fotodup.finder.debug
adb shell am start -n de.softwareapp_hb.fotodup.finder.debug/de.softwareapp_hb.fotodup.finder.MainActivity
adb exec-out screencap -p > store-assets/raw-screenshots/01-empty.png
```
### Demo library: generated photos, so none of anyone's real ones are published ### Demo library: generated photos, so none of anyone's real ones are published
@@ -94,9 +104,10 @@ Two non-obvious details, both load-bearing:
- **Why the headline group has a target size.** Results are ranked by reclaimable bytes, so the - **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 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 device would be pushed below the fold and a real photo would be back in frame. The default
the test device's own 39.8 MB with room to spare; raise `--target-mb` if you capture on a `--target-mb 45` yields a headline group of **43.1 MB reclaimable**, which clears the test
library whose biggest group is larger. 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 - **`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, 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 and re-running it after the folder is deleted drops those rows again — both verified on the
@@ -121,13 +132,15 @@ 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 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 Two more screens are worth adding to the set. Each needs a tap, and MIUI only accepts taps from a
needs two taps on the phone — *Grant access*, then *Scan* — which is the one thing the MIUI test human, so they are captured by hand — a loop that takes a `uiautomator dump` and screencaps whenever
device will not accept from a script): the UI **text** changes is enough, so the capture never depends on tap timing. That is how
`04-options.png` was taken, and it catches the menu, the staging and a system dialog in one pass:
- the **Options** sheet with the three sensitivity presets, - the staged state (*“All duplicates are ready to go”*) after *Remove all selected*. Nothing is
- the staged state (*“All duplicates are ready to go”*) after selecting duplicates, deleted at that point and no dialog appears yet — the rows simply leave the list.
- Android's **confirmation dialog** for moving photos to the trash. - Android's **confirmation dialog** for the whole batch, which *Apply* launches. Dismissing it is
non-destructive: the staging is kept, and *Apply* can be tapped again.
## Store listing copy ## Store listing copy