0bbee25ab3
Fifteen rendering tests across four files are the first tests in this project that compose a screen, and they cover the claims that used to be prose only: the app bar's star exists where there is a folder to star and says Add or Remove according to the state, the selection bar offers Rename to several files and Details only to one, the batch dialog spells out the names it will produce and refuses a collision with the file named, the shared name dialog refuses a slash while it is typed, and a favourite that has gone says "Folder not found" rather than showing a path that is not there. They run on the JVM through Robolectric - the Compose test artifacts plus the test manifest - so they belong to the same verify.sh gate as everything else instead of needing a device: 386 tests in total. Writing them turned up one behaviour worth recording. The batch dialog continues the folder's series by default, so a collision cannot be shown unless the start number is forced back to 1 - which is precisely why the feature works in practice, and now also why two of these tests set the field explicitly. The debug build now enables StrictMode (disk reads and writes, network, leaked closables; logged, not fatal) and attaches LeakCanary. A file manager is the worst possible app for a blocking disk read on the main thread, and a debug build is the last place where noticing it is still cheap. Logging rather than a crash is deliberate: turning a slow screen into a crash on someone's phone is the worse trade. The flag is read from the manifest, not from BuildConfig, which this build does not generate. Performance has a number for the first time, and it immediately paid for itself. tools/measure-startup.sh takes cold start with `am start -W` - median of several runs, one warm-up discarded because the first launch after an install pays for dexopt - and docs/PERFORMANCE.md records 1814 ms over five runs, in the debug build, which is the slow case by construction. StrictMode's first run then produced five violations against that number: 492 ms resolving `app.cacheDir` inside the view-model factory, 397 ms reading free space with StatFs for every volume inside refresh(), 477 ms of the platform loading its own typefaces, and two 54 ms OEM font hooks. That is about 1.5 s of main-thread disk I/O in a 1.8 s start, of which ~0.9 s is ours and mechanical to move. Neither of ours is fixed here on purpose: the numbers and the two fixes are recorded in PERFORMANCE.md, and they are the first items of the speed work. What is not measured is stated in the same file rather than left to be assumed: folder open at 1 000 / 5 000 / 20 000 files, thumbnail paint while scrolling, and keystroke-to-result all need a `:benchmark` module and a profileable build under the release package id, and this device waits for a confirmation dialog on the first install of a new package id. The module is deliberately absent instead of half-configured: build wiring nobody can run is worse than a recorded gap. Gate green (map current, detekt, 386 tests, lint), and CHANGELOG, README, AGENTS.md, ROADMAP, PERFORMANCE.md, EDGE_CASE_TESTING and the regenerated map carry it.
93 lines
5.5 KiB
TOML
93 lines
5.5 KiB
TOML
[versions]
|
|
agp = "9.1.0"
|
|
kotlin = "2.4.10"
|
|
# Static analysis for Kotlin, driven by tools/verify.sh. 1.23.8 is the newest release on the plugin
|
|
# portal (there is no 2.x there yet). It is used through its CLI rather than its Gradle plugin: the
|
|
# plugin runs the analysis inside the build's own JVM, and detekt 1.23 cannot parse JDK 25's version
|
|
# string — see the task in app/build.gradle.kts. The findings that exist today are frozen in
|
|
# config/detekt/baseline.xml, so the gate fails on a new one rather than on the backlog.
|
|
detekt = "1.23.8"
|
|
|
|
# AndroidX / Compose
|
|
coreKtx = "1.18.0"
|
|
lifecycle = "2.10.0"
|
|
activityCompose = "1.13.0"
|
|
# SAF backend: DocumentFile over a persisted tree URI.
|
|
documentfile = "1.1.0"
|
|
# Compose BOM 2026.06.01 -> Compose UI/foundation 1.11.4, material3 1.4.0.
|
|
# Newer BOMs (2026.08.00+) ship Compose 1.12.x, which requires compileSdk 37 —
|
|
# that is newer than what AGP 9.1.0 supports (36) and newer than the API level
|
|
# Google Play requires for targetSdk, so we deliberately stay on 1.11.4.
|
|
composeBom = "2026.06.01"
|
|
# Coil 3.6.x requires compileSdk 37; 3.5.0 requires 36.
|
|
coil = "3.5.0"
|
|
|
|
# Kotlin libraries
|
|
coroutines = "1.9.0"
|
|
|
|
# Archives. Commons Compress is pure Java (zip/tar/gzip/bzip2/xz, 7z read),
|
|
# so it adds no native code and therefore no NDK dependency to the build.
|
|
commonsCompress = "1.27.1"
|
|
# Commons Compress declares its LZMA/LZMA2 support — the `.xz` compressor and the 7z
|
|
# format — against this library, as an *optional* dependency. Optional means it is NOT
|
|
# pulled in transitively, so leaving it out breaks `.xz` and `.7z` at **runtime** with
|
|
# NoClassDefFoundError rather than failing the build. 1.10 is the version Commons
|
|
# Compress 1.27.1 itself compiles against.
|
|
xz = "1.10"
|
|
|
|
# Testing
|
|
junit = "4.13.2"
|
|
robolectric = "4.17"
|
|
# Debug-only leak detection: a Compose screen that outlives its screen is invisible in tests and
|
|
# obvious in a heap dump. Never shipped — `debugImplementation` below.
|
|
leakcanary = "2.14"
|
|
|
|
[libraries]
|
|
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
|
|
androidx-documentfile = { group = "androidx.documentfile", name = "documentfile", version.ref = "documentfile" }
|
|
|
|
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycle" }
|
|
androidx-lifecycle-runtime-compose = { group = "androidx.lifecycle", name = "lifecycle-runtime-compose", version.ref = "lifecycle" }
|
|
androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version.ref = "lifecycle" }
|
|
|
|
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
|
|
|
|
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
|
|
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
|
|
androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
|
androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
|
|
androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
|
|
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
|
|
androidx-compose-material-icons-core = { group = "androidx.compose.material", name = "material-icons-core" }
|
|
androidx-compose-material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
|
|
|
|
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
|
|
|
|
coil-compose = { group = "io.coil-kt.coil3", name = "coil-compose", version.ref = "coil" }
|
|
# Decodes a frame out of a video, so a video row shows what is in the clip rather than a film icon.
|
|
# Same version as coil-compose on purpose — a decoder is part of Coil's pipeline, not a plugin beside
|
|
# it — and its POM adds nothing coil-compose does not already bring (coil-core, kotlin-stdlib,
|
|
# core-ktx). No ExoPlayer and no media3, which is what makes this acceptable for a thumbnail.
|
|
coil-video = { group = "io.coil-kt.coil3", name = "coil-video", version.ref = "coil" }
|
|
|
|
commons-compress = { group = "org.apache.commons", name = "commons-compress", version.ref = "commonsCompress" }
|
|
# Commons Compress's LZMA/LZMA2 support: the `.xz` compressor and the 7z format.
|
|
xz = { group = "org.tukaani", name = "xz", version.ref = "xz" }
|
|
|
|
junit = { group = "junit", name = "junit", version.ref = "junit" }
|
|
robolectric = { group = "org.robolectric", name = "robolectric", version.ref = "robolectric" }
|
|
# Compose rendering tests, run on the JVM through Robolectric. Versions come from the Compose BOM.
|
|
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
|
|
androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
|
|
leakcanary-android = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "leakcanary" }
|
|
kotlinx-coroutines-test = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-test", version.ref = "coroutines" }
|
|
|
|
# Static analysis, run by the :app:detekt task from its own configuration — never an app dependency.
|
|
detekt-cli = { group = "io.gitlab.arturbosch.detekt", name = "detekt-cli", version.ref = "detekt" }
|
|
|
|
[plugins]
|
|
android-application = { id = "com.android.application", version.ref = "agp" }
|
|
# NOTE: `org.jetbrains.kotlin.android` is intentionally absent — AGP 9.x provides
|
|
# built-in Kotlin support and applying that plugin is now an error.
|
|
kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
|