Files
file-manager/CHANGELOG.md
T

124 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Changelog
All notable changes to File Manager are documented in this file.
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and the
project uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
`versionCode` and `versionName` are defined in `app/build.gradle.kts`. Each Play
upload needs a **higher `versionCode`** than every previous upload.
## [Unreleased]
### Fixed
- **The storage-usage screen stayed blank for ~45–60 s** on a real 55 GB volume, which
reads as a hang even though the work was off the main thread. `UsageAnalysis` now passes
the `FileEntry` down the recursion instead of re-`stat`ing every node — the parent's
listing already says whether each child is a folder — so the walk costs one listing per
directory. It also reports each child through an `onProgress` callback, and the ViewModel
appends and re-sorts as they arrive, so the largest folders appear while the rest of the
tree is still being measured. Found by running the app on a real phone.
- **Misleading first-run copy.** The permission prompt said "Full access was declined" on a
device where the user had never declined anything. Reworded so it is true whether access
was declined or simply never granted.
- **The details sheet put untranslatable English on screen.** It rendered `FileKind.name`
directly, so "Type" read `directory`, `archive`, `apk` — enum constants rather than
labels, and impossible to translate. Every category now has a string resource and a
`FileKind.labelRes()` mapping alongside the existing `OperationKind.titleRes()`.
### Added
- **M8 — localisation groundwork.** Every user-visible string lives in
`res/values/strings.xml` with real `<plurals>`, and an audit of the Compose sources found
exactly one genuine miss (above). The debug build type enables
`isPseudoLocalesEnabled`, so running it in English (XA) renders resource-backed text
accented and bracketed while anything hardcoded looks normal — a mechanical way to catch
the next one. Confirmed the pseudo-locale reaches the debug APK and not the release.
Byte units and the timestamp pattern stay unlocalised on purpose, documented in the
README along with the pseudo-locale check.
- **M7 — on-device validation.** The app was run on a real Xiaomi M2101K7AG (Android 12,
MIUI V140) rather than only emulator images. Verified on hardware: the first-run
permission prompt, "All files access" granted while the app is alive (picked up on
resume), browsing real storage, the text editor's read *and* write path (the write
checked independently with `adb shell cat`), the storage-usage screen, landscape layout,
and **graceful degradation when access is revoked mid-use**. The findings live in
[docs/EDGE_CASE_TESTING.md](docs/EDGE_CASE_TESTING.md), including what could *not* be
driven from the host — long-press multi-select is delivered as a plain tap by this MIUI
build, so the compress/extract buttons still want a manual pass.
- **M6 — release config and Play readiness.** An upload keystore (4096-bit RSA,
`CN=Hadrian Burkhardt, L=Potsdam, C=DE`) is generated and wired through the git-ignored
`keystore.properties`, so `bundleRelease` produces a signed, R8-minified, resource-shrunk
`.aab` (4 682 814 bytes) and `assembleRelease` a signed APK. `docs/PLAY_RELEASE_CHECKLIST.md`
walks the Play Console work, with step 4 covering the **Permissions Declaration Form** for
"All files access" — the declaration a file manager must make, and the one that can block
a release. `PRIVACY.md` is written to match the shipped permissions: no collection, no
sharing, no `INTERNET`. The shipped permission set is audited from the release APK.
- **M5 — analysis, archives and viewers.** `domain/UsageAnalysis` rolls up recursive folder
sizes and finds the largest files below a folder, which the new storage-usage screen
shows as a size and a share bar per row. `domain/ArchiveEngine` creates ZIP archives and
extracts ZIP, TAR, TAR.GZ, GZ, BZ2 and XZ using `java.util.zip` and Commons Compress —
all pure Java, so the app still ships no native code; RAR and 7z are recognised and
refused with a clear message rather than half-supported. Every archive entry name is
passed through `core/ArchivePath` before it becomes a path, so a crafted `../` entry
cannot write outside the destination. The browser gains Coil thumbnails in the list and
grid, a built-in image viewer and text editor, and compress/extract actions. 26 new JVM
tests (141 total), including a zip-slip archive that is refused while the same archive's
safe entry still extracts.
- **M4 — the browser UI.** The app is now usable end to end. `StorageLocation` (with a
real `StorageProvider` and a fake for tests) decides which backend is in effect;
`BrowserViewModel` owns navigation, sorting, hidden files, list/grid, search, selection
and the clipboard, and turns an `ASK` conflict into a prompt rather than a guess. The
screen offers roots and folder lists with breadcrumbs, a details sheet, new-folder /
rename / delete / conflict dialogs, and the "All files access" and SAF-folder prompts —
including an upgrade banner so declining full access never blocks browsing. Deletes go
through the platform's own confirmation when `MediaStore` can build one and fall back to
a direct delete otherwise; a `FileProvider` lets other apps open and receive files
without a `file://` URI ever leaving the process. 16 new JVM tests (115 total).
- **M3 — operations engine and foreground service.** `domain/FileOperations` turns the
backend primitives into copy, move, delete and rename: it walks folders, counts the
work up front so the progress bar has an honest denominator, checks for cancellation
between entries (never swallowing `CancellationException`), and reports per-item
failures instead of abandoning a batch because one file was unreadable. Conflicts are
resolved through the M1 `TransferPlanner` — `ASK` is deliberately surfaced to the
caller so the UI can prompt. A move tries the backend's atomic rename first and only
falls back to copy-then-delete when the backend cannot do it (the SAF path across
directories); the source is never deleted when its copy failed. `service/FileOpService`
is a `dataSync` foreground service that mirrors the engine's progress into an ongoing
notification, so a long operation survives the app being backgrounded. 23 new JVM
tests (99 total), including Robolectric coverage of the notification and the service
entering the foreground.
- **M2 — storage layer.** The two storage backends behind one interface. `AllFilesSource`
is `java.io.File` over every mounted volume (the "All files access" path);
`SafSource` is the Storage Access Framework fallback, reading a persisted tree URI
through `DocumentsContract` with `SafDocumentIds` doing the tree↔path mapping and
`SafGrant` owning the folder picker and its persistence. `VolumeRepository` enumerates
volumes via `StorageManager`/`StorageVolume` and reports free/total space through
`StatFs`; `StorageAccess` and `StorageAccessPolicy` decide between the two backends.
Both sources confine their blocking I/O to `Dispatchers.IO` and expose a `suspend`
contract. 32 new JVM tests (76 total), including a Robolectric run of `SafSource`
against a stand-in `DocumentsProvider`.
- **M1 — pure-Kotlin core engine.** An Android-free `core/` package holding the logic
every other layer builds on: `StoragePath` (normalised absolute paths that can never
escape the root), `FileEntry` with extension-based `FileKind` classification and a
`MimeTypes` lookup for "open with", natural-order `SortSpec` (directories always
first, `file2` before `file10`), human-readable `FileSize`, `NameMatcher`
(case-insensitive substring plus `*`/`?` globs), `NameAllocator` ("name (1)") and
`TransferPlanner` (conflict policies and move-into-self detection). Covered by
**44 JVM unit tests** across 10 classes.
- **M0 — project scaffold.** Gradle 9.3.1 / AGP 9.1.0 build with a version catalog,
`de.softwareapp_hb.filemanager` app module, a Compose Material 3 shell with the
branded blue theme (dynamic colour on Android 12+), an adaptive launcher icon, and
the storage-access permission set (all files access + SAF fallback). Debug APK
builds, installs and runs on an AOSP Android 12 AVD.