9.0 KiB
Changelog
All notable changes to File Manager are documented in this file.
The format follows Keep a Changelog, and the project uses Semantic Versioning.
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.
UsageAnalysisnow passes theFileEntrydown the recursion instead of re-stating 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 anonProgresscallback, 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.namedirectly, so "Type" readdirectory,archive,apk— enum constants rather than labels, and impossible to translate. Every category now has a string resource and aFileKind.labelRes()mapping alongside the existingOperationKind.titleRes().
Added
-
M9 — monetisation. Decided: a one-time paid app at €4.49, with a 14-day launch price of €2.29, both set in the Play Console. No ads, no subscription and no in-app purchases — which means no Billing client, no
com.android.vending.BILLINGpermission and no purchase code in the APK at all; the price lives on the listing, not in the build. That is what keeps the "no network permission, nothing leaves your device" claim true of the shipped artefact rather than just the source. The rationale, the market anchors and the rejected alternatives are in docs/MONETISATION.md. Verified against the rebuilt release APK: zero billing references, zeroINTERNET, and the debug-only pseudo-locale correctly absent. -
M8 — localisation groundwork. Every user-visible string lives in
res/values/strings.xmlwith real<plurals>, and an audit of the Compose sources found exactly one genuine miss (above). The debug build type enablesisPseudoLocalesEnabled, 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, 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-ignoredkeystore.properties, sobundleReleaseproduces a signed, R8-minified, resource-shrunk.aab(4 682 814 bytes) andassembleReleasea signed APK.docs/PLAY_RELEASE_CHECKLIST.mdwalks 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.mdis written to match the shipped permissions: no collection, no sharing, noINTERNET. The shipped permission set is audited from the release APK. -
M5 — analysis, archives and viewers.
domain/UsageAnalysisrolls 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/ArchiveEnginecreates ZIP archives and extracts ZIP, TAR, TAR.GZ, GZ, BZ2 and XZ usingjava.util.zipand 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 throughcore/ArchivePathbefore 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 realStorageProviderand a fake for tests) decides which backend is in effect;BrowserViewModelowns navigation, sorting, hidden files, list/grid, search, selection and the clipboard, and turns anASKconflict 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 whenMediaStorecan build one and fall back to a direct delete otherwise; aFileProviderlets other apps open and receive files without afile://URI ever leaving the process. 16 new JVM tests (115 total). -
M3 — operations engine and foreground service.
domain/FileOperationsturns 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 swallowingCancellationException), and reports per-item failures instead of abandoning a batch because one file was unreadable. Conflicts are resolved through the M1TransferPlanner—ASKis 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/FileOpServiceis adataSyncforeground 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.
AllFilesSourceisjava.io.Fileover every mounted volume (the "All files access" path);SafSourceis the Storage Access Framework fallback, reading a persisted tree URI throughDocumentsContractwithSafDocumentIdsdoing the tree↔path mapping andSafGrantowning the folder picker and its persistence.VolumeRepositoryenumerates volumes viaStorageManager/StorageVolumeand reports free/total space throughStatFs;StorageAccessandStorageAccessPolicydecide between the two backends. Both sources confine their blocking I/O toDispatchers.IOand expose asuspendcontract. 32 new JVM tests (76 total), including a Robolectric run ofSafSourceagainst a stand-inDocumentsProvider. -
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),FileEntrywith extension-basedFileKindclassification and aMimeTypeslookup for "open with", natural-orderSortSpec(directories always first,file2beforefile10), human-readableFileSize,NameMatcher(case-insensitive substring plus*/?globs),NameAllocator("name (1)") andTransferPlanner(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.filemanagerapp 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.