diff --git a/AGENTS.md b/AGENTS.md index f3909d9..5c17fc8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -120,6 +120,7 @@ comment. `app/src/test/java/…/ProjectInvariantsTest.kt` holds the source-level | `docs/PLAY_RELEASE_CHECKLIST.md` | Release only — work it in order | | `docs/RELEASE_NOTES.md` | The Play **What's new** copy, at 500 characters, and the longer announcement | | `docs/PERMISSION_VIDEO.md` | The declaration's 23-second video: what it shows, and how to re-shoot it | +| `docs/FGS_VIDEO.md` | The foreground-service video: the local copy, what was cut, and how to re-shoot it | | `docs/COMPETITIVE_ANALYSIS.md`, `docs/MONETISATION.md`, `docs/STORE_LISTING.md` | Product context, pricing, and length-checked listing copy | | `PRIVACY.md` | The policy text the store listing points at | diff --git a/CHANGELOG.md b/CHANGELOG.md index 1cda9bd..32fc821 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -48,6 +48,19 @@ upload needs a **higher `versionCode`** than every previous upload. package-level `appops set … deny` leaves the UID mode allowed, so only `appops set --uid … deny` brings the prompt back. +- **The `dataSync` foreground-service declaration is answered before the Console asks.** Section 5 of + [PLAY_RELEASE_CHECKLIST](docs/PLAY_RELEASE_CHECKLIST.md) now records the + `FOREGROUND_SERVICE_DATA_SYNC` answers: the use case is **Local processing → Importing, exporting** + (Play's own example is moving files from an SD card), the **482-character** functionality + description, and the deferred and interrupted impact. Every `OperationKind` is local — `COPY`, + `MOVE`, `DELETE`, `COMPRESS`, `EXTRACT` — and the app has no `INTERNET` permission, so network + processing, backup/restore and media transcoding are explicitly not ticked. It also records that + the required video is **not** the All files access one: [FGS_VIDEO](docs/FGS_VIDEO.md) holds an + **18.0 s** captioned take of the local copy — selection, Copy, Paste here, the notification with + its progress bar, and the completed folder — cut from a 35.2 s raw recording to remove the two + waits, with every filename synthetic. The traps are written down too: no Up after Copy, and open + the shade only once the notification exists. + - **A dot marks what an operation just produced.** Copy something, extract an archive, rename a file or make a folder, and the rows it created carry a small dot until you open that folder again — so "what did that 4 GB copy do?" has an answer, including when the copy went to a folder you were not looking at. The mark lives in diff --git a/docs/FGS_VIDEO.md b/docs/FGS_VIDEO.md new file mode 100644 index 0000000..005873d --- /dev/null +++ b/docs/FGS_VIDEO.md @@ -0,0 +1,136 @@ +# The foreground-service video + +The Play Console's foreground-service declaration for `FOREGROUND_SERVICE_DATA_SYNC` requires a video +of the user *triggering* the local import/export, not just a description. This is that video, and the +record of how it was made. The All files access declaration has its own video and page: +[PERMISSION_VIDEO](PERMISSION_VIDEO.md). + +## The file + +| file | length | what it is | +|---|---|---| +| `store-assets/video/fgs-import-export.mp4` | **18.0 s** | The submission copy: 1080×2400, H.264, **no audio**, English captions burned in | +| `store-assets/video/fgs-import-export-uncaptioned.mp4` | 16.8 s | The same cut without captions, for a voice-over | +| `store-assets/video/fgs-import-export.srt` | — | The five captions and their timings | + +**The file is not a link.** Upload it to YouTube **Unlisted**, or Drive shared with "anyone with the +link", and paste that URL into the foreground-service declaration. A private upload cannot be +reviewed. + +## What it shows + +| time | on screen | what it proves | +|---|---|---| +| 0:00 | `AADemo`, with `Source` selected and **Copy** in the action bar | The user starts a local import/export | +| 0:03 | `Target`, then the overflow's **Paste here** | The user chooses the local destination and triggers it | +| 0:10 | The notification shade: **Copying files** with a moving progress bar | The `dataSync` foreground service is doing the work and reporting it | +| 0:12 | `Target` with `Source` and the snackbar **Copied 121 items** | The operation completed at the destination | + +The captions, in order: + +1. *Copy a 1.8 GB folder to another folder* +2. *Paste here starts a local copy* +3. *The copy runs in the dataSync service* +4. *The ongoing notification reports progress* +5. *Done: the folder is at the destination* + +## What was cut, and why + +The raw take was **35.2 s**; the published copy is three pieces of it: + +| kept | what it is | +|---|---| +| 0–10 s | Select `Source`, Copy, open `Target`, Paste here | +| 17–19.5 s | The shade with **Copying files** and its progress bar | +| 22–27 s | The completed `Target` and its snackbar | + +The two gaps are elapsed time, not evidence: the wait between Paste and the notification appearing, +and the wait between the shade closing and the listing refreshing. **The app's target listing stays +empty until the copy finishes and then refreshes itself**, so the empty frames are the operation in +progress, not a failure. The notification and the result are from the same take. + +Nothing else is edited. No home screen, launcher or library view appears — the recording starts inside +`AADemo`. The shade was cleared of the phone's weather and Play cards before the take; what remains +besides the app's notification is Android's generic USB debugging/transfer pair. The quick-settings +panel shows only icons, battery and data totals; the Wi-Fi name exists in the accessibility tree but +is not drawn on screen. + +Every filename is synthetic: the source is `AADemo/Source/file001.bin` … `file120.bin`, made from +`/dev/zero`, and the destination is `AADemo/Target`. + +## Re-shooting it + +Create the fixture first — 120 files of 16 MB, so the copy lasts long enough to read the notification: + +```bash +adb shell 'rm -rf /sdcard/AADemo/Target; mkdir -p /sdcard/AADemo/Source /sdcard/AADemo/Target +for i in $(seq -w 1 120); do + dd if=/dev/zero of=/sdcard/AADemo/Source/file$i.bin bs=1M count=16 2>/dev/null +done' +``` + +Clear the notification shade before recording: open it, tap **Clear all**, and check with +`adb shell cmd notification list`. The phone's own weather, Play and messaging cards must not appear +in a video that will be published; USB debugging/transfer cards are generic and may stay. + +Open the app on **Internal storage → AADemo**, long-press `Source`, and verify the bar says +**1 item** with **Copy** before recording. Then: + +```bash +adb shell rm -f /sdcard/fgs.mp4 +adb shell screenrecord --size 1080x2400 --bit-rate 8000000 --time-limit 50 /sdcard/fgs.mp4 & +sleep 1.5 +adb shell input tap 231 2155 # Copy in the selection bar +sleep 1.5 +adb shell input tap 540 644 # Target — already inside AADemo +sleep 2 +adb shell input tap 1003 181 # overflow +sleep 1.5 +adb shell input tap 864 335 # Paste here +# The notification exists only after Paste. Wait for it, or the shade shows only USB cards. +for i in $(seq 1 16); do + adb shell cmd notification list | grep -q 'de.softwareapp_hb.filemanager' && break + sleep 0.5 +done +adb shell cmd statusbar expand-notifications +sleep 4 +adb shell cmd statusbar collapse +sleep 18 # the copy must finish and the listing refresh +adb shell pkill -INT screenrecord +adb pull /sdcard/fgs.mp4 +``` + +Cut the two waits, keeping the trigger, the notification and the result: + +```bash +ffmpeg -y -i fgs.mp4 -filter_complex \ + "[0:v]trim=0:10,setpts=PTS-STARTPTS[a];[0:v]trim=17:19.5,setpts=PTS-STARTPTS[b];[0:v]trim=22:27,setpts=PTS-STARTPTS[c];[a][b][c]concat=n=3:v=1[v]" \ + -map "[v]" -c:v libx264 -crf 20 -pix_fmt yuv420p -an fgs-cut.mp4 +``` + +The times are this take's; check the frames before reusing them. + +Two traps cost takes here: + +- **Do not press Up after Copy.** The selection is made *in* `AADemo`; Copy clears the bar and + `Target` is the next row. An extra Up goes to the card root, the following taps open `AADemo` + again, and Paste then targets `AADemo` — which already holds `Source`, so the app correctly asks + **“Items already exist here”**. The first attempt did exactly that. +- **Open the shade only once the notification exists.** Opening it straight after Paste can beat the + service's first post and shows nothing but Android's USB cards — a video with the trigger and the + result but no evidence of the service. The `cmd notification list` loop above is the guard. + +Also expect the target listing to stay **empty** until the copy completes and the ViewModel reloads +it. That is the operation still running, not a missed Paste; `du -sh /sdcard/AADemo/Target` is the +independent check. + +The captions are five PNG overlays at **y = 1900**, not the 2130 used by the all-files video: the +selection bar sits at 2133–2177 on this phone, and a caption there hides the Copy button — the very +action the video exists to show. + +## Uploading it + +Upload `store-assets/video/fgs-import-export.mp4` to YouTube **Unlisted** or Drive with link sharing, +and paste the URL into the **foreground-service** declaration. This is a different field from the +All files access video: the permission video shows browsing a whole volume, this one shows the local +copy that the `dataSync` service keeps alive. diff --git a/docs/PLAY_RELEASE_CHECKLIST.md b/docs/PLAY_RELEASE_CHECKLIST.md index 60cb482..939ab16 100644 --- a/docs/PLAY_RELEASE_CHECKLIST.md +++ b/docs/PLAY_RELEASE_CHECKLIST.md @@ -174,6 +174,43 @@ honesty, not the justification. video field. The repository file is not a link, and a private video cannot be reviewed. What it shows, why the system UI is German, and how to re-shoot it: [PERMISSION_VIDEO](PERMISSION_VIDEO.md). +### The foreground-service declaration (`dataSync`) + +`FileOpService` is a **`dataSync`** foreground service: it keeps a copy, move, delete (into the trash), +compress or extract alive while the user is in another app, and mirrors the engine's progress into an +ongoing, silent notification. It runs no network code and the app has no `INTERNET` permission. Play +asks about this separately from All files access. + +- **Use case: Local processing → Importing, exporting.** Play's own chart describes the use case as + *"Includes migration of files from SD card. Show the user manually triggering an import or export + of files locally (for example, as moving files from an SD card)"* — which is what **Paste** and + **Copy to… / Move to…** do. Do **not** tick network processing, backing up/restoring, media + transcoding or "Other tasks": every `OperationKind` is local — `COPY`, `MOVE`, `DELETE`, `COMPRESS`, + `EXTRACT` — and none of them touches a network. + +- **Functionality description (482 / 500 characters, measured):** + + ``` + Plain Files is a file manager. Its dataSync service runs user-initiated local file operations: copying and moving files and folders between folders, internal storage, SD cards and USB drives; deleting into a 24-hour trash; creating ZIPs; extracting ZIP, TAR, GZ, BZ2, XZ and 7z archives. It keeps a long operation alive while the user switches apps and posts an ongoing, silent progress notification until the work ends. All operations are local; the app has no INTERNET permission. + ``` + +- **If the task is deferred** (*"does not start immediately"*): *"If deferred, a copy, move, delete, + compress or extract the user just confirmed would not start while Plain Files is in the background: + no progress notification and no visible change until the app returns. A large operation could look + as if it had silently failed, and the user might start it again."* + +- **If the task is interrupted** (*"paused and/or restarted"*): *"If interrupted, the work stops. + Items already completed remain, but a file being copied or extracted can be left partly written at + the destination, and the user has to start the operation again. Nothing is overwritten without + asking, and deletes are staged in the 24-hour trash, so completed deletes remain recoverable."* + +- [ ] **Upload the foreground-service video.** `store-assets/video/fgs-import-export.mp4` (**18.0 s**, + captions burned in) to YouTube **Unlisted** or Drive with link sharing, then paste that URL into + the foreground-service declaration. It shows the selection, **Copy**, **Paste here**, the + **Copying files** notification with its progress bar, and the completed folder. It is a + different field from the All files access video. What was cut, the privacy checks, and how to + re-shoot it: [FGS_VIDEO](FGS_VIDEO.md). + **Verify the shipped permission set** after every release build: ```bash diff --git a/store-assets/video/fgs-import-export-uncaptioned.mp4 b/store-assets/video/fgs-import-export-uncaptioned.mp4 new file mode 100644 index 0000000..340255e Binary files /dev/null and b/store-assets/video/fgs-import-export-uncaptioned.mp4 differ diff --git a/store-assets/video/fgs-import-export.mp4 b/store-assets/video/fgs-import-export.mp4 new file mode 100644 index 0000000..d44cdd9 Binary files /dev/null and b/store-assets/video/fgs-import-export.mp4 differ diff --git a/store-assets/video/fgs-import-export.srt b/store-assets/video/fgs-import-export.srt new file mode 100644 index 0000000..9cfee40 --- /dev/null +++ b/store-assets/video/fgs-import-export.srt @@ -0,0 +1,19 @@ +1 +00:00:00,200 --> 00:00:03,000 +Copy a 1.8 GB folder to another folder + +2 +00:00:03,000 --> 00:00:06,500 +Paste here starts a local copy + +3 +00:00:06,500 --> 00:00:10,000 +The copy runs in the dataSync service + +4 +00:00:10,000 --> 00:00:12,500 +The ongoing notification reports progress + +5 +00:00:12,500 --> 00:00:17,900 +Done: the folder is at the destination