feat: record the foreground-service video Play's declaration asks for

The App content > Foreground service permissions form does not just want the justification that
was already written down. Per declared type it asks for four things, one of which is "a link to
a video demonstrating each foreground service feature. The video should demonstrate the steps
the user needs to take in your app in order to trigger the feature."

tools/record-fgs-demo.sh records exactly that journey on a device or emulator: the app on its
intro, the user tapping Start scan, Home, the ongoing notification visible in the shade while
the scan keeps running, and back into the app for the results. It loads the generated demo
library first so no real photo is in frame, writes a checkpoint PNG at each milestone so the
recording can be checked without decoding it, and takes the library off the device afterwards.
The output is git-ignored, like the screenshots.

Four things the script has to get right, each learned by getting it wrong first:

- bounds="[x1,y1][x2,y2]" cannot be parsed by stripping everything but digits: the "][ "
  between the corners goes too, which glued y1 to x2 and produced coordinates like
  "976 723061" - a tap that lands nowhere and reports no error at all.
- No single text matcher works: an exact match never finds the results card ("2 duplicate
  groups"), while a substring match lets "Scan" match both "Start scan" and "Scanned in
  39.7 s". Both matchers exist and each call site uses the right one.
- A finished scan leaves the app on its results screen, where the intro's button no longer
  exists - tapping those stale coordinates silently does nothing.
- `set -e` plus start="$(failing command)" aborts a run with no message whatsoever, which
  cost a whole debugging cycle.

Checklist step 5 now carries all four fields with paste-ready text: the description, the user
impact if the scan is deferred or interrupted (a scan is not deferrable work - the user is
waiting for a result, so there is no WorkManager/JobScheduler fallback), the use case
(Local processing: Other, the only dataSync option that describes a local, user-initiated
scan), and the video link.

Recording the take on this machine is not reliable, and the docs say so rather than pretending
otherwise, because both failures are platform-level rather than app-level:

- a cold app can miss Android's 5-second startForeground deadline and be killed, giving
  ForegroundServiceDidNotStartInTimeException -> Force finishing activity, i.e. the launcher
  where the scan should have started. The warm-up phase fixes it (a process that has started
  the service once never misses), but the fallback path of restarting the app gives the warmth
  back again, which is why the recorded scan is now triggered from the results screen's top-bar
  "Scan" instead of the intro's button.
- the app also died mid-scan on the emulator, with the launcher on screen afterwards and no
  exception of its own anywhere in logcat. No amount of warming fixes that one.

Neither reproduces on the real test device, where the same 22-photo library scans without
incident, so the take belongs on the phone. The script is the checklist for what it must show.
This commit is contained in:
2026-10-14 11:13:21 +02:00
parent f45e3b79ab
commit 1ce2f2e1f4
4 changed files with 326 additions and 7 deletions
+48 -6
View File
@@ -234,13 +234,55 @@ action except the items marked **[code]**.
| `ScanService.kt` | `ServiceCompat.startForeground(…, ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC)` |
| `ScanService.kt` | `onTimeout()` stops the scan at Android 15's 6-hour `dataSync` cap |
In the Console go to **App content › Foreground service permissions**, select
**Data sync**, and paste:
The declaration page asks for **four** things per type, not one. Go to
**App content › Foreground service permissions**:
> The app finds duplicate photos on the device. The user starts a scan and may leave
> the app while it runs; the foreground service keeps that user-initiated scan alive
> and shows its progress. The scan reads the photo library and writes a small local
> index of file hashes. It performs no network access and transfers no data.
1. **Description** of the app functionality using the type:
> The app finds duplicate photos on the device. The user starts a scan and may leave
> the app while it runs; the foreground service keeps that user-initiated scan alive
> and shows its progress. The scan reads the photo library and writes a small local
> index of file hashes. It performs no network access and transfers no data.
2. **User impact if the task is deferred or interrupted.** A scan is not deferrable work —
the user is watching for a result — so there is no WorkManager/JobScheduler fallback:
> If the scan were deferred, tapping "Start scan" would appear to do nothing and the
> user would have to keep the app open for the work to begin at all. If it were
> interrupted, the user would lose the progress of a whole-library comparison and
> have to start again. Only reads are involved, so an interruption cannot lose or
> corrupt data; the service stops as soon as the scan finishes or is cancelled, and
> it is held only for the duration of the scan.
3. **Use case**: **Local processing: Other** — *"Use for specifically user-initiated work
and not for regular system or server-initiated tasks."* The other `dataSync` choices
are network transfers (backup/restore, upload/download) and *Local processing: Import
or export*, none of which describes a local, user-initiated duplicate scan.
4. **A link to a video** demonstrating the feature — *"The video should demonstrate the
steps the user needs to take in your app in order to trigger the feature."* Record it
with `./tools/record-fgs-demo.sh`, which drives a device through exactly that journey
(empty state → *Start scan* → Home → the notification visible in the shade while the
scan keeps running → results) and writes
`store-assets/fgs-demo/foreground-service-demo.mp4` (git-ignored, like the
screenshots). Put the file where a reviewer can open it without signing in — an
unlisted YouTube link is the usual choice — and paste that link in. Replacing the
video later means resubmitting the declaration.
⚠️ **Record it on a real device.** The capture proved unreliable on this machine's
software-emulated AVD (no KVM), and both failures are platform-level rather than
app-level:
- A **cold** app can miss Android's 5-second `startForeground` deadline and get killed
(`ForegroundServiceDidNotStartInTimeException` → *Force finishing activity*), so the
launcher appears where the scan should have started. The script's warm-up phase gets a
process past that — a process that has already started the service once succeeds — but
- the app also died **mid-scan** on the emulator during testing, and the launcher was on
screen afterwards with no exception from the app in logcat at all. A warmer process
does not fix that one.
Neither failure has been seen on the real test device, where the same 22-photo library
scans without incident. So treat `tools/record-fgs-demo.sh` as the checklist for the
journey it records, and run it against the phone.
The declaration is **per type**: if a new foreground-service type is ever added to
the manifest, it has to be declared here too.