Remember what is in the trash, and add a screen to restore from it
The staged-delete work left a hole. The undo handle lived in BrowserUiState, so a delete from an earlier session was unreachable even though the files were sitting on the volume; and because nothing on disk recorded which folder an item came from, not even the user could put one back by hand with confidence. Adding a Trash screen alone would not have fixed it: the recovery data did not survive the process. So the origin is now written at delete time. data/ gains its first persistence: a SQLite index (staged path, original path, name, type, size, timestamp). SQLite rather than Room because an annotation processor is a large addition to this build for one table and three queries, and behind a TrashIndex port so domain/Trash keeps no Context while the tests drive it with a map and the real one is still tested against real SQLite. New Trash entry in the overflow menu lists batches and restores a whole one. The index is an index, not the truth: the staged files are the truth, and the two can disagree if a user empties the folder by hand or pulls a card. Reconciled both ways - restorableFrom drops entries whose file has gone, purgeExpired forgets entries whose folder has gone and sweeps folders no entry mentions - and rows are written after the move, so a crash leaves an unindexed folder (harmless, swept) rather than an entry that could never restore. Verified on hardware: delete, force-stop, relaunch, restore. Also corrects the docs where they claimed input swipe x y x y 900 is delivered as a plain tap on MIUI. It does open the selection bar - it was driven that way - but it has also landed as a tap, so it is flaky rather than impossible.
This commit is contained in:
@@ -38,7 +38,8 @@ app/src/main/java/de/softwareapp_hb/filemanager/
|
||||
├── core/ Pure-Kotlin engine (no Android imports!) — paths, sorting, formatting,
|
||||
│ search matching, copy/move planning, conflict policy
|
||||
├── data/ Storage backends (all-files access + SAF), volumes, shares coming in
|
||||
│ from other apps, file operations, archives, deletion
|
||||
│ from other apps, the trash index (SQLite), file operations, archives,
|
||||
│ deletion
|
||||
├── domain/ Browser + operation controllers, models, permission state
|
||||
├── service/ Foreground service for long-running file operations
|
||||
├── ui/ Compose theme, browser screen, components, ViewModels
|
||||
@@ -139,11 +140,16 @@ UI.** Translated UI text has to be supplied in `values-<locale>/`.
|
||||
|
||||
## Known limitations
|
||||
|
||||
- **The trash has no screen of its own.** A delete now stages the item in a hidden
|
||||
`.plainfiles-trash` folder on the same volume and keeps it for 24 hours, and the snackbar
|
||||
offers Undo. Once that snackbar goes there is no in-app bin to browse or restore from — the
|
||||
folder is reachable by turning on hidden files, but a proper trash screen is the obvious
|
||||
follow-up.
|
||||
- **The trash is a time window, not a bin you manage.** A delete stages the item in a hidden
|
||||
`.plainfiles-trash` folder on the same volume and keeps it for 24 hours; the **Trash** entry
|
||||
in the overflow menu lists what is still there and puts a whole batch back. There is no
|
||||
"empty it now" action — expired batches are swept the next time something is deleted — and a
|
||||
name taken again in the meantime is never overwritten, so a restored item can arrive beside
|
||||
the new one rather than replacing it.
|
||||
- **Where a trashed item came from is remembered in the app's own database**, not on the
|
||||
volume. The staged files are the truth and the database is only an index, so the two are
|
||||
reconciled in both directions: an entry whose file has gone is not offered back, and a
|
||||
batch folder no entry mentions is swept.
|
||||
- **Sharing a file *out* only works in the all-files mode.** A SAF path is relative to the
|
||||
granted tree rather than a filesystem path, so it cannot be handed to `FileProvider`.
|
||||
Receiving a share and being picked from another app's chooser work either way: the first
|
||||
@@ -161,8 +167,9 @@ UI.** Translated UI text has to be supplied in `values-<locale>/`.
|
||||
- **Cancellation happens between entries**, so a single very large file finishes before a
|
||||
stop is honoured.
|
||||
- **View state is not persisted across process death** (`SavedStateHandle` is not wired up).
|
||||
- **Long-press multi-select could not be driven from adb** on the MIUI test device, so the
|
||||
compress/extract buttons want a manual pass (see
|
||||
- **Long-press multi-select is awkward to script from the host.** The gesture that opens the
|
||||
selection bar (`input swipe x y x y 900`) works on the MIUI test device but has also
|
||||
registered as a plain tap, so compress/extract from the UI wants a manual pass (see
|
||||
[docs/EDGE_CASE_TESTING.md](docs/EDGE_CASE_TESTING.md)).
|
||||
- The display name is still the working title — decided before the first upload (M10).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user