## Version catalog — single source of truth for plugin & dependency versions. ## Covers BOTH the pure Kotlin/JVM modules and the Android-framework modules (the ## SDK is installed on this machine — see android/README.md "Android SDK setup"). ## Kotlin pinned to 2.3.21 to match the Kotlin embedded in Gradle 9.6.1 (best ## compatibility with the Kotlin Gradle Plugin on Gradle 9.6). [versions] agp = "9.2.1" kotlin = "2.3.21" kotlinxSerialization = "1.9.0" kotlinxCoroutines = "1.10.2" junit = "5.13.4" junitPlatform = "1.13.4" turbine = "1.2.1" mockk = "1.14.2" # Kover — coverage gate on the pure JVM modules (plan A36 / section 7: >=80%). kover = "0.9.1" # OkHttp 4.x — plain JVM/Kotlin (NO Android SDK); used by :transport-okhttp (A7). # MockWebServer (same version) drives A7's JVM tests. Okio arrives transitively. okhttp = "4.12.0" # ── Android UI stack (A13 — the :app-stack baseline; resolved against Gradle) ──── # The Compose BOM pins ALL Compose + Material3 + Material3-Adaptive artifacts, so # those libraries below carry NO explicit version (BOM-managed → guaranteed mutual # compatibility). BOM 2025.11.01 → material3 1.4.0 · ui/foundation 1.9.5 · # material3.adaptive 1.2.0 · material3-adaptive-navigation-suite 1.4.0. # # CEILING = compileSdk 36. The env's cmdline-tools cannot fetch platform 37, and # the 2026 androidx line (BOM 2026.x, core 1.19, activity 1.13, lifecycle 2.11) # requires compiling against SDK 37. So the stack is capped at the newest SDK-36 # releases. Bump these together with compileSdk 37 once the platform is installable. composeBom = "2025.11.01" androidxCoreKtx = "1.17.0" androidxActivityCompose = "1.12.4" androidxLifecycle = "2.10.0" # Hilt 2.60.1 — its Gradle plugin references kotlin-bom 2.3.21 (matches Kotlin here). hilt = "2.60.1" hiltNavigationCompose = "1.2.0" # KSP2 — the post-2.3.0 unified scheme tracks Kotlin 2.3.x (Hilt's annotation processor). ksp = "2.3.9" # ── Android storage + crypto (A11 :client-tls-android · A12 :host-registry) ───── # Tink AEAD encrypts the cert-chain+metadata blob at rest (private key stays # non-exportable in AndroidKeyStore). DataStore = host list / last-session / prefs. tink = "1.15.0" datastore = "1.1.1" # ── Terminal renderer (A16 :terminal-view) ────────────────────────────────────── # Termux terminal-emulator + terminal-view — Apache-2.0 (plan §9 R2). Consumed from # JitPack, which builds the two Apache-2.0 submodules of github.com/termux/termux-app # on demand. Version is the git TAG (v-prefixed). Pinned to v0.118.0 (the plan's # default). NEVER pull termux-shared / the app module (GPLv3). terminal-view's only # transitive deps are terminal-emulator + androidx.annotation (no guava → the R2 # listenablefuture conflict dep is unnecessary here). termux = "v0.118.0" # androidx-test — lets androidTest (instrumented) sources COMPILE here; they only # RUN on a device/emulator (plan §7 — the A34 E2E suite needs a real `npm start` host). androidxTestExtJunit = "1.2.1" androidxTestCore = "1.6.1" androidxTestRunner = "1.6.2" androidxTestRules = "1.6.1" # Espresso + UiAutomator drive the A35 happy path (in-process Compose assertions and # out-of-process device interaction respectively). Pinned to the androidx-test 1.6 line. espresso = "3.6.1" uiautomator = "2.3.0" # Macrobenchmark (A35) — startup/frame timing measured from a SEPARATE `com.android.test` # module against a non-debuggable, minified `benchmark` variant of :app. benchmark = "1.3.4" # QR pairing (A19): CameraX preview/analysis + on-device ML Kit barcode scanning. camerax = "1.4.1" mlkitBarcode = "17.3.0" # Push (A30) + nav/deep-links (A32). FCM via the Firebase BOM (no google-services plugin here — # runtime init needs google-services.json, which is device-QA; classes compile without it). firebaseBom = "33.7.0" biometric = "1.1.0" navigationCompose = "2.8.5" [libraries] # Serialization kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerialization" } # Coroutines kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "kotlinxCoroutines" } kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "kotlinxCoroutines" } # The Android Main-dispatcher provider — REQUIRED at runtime by any Android module that # references Dispatchers.Main(.immediate) (else "Module with the Main dispatcher is missing"). kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "kotlinxCoroutines" } # Test — JUnit5 (Jupiter) junit-jupiter = { module = "org.junit.jupiter:junit-jupiter", version.ref = "junit" } junit-platform-launcher = { module = "org.junit.platform:junit-platform-launcher", version.ref = "junitPlatform" } # Test — Flow assertions + mocking turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" } mockk = { module = "io.mockk:mockk", version.ref = "mockk" } # OkHttp (WS + REST) — :transport-okhttp (A7). Pure JVM. okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" } okhttp-mockwebserver = { module = "com.squareup.okhttp3:mockwebserver", version.ref = "okhttp" } # ── Android UI stack (A13) — SDK-gated modules (:app etc.) only ────────────────── # AndroidX foundation androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "androidxCoreKtx" } androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivityCompose" } androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidxLifecycle" } androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidxLifecycle" } androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidxLifecycle" } # Compose — the BOM governs every version below (do NOT pin these individually). androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" } androidx-compose-ui = { module = "androidx.compose.ui:ui" } androidx-compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics" } androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } androidx-compose-foundation = { module = "androidx.compose.foundation:foundation" } androidx-compose-material3 = { module = "androidx.compose.material3:material3" } # Material 3 Adaptive (size-class split — SwiftUI-adaptive analogue, plan §"iPad-equivalent") androidx-compose-material3-adaptive = { module = "androidx.compose.material3.adaptive:adaptive" } androidx-compose-material3-adaptive-layout = { module = "androidx.compose.material3.adaptive:adaptive-layout" } androidx-compose-material3-adaptive-navigation = { module = "androidx.compose.material3.adaptive:adaptive-navigation" } androidx-compose-material3-adaptive-navigation-suite = { module = "androidx.compose.material3:material3-adaptive-navigation-suite" } # Hilt (Dagger) — one shared OkHttpClient singleton + testable seams (plan §2 DI row). hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } hilt-compiler = { module = "com.google.dagger:hilt-compiler", version.ref = "hilt" } androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" } # Storage + crypto (A11/A12) tink-android = { module = "com.google.crypto.tink:tink-android", version.ref = "tink" } androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" } # Terminal renderer (A16) — Apache-2.0 Termux libs via JitPack (see [versions].termux). termux-terminal-view = { module = "com.github.termux.termux-app:terminal-view", version.ref = "termux" } termux-terminal-emulator = { module = "com.github.termux.termux-app:terminal-emulator", version.ref = "termux" } # Instrumented-test deps (androidTest source sets; compile here, run on device) androidx-test-ext-junit = { module = "androidx.test.ext:junit", version.ref = "androidxTestExtJunit" } androidx-test-core = { module = "androidx.test:core", version.ref = "androidxTestCore" } androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidxTestRunner" } androidx-test-rules = { module = "androidx.test:rules", version.ref = "androidxTestRules" } androidx-test-espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espresso" } androidx-test-uiautomator = { module = "androidx.test.uiautomator:uiautomator", version.ref = "uiautomator" } # Compose UI test — BOM-managed (do NOT pin). `ui-test-manifest` is a debugImplementation: # it supplies the empty activity `createComposeRule()` launches into. androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4" } androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" } # Hilt instrumented testing — supplies `HiltAndroidRule` + the generated # `dagger.hilt.android.testing.HiltTestApplication` that :app's custom runner installs. # REQUIRES kspAndroidTest(hilt-compiler); without the processor HiltTestApplication is # never generated and the runner will not compile. hilt-android-testing = { module = "com.google.dagger:hilt-android-testing", version.ref = "hilt" } # Macrobenchmark (A35) — used by the :macrobenchmark `com.android.test` module only. androidx-benchmark-macro-junit4 = { module = "androidx.benchmark:benchmark-macro-junit4", version.ref = "benchmark" } # QR pairing camera (A19) androidx-camera-core = { module = "androidx.camera:camera-core", version.ref = "camerax" } androidx-camera-camera2 = { module = "androidx.camera:camera-camera2", version.ref = "camerax" } androidx-camera-lifecycle = { module = "androidx.camera:camera-lifecycle", version.ref = "camerax" } androidx-camera-view = { module = "androidx.camera:camera-view", version.ref = "camerax" } mlkit-barcode-scanning = { module = "com.google.mlkit:barcode-scanning", version.ref = "mlkitBarcode" } # Push (A30) + nav (A32) firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } firebase-messaging = { module = "com.google.firebase:firebase-messaging" } androidx-biometric = { module = "androidx.biometric:biometric", version.ref = "biometric" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } [bundles] # Convenience bundle wired into every pure module's testImplementation. unit-test = ["junit-jupiter", "kotlinx-coroutines-test", "turbine", "mockk"] # CameraX (A19 QR pairing) — preview + analysis + PreviewView. camerax = ["androidx-camera-core", "androidx-camera-camera2", "androidx-camera-lifecycle", "androidx-camera-view"] # Instrumented (androidTest) foundation — wired into :app's androidTestImplementation. # JUnit4 arrives transitively via androidx.test.ext:junit (AndroidJUnitRunner is JUnit4-only; # the JVM unit tests stay on the JUnit5 platform, which instrumented tasks never touch). android-instrumented-test = [ "androidx-test-ext-junit", "androidx-test-core", "androidx-test-runner", "androidx-test-rules", "androidx-test-espresso-core", ] # Compose UI + Material 3 (+ Adaptive) — wired into :app implementation. All # BOM-managed; add ui-tooling separately as a debugImplementation. compose = [ "androidx-compose-ui", "androidx-compose-ui-graphics", "androidx-compose-ui-tooling-preview", "androidx-compose-foundation", "androidx-compose-material3", "androidx-compose-material3-adaptive", "androidx-compose-material3-adaptive-layout", "androidx-compose-material3-adaptive-navigation", "androidx-compose-material3-adaptive-navigation-suite", ] [plugins] kotlin-jvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" } kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } android-library = { id = "com.android.library", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" } # NOTE: there is deliberately NO `android-test` alias for :macrobenchmark's # `com.android.test` plugin. The two aliases above put AGP on the build classpath with # an "unknown version", so a THIRD versioned alias for the same artifact is rejected # ("plugin is already on the classpath with an unknown version"). :macrobenchmark # therefore applies `id("com.android.test")` bare and inherits the `agp` version above — # exactly the pattern :terminal-view uses for `id("com.android.library")`. kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" } # NOTE: AGP 9+ has BUILT-IN Kotlin support — Android modules apply only the # android plugin, NOT a separate org.jetbrains.kotlin.android (it errors out). # # Compose compiler plugin — REQUIRED for Kotlin 2.x Compose; version MUST equal the # Kotlin version (the compiler ships inside the Kotlin release since 2.0). compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } # KSP — Hilt's annotation processor runs through it. ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } # Hilt Gradle plugin (component-tree codegen); version tracks the hilt libraries. hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }