1f94d0a38a
docs/PERFORMANCE.md has been honest that three of its four numbers do not exist. They exist now as measurements, if not yet as results: benchmark/ is a macrobenchmark module with four tests - cold start with StartupTimingMetric, then opening, scrolling and searching a folder of a thousand files with FrameTimingMetric - sharing a fixture at /sdcard/AABenchmark that is created once per run and deleted in @AfterClass. Frames rather than a named trace section on purpose: what a user calls slow is jank on screen, and the alternative puts benchmarking calls into production code for the sake of a number. CompilationMode.None() is the honest cold-install baseline; Partial() plus a baseline profile is the follow-up and needs the same module. The storage grant is applied with appops in setup, which is what lets the run be unattended - a permission dialog is the one thing a test cannot answer. Three build facts had to be discovered rather than designed, and each is now written where it will be read. The app under test cannot keep its own package id: it needs a debug signature to install on a test device, so it cannot replace a release install, and an un-suffixed build is exactly that. It also cannot use the obvious suffix: `.benchmark` is already the benchmark module's own APK id, and the test APK then fails to install with INSTALL_FAILED_VERSION_DOWNGRADE - which is what the first attempt did. Hence `.perf`. And `testBuildType = "benchmark"` cannot be set unconditionally, because it makes benchmark the variant that *unit* tests run against and :app:testDebugUnitTest - the task the entire gate is built on - stops existing; it is behind -PbenchmarkBuild instead. All three were measured, not guessed, and the third would have broken CI silently. Two smaller things came with it. tools/verify.sh --release now also compiles the app's benchmark variant and the test APK, because build wiring nobody runs is wiring that rots; no device is involved. And the code map grew an EXTRA_ROOTS section so it indexes benchmark/ too - a map that silently omits a module lies by omission, which is worse than the one line it costs. The numbers themselves are still missing, and for a device reason rather than a code one: installing the new package id on this MIUI phone fails with INSTALL_FAILED_USER_RESTRICTED (the "Install via USB" gate, which wants a human and answered too late). Both APKs are pushed to /sdcard/Download/ for a manual install, and the run is one command away: `adb shell am instrument -w -e class de.softwareapp_hb.filemanager.benchmark.BrowserBenchmark de.softwareapp_hb.filemanager.benchmark/androidx.test.runner.AndroidJUnitRunner`. Gate green with --release, including the new compile step; map current; 386 tests unchanged.
106 lines
6.2 KiB
TOML
106 lines
6.2 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"
|
|
|
|
# Macrobenchmark (the `:benchmark` module). Its own versions because it is a test APK that is never
|
|
# part of the app.
|
|
benchmark = "1.5.0"
|
|
androidxTestJunit = "1.3.0"
|
|
uiautomator = "2.4.0"
|
|
androidxTestRunner = "1.7.0"
|
|
|
|
[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" }
|
|
|
|
# Macrobenchmark, for the `:benchmark` module only.
|
|
androidx-benchmark-macro-junit4 = { group = "androidx.benchmark", name = "benchmark-macro-junit4", version.ref = "benchmark" }
|
|
androidx-test-ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidxTestJunit" }
|
|
androidx-test-uiautomator = { group = "androidx.test.uiautomator", name = "uiautomator", version.ref = "uiautomator" }
|
|
androidx-test-runner = { group = "androidx.test", name = "runner", version.ref = "androidxTestRunner" }
|
|
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" }
|