Rename a whole batch at once, numbered on from the folder's own series
Selecting more than one file and choosing Rename now opens a pattern dialog rather than the
single-file one: a name, the number to start at, and a live preview of the names it will produce.
The number is the part that makes the feature worth having. A batch rename that always starts at 1
is useless the second time it is used, because the first batch already owns 1 to 12, so the dialog
reads the names already in the folder and starts at the highest "base (n)" it finds: a folder
holding "series (1)" and "series (2)" makes the next batch start at 3, which is the market survey's
most-helpful complaint answered exactly ("if I later find another group of photos which I want to
continue the batch naming with 'Ice (3)...'"). Three rules in core/BatchRename are decisions rather
than defaults, and are written down as such: the extension is kept per file, so a mixed batch of
.jpg and .png keeps each file's own; the series is counted across extensions, because "Holiday
(9).png" making the next number 10 is one series while "Holiday (10).jpg" beside "Holiday (1).png"
is two; and it continues from the highest number rather than from the first gap, because someone
renaming a second batch is carrying a series on rather than filling in the holes of one, with the
start number theirs to type where a gap matters more.
The batch is planned in full before a single file moves, and the plan has three answers and
deliberately no fourth: Ready, Blocked (naming the target that is already in the folder) or
NothingToDo. A batch that renamed nine files of twelve and then stopped would leave the user to work
out which nine and to guess whether the last three crashed or were skipped, so a collision refuses
the whole batch, the dialog names the file in the way and disables its button as the name is typed,
and a chain is declined outright - "a.txt" taking "a (1).txt" while "a (1).txt" is itself in the
batch would need the files shuffled through temporary names. Every rename still goes through the
same FileOperations.rename a single rename uses, so the refusal to overwrite anything is the same
one, and the successful renames are collected into the single Undo.Relocate step that already
existed: a feature that touches twelve files at once owes the user one tap back. The action bar now
offers Rename to any selection, since which of the two dialogs a tap means is described by the
dialog that opens rather than by the bar; Details stays single, because "the details" of twelve
files is not a screen.
Writing it turned up a quieter bug in the rename that was already there. rename(path,
"notes/final.txt") built a child path, which resolves into the subfolder rather than alongside the
file, so a name containing a separator moved the file instead of renaming it and nothing in the
dialog or the listing showed that anything had happened. Both rename paths now refuse a name that is
not one path segment (core/FileNames.isUsableName), and the dialog says which rule it is while it is
typed, which is where the user can still do something about it.
Twenty new core tests (BatchRenameTest, FileNamesTest) and six through the view model, 350 JVM
tests in total, lint clean. Verified by driving it on the phone against a folder that already held
"series (1).txt" and "series (2).txt": with three files selected and "series" typed in, Start at
moved to 3 on its own and the preview listed "series (3).txt, series (4).txt, series (5).txt";
afterwards the folder held all five series files, each file's contents having travelled with its
rename and the two originals untouched, and the snackbar's Undo put a later batch of three back in
one step. The collision and the slash were driven the same way - the file in the way named with the
button disabled, and "A name cannot contain a slash" - and the scratch folder was removed from the
device afterwards. CHANGELOG, ROADMAP (M14), COMPETITIVE_ANALYSIS (the gap moves from candidate to
closed), STORE_LISTING and EDGE_CASE_TESTING carry the feature, its decisions and the pass itself;
two tooling notes went into the last of them, that "adb input text" does not deliver a slash and
that the dialog lifts once the keyboard appears.
This commit is contained in:
@@ -307,7 +307,7 @@ requested feature in the market leader, and the permission line is a claim it ca
|
||||
|---|---|---|
|
||||
| Automatic category libraries (Images / Audio / Videos / Documents / Downloads / New files / Recent) | **No** | The most-praised organisational feature in the reviews, and the one thing a brand-new user sees before they understand folders. We open on a volume list instead. This is the largest honest gap |
|
||||
| Duplicate finder | **No** | Explicitly requested, with an offer to pay. Pure local computation over a listing we can already do |
|
||||
| Batch rename, including continuing an existing numbering sequence | **No** | We have single-item rename only; "Ice (3), Ice (4)" is a stated wish from the top complainant |
|
||||
| Batch rename, including continuing an existing numbering sequence | **No** | **Closed in M14.** Several files are renamed to one name with a number after each, and the numbering continues the folder's own series, so the "Ice (3), Ice (4)" wish from the top complainant is what the dialog now defaults to |
|
||||
| Bookmarks | **No** | Their action list includes it; we have none |
|
||||
| "Copy again" / "Return to source folder" after a paste | **No** | Shipped in their 3.6.8 and cheap for us — the post-paste moment is when a user most needs a way back |
|
||||
| New-file dot after an operation | **No** | Their 3.6.5. Cheap, and it answers "what did that 4 GB copy just do?" |
|
||||
@@ -346,13 +346,15 @@ latency, so this needs to be pinned down rather than assumed:
|
||||
|
||||
## What I would do with this
|
||||
|
||||
In the ROADMAP's own vocabulary (💡 = candidate, proposed, not scheduled):
|
||||
In the ROADMAP's own vocabulary (💡 = candidate, proposed, not scheduled; ✅ = since shipped):
|
||||
|
||||
1. 💡 **Category libraries (Images / Audio / Videos / Documents / Downloads · New files · Recent).**
|
||||
The largest honest gap: it is the market leader's most-praised organisation feature, it needs no
|
||||
permission, and it is the first thing a new user sees.
|
||||
2. 💡 **Duplicate finder.** Asked for by name, by a user offering to pay.
|
||||
3. 💡 **Batch rename with sequence continuation** — including the "continue from Ice (2)" case.
|
||||
1. ✅ **Category libraries (Images / Audio / Videos / Documents · New files · Recent)** — shipped as
|
||||
**M11**, with Downloads and New files left out on purpose. It was the largest honest gap: the
|
||||
market leader's most-praised organisation feature, needing no permission, and the first thing a
|
||||
new user sees.
|
||||
2. ✅ **Duplicate finder** — shipped as **M12**. Asked for by name, by a user offering to pay.
|
||||
3. ✅ **Batch rename with sequence continuation** — shipped as **M14**, including the "continue from
|
||||
Ice (2)" case that the top complainant names.
|
||||
4. 💡 **Bookmarks, plus "Return to source folder" and "Copy again" after a paste.** Cheap, local,
|
||||
and the post-paste moment is the one the competitor's changelog calls out.
|
||||
5. 💡 **Highlight files changed by an operation.** Their 3.6.5; answers an obvious question.
|
||||
|
||||
@@ -192,6 +192,46 @@ through the prompt, because the prompt was already covered in the M7 session.
|
||||
the AVDs have no `/dev/kvm` here, and the two-pane root list is where the libraries now appear
|
||||
first.
|
||||
|
||||
## The batch rename session (M14)
|
||||
|
||||
Driven on the Xiaomi phone against a purpose-made folder — `/sdcard/AAABatchRename`, holding
|
||||
`alpha.txt`, `beta.txt`, `gamma.txt` and an existing series `series (1).txt` / `series (2).txt` —
|
||||
because the one thing worth proving here is the **continuation**, and that needs a folder that
|
||||
already holds a series. Every step went through the UI with `adb input`, and each claim below was
|
||||
read back off the device rather than off the screen.
|
||||
|
||||
- **The dialog continues the series on its own.** Three files selected (long-press, then a tap each —
|
||||
the M4 tap-to-toggles behaviour), Rename from the action bar, `series` typed into the name and the
|
||||
**Start at** field moved from 1 to **3** by itself, with the preview reading *Will be renamed to
|
||||
series (3).txt, series (4).txt, series (5).txt*. Nothing but the names in the folder was consulted,
|
||||
and those are the listing on screen, so the dialog and the view model cannot disagree about them.
|
||||
- **The work is exactly what the dialog promised.** `ls` afterwards showed `series (1) … (5)`, the
|
||||
two originals untouched, and `cat` showed `series (3).txt` holding `alpha` and `series (4).txt`
|
||||
holding `beta` — the renames *moved* the files rather than copying them.
|
||||
- **One undo step, through the snackbar.** A second batch was renamed to `clip (1..3)`, the snackbar
|
||||
read *Renamed 3 files* with **Undo**, and a single tap put all three names back, contents intact
|
||||
through the round trip (`series (5).txt` still held `gamma`).
|
||||
- **Both refusals are visible before anything can be pressed.** With `series` typed and the start
|
||||
forced back to 1 over a folder holding `series (1)`/`series (2)`, the dialog read *series (1).txt is
|
||||
already in this folder* — the file in the way, named — and the only `enabled="false"` node in the
|
||||
whole dump was the Rename button. Renaming two files to the names they already hold read *Every
|
||||
selected file already has that name* instead, which is the honest version of "nothing to do", and a
|
||||
name containing `/` read *A name cannot contain a slash*, button disabled as well.
|
||||
- **The test folder was removed afterwards.** It was created for this pass, in the phone's own
|
||||
storage, and deleted through `adb` at the end: the M10 rule about not leaving artefacts on a device
|
||||
that is not ours applies to a scratch folder as much as to screenshots.
|
||||
|
||||
Two things this session added to the tooling notes:
|
||||
|
||||
- **`adb input text` does not deliver a `/` reliably.** `'a/b'` arrived in the field mangled (`a!c`,
|
||||
after the trailing character had to be deleted), so the slash case was sent as `input keyevent 76`
|
||||
— worth knowing before concluding that a field filters separators: here the *field* accepted it and
|
||||
the dialog is what refused it.
|
||||
- **The dialog lifts once the keyboard appears.** Its fields sit ~200 px higher afterwards, so a
|
||||
second field has to be aimed at the post-keyboard layout. Two dumps were needed to tell the two
|
||||
positions apart, which is also why the numbers quoted above come from a dump rather than from
|
||||
memory.
|
||||
|
||||
## Not verified, and why
|
||||
|
||||
- **Long-press multi-select via adb: it works, but not every time.** `input swipe x y x y 900`
|
||||
|
||||
+7
-6
@@ -1,6 +1,6 @@
|
||||
# Roadmap
|
||||
|
||||
File Manager is built against a fixed milestone plan (**M0–M10**). This file is the
|
||||
File Manager is built against a fixed milestone plan (**M0–M14**). This file is the
|
||||
single source of truth for what is done and what remains.
|
||||
|
||||
Legend: ✅ done · 🚧 in progress · ⏳ not started · 💡 candidate (proposed, not scheduled)
|
||||
@@ -23,7 +23,7 @@ Legend: ✅ done · 🚧 in progress · ⏳ not started · 💡 candidate (propo
|
||||
| M11 | Library views | ✅ | A **Libraries** section on the root screen — Images, Videos, Audio, Documents, Archives, Recent — each row carrying how many files the index knows of that kind, each opening an ordinary listing (thumbnails, sort, multi-select, details, the existing viewers) answered from the index instead of from a directory. `core/FileCategory` groups `FileKind`s and is asserted to leave `DIRECTORY`, `APK` and `OTHER` out deliberately; `SearchIndex` gained `byKind`/`recent`/`kindCounts`, and SQLite schema **v3** adds the derived `kind` column — the migration rebuilds the table *and* drops the volume stamps, because a stamp whose rows have gone would make the next search trust an empty index. A library obeys the indexed search's own two rules: the answer comes first with the walk behind it, and every row is confirmed against the filesystem before it is shown. Not included, and recorded in the CHANGELOG as decisions rather than omissions: Downloads and New files, and restoring a library across process death. **308 JVM tests** (19 added here) |
|
||||
| M12 | Duplicate finder | ✅ | ***Find duplicates*** **in the overflow.** Three passes, cheapest first: **size** (the index's `duplicateCandidates` query — one query and no reading at all), **the first 64 KB** of every candidate (a filter that may only rule files *out*), and the **whole file** to confirm (`domain/DuplicateFinder`, SHA-256, cancellable between files and between read buffers). Groups are ordered by reclaimable space; each header states the copies and what they cost; every row carries its folder, because identical names with no paths are unactionable; and **nothing is preselected** — the delete is the app's ordinary one, through the trash and the undo snackbar. A file that cannot be read is counted and reported rather than skipped, and the empty state says how many files were compared — or that the index has never run and there was nothing to compare. **323 JVM tests** (15 added). Verified on the Xiaomi phone against four genuine groups in its own storage; the delete was deliberately not pressed on a device that is not ours — see [EDGE_CASE_TESTING](EDGE_CASE_TESTING.md) |
|
||||
| M13 | Video thumbnails | ✅ | A video row shows **a frame of the video** instead of a film icon: images and videos take the same path through Coil, with the video request asking for a frame a second in. One dependency — `coil-video` at the Coil version already pinned, whose POM adds only coil-core/stdlib/core-ktx — the platform's own `MediaMetadataRetriever` behind it, **no new permission** (no `READ_MEDIA_VIDEO`), and the decoder registered **explicitly** against an `ImageLoader` in `FileManagerApplication` — because R8 strips `coil-video`'s ServiceLoader entry from a release build, which would otherwise have left frames decoding in debug and quietly falling back to icons for real users. A decode that fails settles on the icon rather than an empty square. Verified on the phone against two purpose-made clips and a real video of the device's own, with the demo library's stand-ins falling back as intended, and **re-verified on the release APK**. **324 JVM tests** |
|
||||
|
||||
| M14 | Batch rename with sequence continuation | ✅ | Selecting more than one file and choosing Rename opens a **pattern** dialog: a name, a start number, and a live preview of the names it will produce. `core/BatchRename` plans the whole batch before a single file moves and returns one of three answers — **Ready**, **Blocked** (naming the target the folder already holds) or **NothingToDo** — with the deliberate absence of a fourth: a batch that renamed nine of twelve and stopped would leave the user to work out which nine. The numbering **continues the folder's own series** `nextNumber` from the highest `base (n)` it finds, which is the market survey's most-helpful complaint answered by name; the extension is kept per file, the series is counted across extensions, and continuing beats filling the first gap. `SelectionBar` now offers Rename to any selection (one file keeps the name dialog, several get the pattern one), and the batch's renames are **one** `Undo.Relocate` step. The same commit hardens the single rename: `core/FileNames.isUsableName` refuses a name that is not one path segment, because `notes/final.txt` was being resolved as a path and **moved** the file instead of renaming it. **350 JVM tests** (26 added) |
|
||||
## Key decisions
|
||||
|
||||
| Decision | Choice | Rationale |
|
||||
@@ -47,7 +47,8 @@ Legend: ✅ done · 🚧 in progress · ⏳ not started · 💡 candidate (propo
|
||||
|
||||
A survey of the market leader (its features, and what its users praise and complain about) is in
|
||||
[COMPETITIVE_ANALYSIS](COMPETITIVE_ANALYSIS.md). Its gaps that cost **no** new permission are
|
||||
listed there as 💡 candidates. **Category libraries were the first of them and are now M11, and the
|
||||
duplicate finder the second (M12)**; the rest — batch rename with sequence continuation, bookmarks,
|
||||
and the post-paste "copy again" / "return to source folder" — are proposals, not scheduled work, and
|
||||
adding one is a roadmap decision rather than a side effect of reading that document.
|
||||
listed there as 💡 candidates. **Category libraries were the first of them and are now M11, the
|
||||
duplicate finder the second (M12) and batch rename with sequence continuation the third (M14)**; the
|
||||
rest — bookmarks, and the post-paste "copy again" / "return to source folder" — are proposals, not
|
||||
scheduled work, and adding one is a roadmap decision rather than a side effect of reading that
|
||||
document.
|
||||
|
||||
@@ -122,6 +122,7 @@ WORKS WITH YOUR OTHER APPS
|
||||
|
||||
MANAGE YOUR FILES
|
||||
• Copy, move, rename and delete, one item or many at once
|
||||
• Rename a whole batch at once, numbered in a series that carries on where the last one stopped
|
||||
• Create folders and edit text files in the built-in editor
|
||||
• See the details of anything: size, type, location, modified date
|
||||
• Open a file in any app, or share it
|
||||
|
||||
Reference in New Issue
Block a user