Write the release notes, the permission answers, and the video
Play asks three separate things before a file manager may keep All files access: the What's new copy, a justification for why SAF and MediaStore cannot carry it, and a short video of the permission in use. Writing them exposed two stale claims and one privacy problem, so those are fixed here rather than repeated. - docs/RELEASE_NOTES.md holds the Console field at 450/500 characters, measured, and the longer announcement. The first release's copy leads with the claim no competitor makes (no ads, no INTERNET permission) and then proves the app is still a file manager. - docs/PLAY_RELEASE_CHECKLIST.md section 5 holds the declaration answer at 490/500 characters. It argues from what the Storage Access Framework and MediaStore cannot do - one picked tree, no volume enumeration or whole-device hierarchy, no arbitrary non-media files - and never from performance, which Google's wording rejects. Both APIs are named as already used where they can be, so the argument is not that they are useless. - docs/PERMISSION_VIDEO.md and store-assets/video hold a 19.7 s captioned video recorded on the release build. The home-screen segment is cut because its library counts are the owner's real totals; the published copy resumes on the internal-storage listing. Also recorded: the emulator captures one frame in three seconds, and only appops set --uid ... deny brings the prompt back after Settings has granted the app-op. - README and ROADMAP caught up with the build: the status stopped at M15, and the trash limitation still said there was no Empty trash and that expiry waited for the next delete, both changed in M19. Verified: ./tools/verify.sh green, the code map current, the Play field measured at 450 characters and the form answer at 490, and the video decoded as H.264 1080x2400, 19.666 s, no audio, with frames checked at the cut and the end state.
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
# The permission-use video
|
||||
|
||||
The Permissions Declaration Form asks for a link to a short video that shows
|
||||
`MANAGE_EXTERNAL_STORAGE` being used — 30 seconds or fewer, with a voice-over or captions where the
|
||||
UI does not make the use obvious. This file is the record of the video that answers it, and the
|
||||
instructions for making another.
|
||||
|
||||
## The file
|
||||
|
||||
| file | length | what it is |
|
||||
|---|---|---|
|
||||
| `store-assets/video/permission-use.mp4` | **19.7 s** | The submission copy: 1080×2400, H.264, **no audio**, English captions burned in. The home-screen segment is deliberately cut (below) |
|
||||
| `store-assets/video/permission-use-uncaptioned.mp4` | 19.7 s | The same cut without captions, for a voice-over instead |
|
||||
| `store-assets/video/permission-use.srt` | — | The caption text and timings, for a re-upload or translation |
|
||||
|
||||
**The file is not a link.** Play asks for a URL, so upload the MP4 — YouTube **Unlisted**, or Google
|
||||
Drive shared with "anyone with the link" — and paste that URL into the form. A private video or a
|
||||
sign-in-gated Drive link cannot be reviewed. Unlisted is enough; the video does not need to be
|
||||
public or searchable.
|
||||
|
||||
## What it shows
|
||||
|
||||
| time | on screen | what it proves |
|
||||
|---|---|---|
|
||||
| 0:00 | The app's **Choose a folder** prompt, with **Grant full access** | The app asks for the permission rather than assuming it |
|
||||
| 0:01–0:08 | Android's **All files access** screen; the toggle is off, then on | The user grants it through Android's own UI, not an in-app shortcut |
|
||||
| 0:08–0:12 | *(after a deliberate cut)* Internal storage, then the **AADemo** folder | The permission is what makes a whole-volume listing possible |
|
||||
| 0:12–0:14 | Documents: `budget.csv`, `itinerary.md`, `offsite-notes.txt`, `quarterly-report.pdf`, `service-agreement.docx` | The use is not media: these are files `MediaStore` does not manage |
|
||||
| 0:14–0:15 | **New folder** dialog | A write is about to be attempted |
|
||||
| 0:15–0:19 | **New folder** appears with the change dot; snackbar **Folder created** | The same permission is used to write, not only to read |
|
||||
|
||||
**The cut at 0:08 is a privacy redaction, not an edit to the evidence.** The raw take returns to the
|
||||
app's home screen between the grant and the folder listing, and that screen shows the phone's own
|
||||
library counts — **6 238 images and 20 248 documents**. [STORE_LISTING](STORE_LISTING.md) forbids
|
||||
publishing those counts, because they describe the owner's device rather than the app. The published
|
||||
video removes the home-screen segment and resumes on the internal-storage listing, which is the
|
||||
first frame that proves the grant took effect. The uncaptioned file is cut at the same two points,
|
||||
so both copies tell the same story.
|
||||
|
||||
The captions, in order:
|
||||
|
||||
1. *Plain Files needs All files access*
|
||||
2. *Android asks to allow all-file management*
|
||||
3. *Switching on All files access*
|
||||
4. *Granted: browsing internal storage*
|
||||
5. *Documents: CSV, MD, TXT, PDF, DOCX*
|
||||
6. *Writable too: a new folder is created*
|
||||
|
||||
## Why the system UI is German
|
||||
|
||||
The test phone (Xiaomi M2101K7AG) runs MIUI in German; the app itself is English. Changing the phone's
|
||||
language to flatter a recording would not change what the permission does, and the two system strings
|
||||
that matter are translated by the captions: *Zugriff auf alle Dateien* ("All files access") and
|
||||
*Zugriff zum Verwalten aller Dateien zulassen* ("Allow access to manage all files"). The reviewer sees
|
||||
the toggle move and the app change state, which is the evidence; the language is incidental.
|
||||
|
||||
## Privacy: what is not in the video
|
||||
|
||||
- The library is **generated** by `tools/make-demo-library.py`, not taken from the phone. It was
|
||||
copied to `/sdcard/AADemo` so it sorts first and the recording never has to open `Download`, whose
|
||||
listing on this device holds the owner's real PDFs.
|
||||
- Every filename on screen — `budget.csv`, `itinerary.md`, `quarterly-report.pdf`, `AADemo` — is
|
||||
synthetic. The published cut removes the home screen entirely, because its library counts
|
||||
(**6 238 images, 20 248 documents**) are the owner's real totals; the uncaptioned file is cut at the
|
||||
same points. What remains shows synthetic names and Android's own screens.
|
||||
- Frames were inspected with a contact sheet before and after the cut, and the status bar holds
|
||||
nothing but the clock, signal, battery and the screen-record indicator.
|
||||
|
||||
## Re-shooting it
|
||||
|
||||
The take was made on the release build, because that is the artefact the declaration is about:
|
||||
|
||||
```bash
|
||||
./gradlew :app:assembleRelease
|
||||
adb install -r app/build/outputs/apk/release/app-release.apk
|
||||
adb shell cp -r /sdcard/Download/PlainFiles-Demo /sdcard/AADemo # only if absent
|
||||
adb shell appops set --uid de.softwareapp_hb.filemanager MANAGE_EXTERNAL_STORAGE deny
|
||||
adb shell am force-stop de.softwareapp_hb.filemanager
|
||||
adb shell am start -n de.softwareapp_hb.filemanager/.MainActivity
|
||||
```
|
||||
|
||||
⚠️ **The `--uid` is the whole trap.** After Android's Settings screen grants the permission, a
|
||||
package-level `appops set de.softwareapp_hb.filemanager MANAGE_EXTERNAL_STORAGE deny` leaves the
|
||||
**UID mode** at `allow`, the app keeps `isExternalStorageManager() == true`, and no prompt appears.
|
||||
The revoke only works with `--uid`, and `appops get` shows both lines so the state can be checked
|
||||
before recording.
|
||||
|
||||
Then, with the prompt verified on screen, record and drive it:
|
||||
|
||||
```bash
|
||||
adb shell rm -f /sdcard/permission-use.mp4
|
||||
adb shell screenrecord --size 1080x2400 --bit-rate 8000000 --time-limit 30 /sdcard/permission-use.mp4 &
|
||||
sleep 1
|
||||
adb shell input tap 540 1358 # Grant full access
|
||||
sleep 2.5
|
||||
# If the runtime storage dialog appears, tap ERLAUBEN at (540,1321). It does not when
|
||||
# READ_EXTERNAL_STORAGE is already granted, which is the normal case on this phone.
|
||||
sleep 2.5
|
||||
adb shell input tap 931 526 # All files access toggle
|
||||
sleep 2
|
||||
adb shell input keyevent KEYCODE_BACK
|
||||
sleep 2
|
||||
adb shell input tap 540 1600 # Internal storage
|
||||
sleep 2
|
||||
adb shell input tap 540 644 # AADemo
|
||||
sleep 2
|
||||
adb shell input tap 540 644 # Documents
|
||||
sleep 2
|
||||
adb shell input tap 959 2146 # New folder (FAB)
|
||||
sleep 1.5
|
||||
adb shell input tap 824 1368 # Create; the name is prefilled, so no keyboard appears
|
||||
sleep 4
|
||||
adb shell pkill -INT screenrecord
|
||||
adb pull /sdcard/permission-use.mp4
|
||||
```
|
||||
|
||||
Then cut out the home screen. In this take the last Settings frame is at **8.25 s** and the first
|
||||
internal-storage frame is at **10.5 s**; re-check those two frames before reusing the numbers, because
|
||||
the exact times depend on how fast the taps landed:
|
||||
|
||||
```bash
|
||||
ffmpeg -y -i permission-use.mp4 -filter_complex \
|
||||
"[0:v]trim=0:8.25,setpts=PTS-STARTPTS[a];[0:v]trim=10.5,setpts=PTS-STARTPTS[b];[a][b]concat=n=2:v=1[v]" \
|
||||
-map "[v]" -c:v libx264 -crf 20 -pix_fmt yuv420p -an permission-use-cut.mp4
|
||||
```
|
||||
|
||||
Do not try to record this on the AVD. Under software emulation `screenrecord` captured **one frame in
|
||||
three seconds** — a three-second test produced a 1-frame, duration-less file — so the clean emulator
|
||||
cannot produce video. The phone records properly; the emulator is only useful for checking the demo
|
||||
library and the permission screens.
|
||||
|
||||
Two details are deliberate:
|
||||
|
||||
- **Never tap the name field.** On this MIUI build it raises the keyboard, the dialog shifts up, and
|
||||
the next tap lands on the keyboard instead of **Create** — the first attempt created the folder but
|
||||
ended on an empty screen. The prefilled *New folder* is enough to show a write.
|
||||
- **The coordinates assume 1080×2400 at 440 dpi** (the Xiaomi phone). On another device, map each
|
||||
screen with `adb shell uiautomator dump` and use the bounds rather than scaling these.
|
||||
|
||||
The captions were burned in from six transparent PNG overlays with `ffmpeg overlay` (Pillow, DejaVu
|
||||
Sans Bold 40 px, black 80 % box, bottom-centred above the snackbar). The text and timings are in
|
||||
`store-assets/video/permission-use.srt`, so a re-upload can carry the same captions without burning
|
||||
them in.
|
||||
Reference in New Issue
Block a user