feat: record the FGS video on the real device, and fix what the take exposed
The declaration's video exists now: store-assets/fgs-demo/foreground-service-demo.mp4, recorded on
the test device (Android 12 / MIUI, 2a2ae895) with INSTALL=0 WARMUP=0, and then checked frame by
frame rather than assumed - the intro at 0 s, the scan starting ("Comparing file signatures...
91/102", 8 s), the shade held open for ~6 s with the live notification ("Finding duplicate photos
/ Looking for visually similar photos... / CANCEL", 13-18 s) while the scan kept running behind
it, and the finished results at the end. Hosting it and pasting the link into the declaration is
what remains.
The script grew two flags, both because an earlier attempt got it wrong:
- WARMUP=0 skips the warm-up and keeps the *cold* scan on camera. A warm scan of this library
takes 5.4 s - less than the notification shade takes to open - so the first phone take filmed an
empty shade, convincingly. The warm-up only ever existed for the emulator's FGS deadline
problem, which a real device does not have.
- INSTALL=0 records with the build already on the device, because MIUI does not allow adb
installs: the command hangs on two on-device prompts (Google Play Protect, then MIUI's own
Installieren) and auto-cancels as INSTALL_FAILED_USER_RESTRICTED if they go unanswered.
Also found by running this against a phone instead of an AVD:
- the trigger is chosen by mode - the intro's "Start scan" when the warm-up is skipped, the
results screen's top-bar "Scan" when it is not - and the lookup result is checked rather than
assumed,
- pacing is a variable now, tightened in the WARMUP=0 path, because the phone's cold scan is only
~20 s and the shade has to be open while it runs,
- the library check counts the demo folder rather than the whole device library: on a phone the
rest of the library is the owner's own photos and is supposed to be there,
- the warm-up became a function and does not run at all under WARMUP=0, and a stray reference to
its `attempt` variable (fatal under `set -u`) is gone.
Step 5 of the checklist now carries the recorded recipe, both flags with the reasons, and two
device facts that were invisible until a phone was attached: MIUI fails every synthetic tap with
INJECT_EVENTS until USB debugging is enabled, and toggling that resets the USB connection onto a
new /dev/bus/usb node. The emulator verdict is compressed to what it is - the platform kills the
app on every scan start there (am_crash ForegroundServiceDidNotStartInTimeException, no am_kill),
with QEMU's -icount clock dilation noted as the one thing that ever worked, and only once.
This commit is contained in:
@@ -267,61 +267,61 @@ action except the items marked **[code]**.
|
||||
fits *Other*.
|
||||
|
||||
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.
|
||||
steps the user needs to take in your app in order to trigger the feature."*
|
||||
|
||||
A hand recording is just as valid and needs no tooling at all: every Android 11+ phone
|
||||
has a screen recorder in the quick settings, and the whole video is six steps —
|
||||
**Recorded on the test device** (Android 12 / MIUI, `2a2ae895`) on 2026-10-14 with:
|
||||
|
||||
1. the app on its intro,
|
||||
2. tap *Start scan* — this is the trigger the declaration is about,
|
||||
3. press Home, so the app is no longer on screen,
|
||||
4. pull the notification shade down and let "Scan progress" sit there for a few
|
||||
seconds, with its live counter and *Cancel* button, while the scan keeps running,
|
||||
5. tap the app back into the foreground for the finished results,
|
||||
6. stop the recording and upload it.
|
||||
|
||||
Keep it under two minutes and don't cut mid-way; a reviewer has to be able to follow
|
||||
the trigger and see the service doing its job after the app leaves the screen.
|
||||
⚠️ **Record it on a real device — it cannot be done on a KVM-less emulator.** Every
|
||||
attempt to start a scan on this machine's software-emulated AVD ended the same way, and
|
||||
logcat's events buffer names the cause each time:
|
||||
|
||||
```
|
||||
am_crash: [..., android.app.ForegroundServiceDidNotStartInTimeException,
|
||||
Context.startForegroundService() did not then call
|
||||
Service.startForeground(): .../ScanService]
|
||||
am_proc_died: [...]
|
||||
```bash
|
||||
INSTALL=0 WARMUP=0 ./tools/record-fgs-demo.sh 2a2ae895
|
||||
```
|
||||
|
||||
The platform kills the app and force-finishes the activity, so the launcher appears
|
||||
where the scan should have started. It is not a crash in the app's own code, it is not
|
||||
memory pressure (there is no `am_kill` or low-memory record), and it is not something a
|
||||
warm-up can fix: the last one happened on a process that had been alive and idle for over
|
||||
a minute. Software emulation is simply too slow to get from `startForegroundService()` to
|
||||
`startForeground()` inside Android's budget — while on the phone this app scans a
|
||||
5,363-photo library in 20 s with the notification up throughout.
|
||||
The output is `store-assets/fgs-demo/foreground-service-demo.mp4` (git-ignored, like the
|
||||
screenshots) with a checkpoint PNG per milestone beside it. It was checked frame by frame
|
||||
afterwards rather than assumed: the intro at 0 s, the scan starting (*"Comparing file
|
||||
signatures… 91/102"*, 8 s), the shade held open for ~6 s with the live notification
|
||||
(*"Finding duplicate photos / Looking for visually similar photos… / CANCEL"*, 13–18 s)
|
||||
while the scan kept running behind it, and the finished results (*17 duplicate groups ·
|
||||
54 of 5,385 photos*) at the end. Host the file where a reviewer can open it without
|
||||
signing in — an unlisted YouTube link is the usual choice — and paste that link into the
|
||||
declaration. Replacing the video later means resubmitting it.
|
||||
|
||||
One emulator-side lever is worth knowing, because it is the only thing that ever got this
|
||||
app's foreground service started on this machine: QEMU's virtual instruction counter
|
||||
dilates the **guest** clock, so Android's deadline is measured against time that passes
|
||||
more slowly than the host's. Booting with `-qemu -icount shift=1,sleep=off` made the guest
|
||||
clock run **2.30× slow** (measured: 30.9 s of host time to 13.4 s of guest uptime), and on
|
||||
that boot a scan start *worked* — the app's notification appeared five seconds after the
|
||||
tap, with no `am_crash`, the only success recorded here. It then stopped working: later
|
||||
attempts produced neither a notification nor a crash, which means the tap never reached
|
||||
the app, or `ScanService.start()` silently swallowed a
|
||||
`ForegroundServiceStartNotAllowedException` (it catches `IllegalStateException` and does
|
||||
nothing with it, so that path leaves no trace in logcat at all — a diagnosability gap
|
||||
worth a log line if this is ever chased further). So treat `-icount` as a lead to retry
|
||||
with patience rather than a recipe to rely on, and use `SETTLE_SECONDS` / `WARM_SECONDS`
|
||||
to scale this script's waits for a dilated clock.
|
||||
Both flags exist because this take found two traps worth knowing before a re-shoot:
|
||||
|
||||
- **`WARMUP=0` keeps the *cold* scan on camera.** A warm scan of this library takes
|
||||
**5.4 s**, which is less than the notification shade takes to open — so the first take
|
||||
filmed an empty shade, convincingly. The cold first scan (~20 s) is what gives the
|
||||
shade time to appear while the notification is still live, and reinstalling the app
|
||||
restores that window.
|
||||
- **`INSTALL=0` skips the install**, because MIUI does not let `adb` install anything:
|
||||
the command **hangs** waiting for two prompts on the device — Google Play Protect
|
||||
(*"App zum Sicherheitsscan senden?"*) and then MIUI's own *Installieren* — and
|
||||
auto-cancels as `INSTALL_FAILED_USER_RESTRICTED` if they go unanswered. The simplest
|
||||
route is to install once by hand and then record with `INSTALL=0`.
|
||||
|
||||
A hand recording is just as valid and needs no tooling: every Android 11+ phone has a
|
||||
screen recorder in the quick settings, and the whole video is six steps — the intro, tap
|
||||
*Start scan* (the trigger the declaration asks to see), press Home, pull the shade down
|
||||
and leave the notification sitting there while the scan runs, tap the app back for the
|
||||
results, stop. Under two minutes and uncut: a reviewer has to be able to follow the
|
||||
trigger and see the service still working once the app has left the screen.
|
||||
⚠️ **MIUI blocks synthetic input until the matching developer option is on.** Every
|
||||
`adb shell input tap` on this phone fails with *"Injecting to another application
|
||||
requires INJECT_EVENTS permission"* — which is why the project's earlier captures were all
|
||||
taken by hand. With **USB debugging** enabled (and the security-settings toggle that comes
|
||||
with it) the block lifts, `tools/record-fgs-demo.sh` drives the phone end to end and
|
||||
nothing is needed from a human. Toggling it resets the USB connection, though: the device
|
||||
comes back on a **new** `/dev/bus/usb` node, which has to be made readable before `adb`
|
||||
sees it again.
|
||||
|
||||
⚠️ **It cannot be recorded on a KVM-less emulator**, and the failure is the platform's,
|
||||
not the app's: every scan start there ends in
|
||||
`am_crash … ForegroundServiceDidNotStartInTimeException` → *Force finishing activity*,
|
||||
with no `am_kill` or low-memory record, because software emulation is too slow to get
|
||||
from `startForegroundService()` to `startForeground()` inside Android's budget. QEMU's
|
||||
virtual instruction counter dilates the guest clock and made it work exactly once
|
||||
(`-qemu -icount shift=1,sleep=off`, guest clock measured 2.30× slow) but not
|
||||
reproducibly, so that is a lead rather than a recipe; `SETTLE_SECONDS` / `WARM_SECONDS`
|
||||
scale this script's waits if it is ever retried.
|
||||
|
||||
The declaration is **per type**: if a new foreground-service type is ever added to
|
||||
the manifest, it has to be declared here too.
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ Legend: ✅ done · 🚧 in progress · ⏳ not started · 💡 candidate (propo
|
||||
|---|---|
|
||||
| ~~Large-library scan stall~~ ✅ **fixed — verified on device** | The 5,363-photo library used to freeze at *“74 / 100 · Comparing file signatures…”* (a **blocked syscall**: zero CPU, workers sleeping). `BoundedCall` now abandons a stuck provider call **and** trips a stall breaker after three consecutive timeouts. **Verified on the real test device (Android 12 / MIUI): the scan completes — 20.2 s cold, 11.0 s warm, 15 groups, 6 unreadable — where it used to hang.** Covered by `BoundedCallTest` and `ScanEngineEndToEndTest` |
|
||||
| ~~Unreleased `BoundedCall` fix~~ ✅ | The provider-call deadline shipped in **1.0.1** (`versionCode` 2) |
|
||||
| **Play Console: foreground-service declaration** | Declaring the `dataSync` type (App content › Foreground service permissions) is release-blocking. **[code]** the manifest already declares it, and [PLAY_RELEASE_CHECKLIST](PLAY_RELEASE_CHECKLIST.md) step 5 now carries all four things the form asks for: the description, the user impact if the scan is deferred or interrupted, the use case (**Local processing: Other** — the only `dataSync` option that fits a local, user-initiated scan) and the **video link** Play requires. `tools/record-fgs-demo.sh` records that video — empty state → *Start scan* → Home → the notification in the shade while the scan continues → results — into the git-ignored `store-assets/fgs-demo/`; hosting it and pasting the link is still a manual step. The take itself belongs on a real device: on this machine's KVM-less AVD every scan start ended in `am_crash … ForegroundServiceDidNotStartInTimeException` → *Force finishing activity*, with no `am_kill` or low-memory record, i.e. software emulation is too slow to reach `startForeground()` inside Android's budget — and that, not a mysterious "mid-scan death", is what an earlier note here was misreading (the script's old check matched the service *record*, which exists before `startForeground()` is called, so it reported success on runs that were already dying). The evidence that this is the emulator and not the app is the phone's own history: 5,363 photos scanned in 20 s with the notification up throughout |
|
||||
| **Play Console: foreground-service declaration** | Declaring the `dataSync` type (App content › Foreground service permissions) is release-blocking. **[code]** the manifest already declares it, [PLAY_RELEASE_CHECKLIST](PLAY_RELEASE_CHECKLIST.md) step 5 carries all four things the form asks for (description, defer/interrupt impact, use case **Local processing: Other**, video link), and the **video now exists**: recorded on the test device on 2026-10-14 with `INSTALL=0 WARMUP=0 tools/record-fgs-demo.sh`, then verified frame by frame rather than assumed — intro at 0 s, the scan starting, **~6 s of the shade with the live "Finding duplicate photos / Looking for visually similar photos… / CANCEL" notification while the scan kept running**, and the results at the end. Only hosting it (an unlisted YouTube link) and pasting that link remain. Two details of the take are recorded in step 5 because they each cost an attempt: a *warm* scan is 5.4 s, shorter than the shade takes to open, so `WARMUP=0` films the cold one; and MIUI refuses `adb install` outright, so `INSTALL=0` records with the build already on the device. On a KVM-less emulator the same video cannot be made at all: every scan start dies as `am_crash … ForegroundServiceDidNotStartInTimeException` → *Force finishing activity* with no `am_kill`, i.e. emulation is too slow to reach `startForeground()` inside Android's budget (QEMU's `-icount` clock dilation made it work once, not reproducibly) |
|
||||
| **Remaining Play paperwork** | Console actions only, no code: the **first upload + Play App Signing enrolment**, the **hosted privacy policy URL** (the working copy is now published to Gitea as [`fishfacegit/foto-duplicate-finder`](https://gitea.softwareapp-hb.de/fishfacegit/foto-duplicate-finder), so the policy renders at a public, login-free URL and only the Console paste is left), **Data safety**, the **Photo/video permissions** declaration, the **content rating / audience / app access / ads / export** questions, and the **listing copy + the three screenshot sets** (phone, 7-inch and 10-inch, all captured), plus the **paid-app setup**: a payments profile with tax rates, the €2.99 price and the 14-day €1.49 launch sale on *Products > App pricing* — the price must be set before the first upload, because free → paid is not possible afterwards (M9). Keystore, `versionCode`, listing copy, graphics, permission audit and the signed `.aab` (`sha256 f8e6a29a…`) are all in place; rollout goes internal → closed → production. Step-by-step: [PLAY_RELEASE_CHECKLIST](PLAY_RELEASE_CHECKLIST.md) |
|
||||
| ~~Store screenshots need one re-shoot~~ ✅ **done — verified on device** | The old captures showed the *Photo access needed* banner (an app-op is not a grant) and `03-results.png` showed the developer's own library. After a real grant the set was re-shot over the generated demo library: **01-empty** (no tap needed — results and selection live in `ScannerViewModel`, so force-stop + relaunch returns to the empty state with the grant still in place), **02-scanning** (`5353 / 5385`), **03-results** (**17 groups, 54 of 5385 photos, 83.5 MB reclaimable**; the headline group's 43.1 MB out-ranks the device's own 39.8 MB, so no real photo is in frame) and **04-options**. Captures are taken by watching for UI **text** changes, because MIUI refuses synthetic taps (`INJECT_EVENTS`). The demo library is deleted from the test device afterwards — MediaStore **5,386 → 5,364** rows, and the 22 stale rows it leaves in the hash index are pruned by `PhotoIndexStore.pruneTo` on the next scan. Deliberately not captured: the staged state and the system confirmation dialog, because staging clears the demo group and would put real photos back in frame. Details: [STORE_LISTING](STORE_LISTING.md) |
|
||||
| ~~Tablet store screenshots outstanding~~ ✅ **done — captured on AVDs** | Play has a **7-inch tablet** and a **10-inch tablet** screenshot slot of its own (asking for **≥ 4** screenshots on large screens). Both are now captured on AOSP Android 12 AVDs — `Nexus 7 2013` (1200×1920, `sw600dp`) and `medium_tablet` (1600×2560, `sw800dp`) — because this machine exposes no `/dev/kvm`: they ran in **pure software emulation** (`-accel off -no-window -gpu swiftshader_indirect`), ≈3 min to boot, with `hide_error_dialogs` on so SystemUI's ANR dialog could not sit in frame. Four screens per tablet over the same generated demo library: empty, scanning (20 / 22), results (**2 groups · 16 of 22 photos · 43.7 MB reclaimable**, 39.7 s / 53.1 s) and Options. `tools/make-store-screenshots.sh` is form-factor aware (`FORM_FACTOR=phone\|tablet-7in\|tablet-10in`) and the tablet form factors crop **0 px** of chrome, because a tablet frame is already inside Play's 2:1 cap. Recipe: [STORE_LISTING](STORE_LISTING.md) |
|
||||
|
||||
+80
-33
@@ -145,7 +145,14 @@ checkpoint() { # <label> — one PNG per milestone, so the video can be verified
|
||||
# --------------------------------------------------------------- device and app setup
|
||||
|
||||
say "Device $serial"
|
||||
"${adb[@]}" install -r "$apk" | tail -1
|
||||
if [ "${INSTALL:-1}" = "1" ]; then
|
||||
"${adb[@]}" install -r "$apk" | tail -1
|
||||
else
|
||||
# MIUI refuses shell-initiated installs unless "Install via USB" is on, and answers one of them
|
||||
# with two dialogs (Play Protect, then its own installer) that a script cannot see. So the APK
|
||||
# comes from the device itself: INSTALL=0 records with whatever build is already installed.
|
||||
printf ' install skipped (INSTALL=0); recording with the build already on the device\n'
|
||||
fi
|
||||
# The video is about the foreground service, not about a permission prompt, so grant photo access
|
||||
# up front - the "Photo access needed" banner would otherwise be the first thing a reviewer sees.
|
||||
"${adb[@]}" shell pm grant "$pkg" android.permission.READ_EXTERNAL_STORAGE 2>/dev/null || true
|
||||
@@ -161,14 +168,15 @@ sleep 2
|
||||
"${adb[@]}" shell content call --uri content://media --method scan_file --arg /sdcard/DCIM/foto-dup-demo >/dev/null
|
||||
sleep 2
|
||||
local_files="$(find "$repo/demo-photos/foto-dup-demo" -name '*.jpg' | wc -l)"
|
||||
# `content query` prints "No result found." rather than nothing when the table is empty, so count
|
||||
# the rows instead of the lines of output.
|
||||
indexed="$("${adb[@]}" shell content query --uri content://media/external/images/media --projection _id 2>/dev/null \
|
||||
| grep -c '^Row:' || true)"
|
||||
# Count the demo folder only, not the whole library: on a real phone the rest of the library is the
|
||||
# owner's own photos and is meant to be there. What matters is that the demo group out-ranks it,
|
||||
# which is what tools/make-demo-photos.py's --target-mb is for.
|
||||
indexed="$("${adb[@]}" shell content query --uri content://media/external/images/media --projection relative_path 2>/dev/null \
|
||||
| grep -c 'foto-dup-demo' || true)"
|
||||
indexed="${indexed:-0}"
|
||||
printf ' %s images indexed, %s in the generated library\n' "$indexed" "$local_files"
|
||||
if [ "$indexed" -ne "$local_files" ]; then
|
||||
echo "warn: device library does not match the generated one - the results screen will not match the other captures" >&2
|
||||
printf ' %s demo photos indexed, %s in the generated library\n' "$indexed" "$local_files"
|
||||
if [ "$indexed" -lt "$local_files" ]; then
|
||||
echo "warn: fewer demo photos are indexed than were generated - the results capture may not show the demo group first" >&2
|
||||
fi
|
||||
|
||||
say "Clearing stale notifications, so the shade shows only this app"
|
||||
@@ -196,14 +204,15 @@ sleep 2
|
||||
# process has been alive for a while (classes loaded, JIT warm) and the emulator is not still doing
|
||||
# first-boot work. So warm the app up first, and only start recording once a real scan has been
|
||||
# confirmed - by the notification, for the reason given at fgs_started().
|
||||
if [ "${SETTLE_SECONDS:-150}" != "0" ]; then
|
||||
warm_up() {
|
||||
if [ "${SETTLE_SECONDS:-150}" != "0" ]; then
|
||||
say "Letting the emulator settle (first-boot work competes for the same CPU)"
|
||||
sleep "${SETTLE_SECONDS:-150}"
|
||||
fi
|
||||
fi
|
||||
|
||||
say "Warming the app up (a cold process misses the FGS deadline - see the note in this script)"
|
||||
attempt=0
|
||||
while [ "$attempt" -lt 4 ]; do
|
||||
say "Warming the app up (a cold process misses the FGS deadline - see the note in this script)"
|
||||
attempt=0
|
||||
while [ "$attempt" -lt 4 ]; do
|
||||
attempt=$(( attempt + 1 ))
|
||||
"${adb[@]}" shell am force-stop "$pkg"
|
||||
"${adb[@]}" shell am start -n "$activity" >/dev/null
|
||||
@@ -242,23 +251,56 @@ while [ "$attempt" -lt 4 ]; do
|
||||
fi
|
||||
printf ' attempt %s: the service never started\n' "$attempt"
|
||||
"${adb[@]}" logcat -b events -d -t 400 2>/dev/null | grep -m1 'am_crash' || true
|
||||
done
|
||||
if [ "$started" != yes ]; then
|
||||
done
|
||||
if [ "$started" != yes ]; then
|
||||
echo "error: no scan could be started on this emulator - record the video on a real device" >&2
|
||||
exit 4
|
||||
fi
|
||||
}
|
||||
|
||||
# A real device needs none of that warm-up, and skipping it is what keeps the *cold* first scan -
|
||||
# ~20 s over 5,385 photos - as the one on camera. That is not a nicety: a warm scan on this phone
|
||||
# finishes in about five seconds, which is less than the notification shade takes to render, so the
|
||||
# take would film an empty shade. Hence WARMUP=0 for a real device, and the trigger becomes the
|
||||
# intro's own button rather than the results screen's "Scan".
|
||||
trigger_text='Scan'
|
||||
trigger_exact=1
|
||||
# Pacing: the emulator needs a long, dump-free window after the tap because the FGS deadline is the
|
||||
# thing at risk there. A real device does not - the notification is up within a second - and it needs
|
||||
# the opposite, because its cold scan is only ~20 s and the shade has to be open *while it runs*.
|
||||
post_tap_wait=8
|
||||
leave_wait=3
|
||||
shade_wait=6
|
||||
if [ "${WARMUP:-1}" = "0" ]; then
|
||||
say "Warm-up skipped (WARMUP=0): the cold first scan is the one being recorded"
|
||||
post_tap_wait=2
|
||||
leave_wait=1
|
||||
shade_wait=5
|
||||
"${adb[@]}" shell am force-stop "$pkg"
|
||||
"${adb[@]}" shell am start -n "$activity" >/dev/null
|
||||
if ! wait_for 'Start scan' 120 >/dev/null; then
|
||||
echo "error: the empty state never appeared - right APK installed? photo access granted?" >&2
|
||||
exit 3
|
||||
fi
|
||||
trigger_text='Start scan'
|
||||
trigger_exact=0
|
||||
else
|
||||
warm_up
|
||||
fi
|
||||
|
||||
# Let the warm-up scan run to *completion* instead of cancelling it: cancelling does not lead back to
|
||||
# the intro, and restarting the app to get there throws away exactly the warmth that keeps the
|
||||
# deadline met - which is how the first recordings ended up with the platform killing the app on
|
||||
# camera. So the recorded scan is triggered the way a returning user triggers one: the top bar's
|
||||
# "Scan", on the results screen.
|
||||
say "Letting the warm-up scan finish"
|
||||
if ! wait_for 'duplicate group' 300 >/dev/null; then
|
||||
if [ "${WARMUP:-1}" != "0" ]; then
|
||||
# Let the warm-up scan run to *completion* instead of cancelling it: cancelling does not lead
|
||||
# back to the intro, and restarting the app to get there throws away exactly the warmth that
|
||||
# keeps the deadline met - which is how the first recordings ended up with the platform killing
|
||||
# the app on camera. So the recorded scan is triggered the way a returning user triggers one:
|
||||
# the top bar's "Scan", on the results screen.
|
||||
say "Letting the warm-up scan finish"
|
||||
if ! wait_for 'duplicate group' 300 >/dev/null; then
|
||||
wait_for 'No duplicates found' 20 >/dev/null \
|
||||
|| echo "warn: the warm-up scan never reached a result screen" >&2
|
||||
fi
|
||||
checkpoint 0-warmup-results
|
||||
fi
|
||||
checkpoint 0-warmup-results
|
||||
|
||||
say "Recording (screenrecord, at most ${recording_seconds}s)"
|
||||
"${adb[@]}" shell rm -f "$remote"
|
||||
@@ -268,30 +310,35 @@ say "Recording (screenrecord, at most ${recording_seconds}s)"
|
||||
recorder_pid=$!
|
||||
sleep 3
|
||||
|
||||
# The trigger. This is the step the declaration video exists to show. Exact match, because the
|
||||
# results card's "Scanned in 39.7 s" also contains "Scan".
|
||||
if ! scan_button="$(wait_for_exact 'Scan' 30)"; then
|
||||
echo "error: no Scan button on screen to trigger the scan with" >&2
|
||||
# The trigger. This is the step the declaration video exists to show.
|
||||
if [ "$trigger_exact" = 1 ]; then
|
||||
# Exact match, because the results card's "Scanned in 5.4 s" also contains "Scan".
|
||||
scan_button="$(wait_for_exact "$trigger_text" 30)" || true
|
||||
else
|
||||
scan_button="$(wait_for "$trigger_text" 30)" || true
|
||||
fi
|
||||
if [ -z "$scan_button" ]; then
|
||||
echo "error: no '$trigger_text' button on screen to trigger the scan with" >&2
|
||||
exit 3
|
||||
fi
|
||||
printf ' Scan (top bar) at %s\n' "$scan_button"
|
||||
printf ' %s at %s\n' "$trigger_text" "$scan_button"
|
||||
"${adb[@]}" shell input tap $scan_button
|
||||
sleep 8 # deliberately dump-free: this is the deadline window
|
||||
sleep "$post_tap_wait" # deliberately dump-free: this is the deadline window
|
||||
if ! fgs_started; then
|
||||
printf ' the service did not start; the events buffer said:\n'
|
||||
"${adb[@]}" logcat -b events -d -t 400 2>/dev/null | grep -m1 'am_crash' || true
|
||||
echo "warn: no scan running - the video will not show the service" >&2
|
||||
else
|
||||
checkpoint 1-scanning
|
||||
printf ' the service started (notification posted) on a process warmed by attempt %s\n' "$attempt"
|
||||
printf ' the service started (notification posted)\n'
|
||||
fi
|
||||
|
||||
# Away from the app: from here on the service is what keeps the scan alive.
|
||||
sleep 3
|
||||
sleep "$leave_wait"
|
||||
"${adb[@]}" shell input keyevent KEYCODE_HOME
|
||||
sleep 3
|
||||
sleep 2
|
||||
"${adb[@]}" shell cmd statusbar expand-notifications
|
||||
sleep 6
|
||||
sleep "$shade_wait"
|
||||
checkpoint 2-notification
|
||||
printf ' notifications posted by the app: %s\n' "$(notifications_from_app)"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user