Make Copy to… and Cut to… the selection bar's actions
verify / gate (push) Has been cancelled
verify / release (push) Has been cancelled

The selection bar offered Copy and Move, which only filled an in-app clipboard; the
destination picker then lived behind an overflow menu, and Paste in the top bar. That
made a copy a two-step detour through another folder - the thing M16's picker exists to
avoid.

- The bar's two verbs are now Copy to… and Cut to…, both opening the destination picker.
- The clipboard state, Paste here, and the overflow's duplicate Copy to… / Move to… are
  removed. The picker's cut title and confirm are Cut to… / Cut here.
- A copy still keeps the selection for a second destination; a cut clears it. Conflicts
  still ask before overwriting, naming the picked folder.
- Five clipboard tests are removed; the mark, restart and undo tests now use the picker.
  BrowserTopBar's search and overflow sections are extracted so its two new detekt
  findings are fixed rather than baselined.

Driven on the Xiaomi phone: Copy to… copied 120 x 16 MB through Internal storage ->
AADemo -> Target and reported Copied 121 items to Target; Cut to… moved file001.bin and
offered Undo; a name in the way raised Items already exist here; the top-bar overflow
has no Paste. The foreground-service video was re-recorded against the new bar at 16.1 s.

Verified: ./tools/verify.sh green, the code map current, 426 unit tests pass, and the
video decodes as H.264 1080x2400 with no audio.
This commit is contained in:
2026-10-18 15:07:35 +02:00
parent f9305d1070
commit ddcd68b4b7
23 changed files with 322 additions and 417 deletions
+57 -45
View File
@@ -9,8 +9,8 @@ record of how it was made. The All files access declaration has its own video an
| 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.mp4` | **16.1 s** | The submission copy: 1080×2400, H.264, **no audio**, English captions burned in |
| `store-assets/video/fgs-import-export-uncaptioned.mp4` | 18.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
@@ -21,33 +21,34 @@ reviewed.
| 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:00 | `AADemo`, with `Source` selected and **Copy to…** in the action bar | The user starts a local import/export |
| 0:02 | The **Copy to…** picker: `Internal storage`, then `AADemo` | The destination is chosen locally, without leaving the source |
| 0:06 | The picker on `Target`, then **Copy here** | The user triggers the local copy |
| 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 |
| 0:14 | `AADemo` with the snackbar **Copied 121 items to Target** and **Show** | The operation completed at the chosen 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*
1. *Copy a 1.8 GB folder with Copy to…*
2. *Pick Internal storage, then AADemo*
3. *Choose Target, then Copy here*
4. *The foreground service reports progress*
5. *Done: Copied 121 items to Target*
## What was cut, and why
The raw take was **35.2 s**; the published copy is three pieces of it:
The raw take was **41.0 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 |
| 0–9.5 s | Select `Source`, **Copy to…**, pick `Internal storage → AADemo → Target`, **Copy here** |
| 21–25.5 s | The shade with **Copying files** and its progress bar |
| 26–31 s | The completed folder and the **Copied 121 items to Target** 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.
The two gaps are elapsed time, not evidence: the wait between **Copy here** 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
@@ -74,59 +75,70 @@ Clear the notification shade before recording: open it, tap **Clear all**, and c
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:
**1 item** with **Copy to…** 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 &
adb shell screenrecord --size 1080x2400 --bit-rate 8000000 --time-limit 55 /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
adb shell input tap 231 2155 # Copy to… in the selection bar
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.
adb shell input tap 540 1148 # Internal storage in the picker
sleep 2
adb shell input tap 540 994 # AADemo
sleep 2
adb shell input tap 540 1302 # Target
sleep 2
adb shell input tap 740 1396 # Copy here
# The notification exists only after Copy here. 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
sleep 6 # the card can rank below the USB pair for the first second
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:
Cut the two waits, keeping the trigger, the notification and the result. Use `-ss`/`-t` per segment
rather than a filter graph: the phone records variable-frame video, and a `trim` concat came out 6 s
short on this take.
```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
ffmpeg -y -ss 0 -t 9.5 -i fgs.mp4 -an -c:v libx264 -crf 20 -pix_fmt yuv420p seg-a.mp4
ffmpeg -y -ss 21 -t 4.5 -i fgs.mp4 -an -c:v libx264 -crf 20 -pix_fmt yuv420p seg-b.mp4
ffmpeg -y -ss 26 -t 5 -i fgs.mp4 -an -c:v libx264 -crf 20 -pix_fmt yuv420p seg-c.mp4
printf "file 'seg-a.mp4'\nfile 'seg-b.mp4'\nfile 'seg-c.mp4'\n" > segments.txt
ffmpeg -y -f concat -safe 0 -i segments.txt -c copy fgs-cut.mp4
```
The times are this take's; check the frames before reusing them.
The times are this take's; check the frames before reusing them. The published file is 16.1 s after
the caption pass, because the last segment is shorter than its nominal five seconds.
Two traps cost takes here:
Three 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.
- **The picker recentres as the trail grows.** Its rows and its **Copy here** button move with each
level, so map each screen with `adb shell uiautomator dump`; the coordinates above are this phone's
1080×2400 layout, not a scale factor.
- **Never run `assembleRelease` and `adb install` in one parallel batch.** That race installed the
previous APK, and the bar still said **Copy / Move** even though the new strings were in the built
APK. Install sequentially, then check the phone:
`aapt2 dump resources <installed.apk> | grep browser_action_cut_to`.
- **Open the shade only once the notification exists, and hold it.** Opening it straight after
**Copy here** can show only Android's USB cards, and even when the app's card exists it can rank
below them for the first second. The `cmd notification list` loop plus the 6 s hold is what made
**Copying files** readable.
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.
it. That is the operation still running, not a missed **Copy here**; `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.
selection bar sits at 2133–2177 on this phone, and a caption there hides the **Copy to…** button —
the very action the video exists to show.
## Uploading it