build.gradle.kts
| 1 | import java.util.Properties |
| 2 | |
| 3 | // The only build file in the project. AGP 9 ships Kotlin built in, so there is |
| 4 | // no `org.jetbrains.kotlin.android` line; and at one module a root build file and |
| 5 | // a version catalog would both be pure ceremony. Versions are pinned exactly — |
| 6 | // no `+`, no version ranges — because reproducible release builds are a goal from |
| 7 | // day one. |
| 8 | // |
| 9 | // The Compose compiler plugin is *not* implied by AGP's built-in Kotlin, despite |
| 10 | // what the "built-in Kotlin" framing suggests: enabling `buildFeatures.compose` |
| 11 | // without it fails configuration outright. Its version must equal the KGP version |
| 12 | // AGP bundles — 2.2.10 for AGP 9.2.1. Verify after any AGP bump with: |
| 13 | // ./gradlew :app:buildEnvironment | grep kotlin-gradle-plugin |
| 14 | plugins { |
| 15 | id("com.android.application") version "9.2.1" |
| 16 | id("org.jetbrains.kotlin.plugin.compose") version "2.2.10" |
| 17 | } |
| 18 | |
| 19 | android { |
| 20 | namespace = "net.lexcom.opentracker" |
| 21 | compileSdk = 36 |
| 22 | |
| 23 | defaultConfig { |
| 24 | applicationId = "net.lexcom.opentracker" |
| 25 | // 29 is the first API with the 3-arg startForeground() overload, which |
| 26 | // is what lets us run a location foreground service without any |
| 27 | // compat library. |
| 28 | minSdk = 29 |
| 29 | targetSdk = 36 |
| 30 | versionCode = 1 |
| 31 | versionName = "0.1.0" |
| 32 | } |
| 33 | |
| 34 | buildFeatures { |
| 35 | compose = true |
| 36 | // Off by default since AGP 8; we read FLAG_DEBUGGABLE instead of |
| 37 | // generating a class for one boolean. |
| 38 | buildConfig = false |
| 39 | } |
| 40 | |
| 41 | compileOptions { |
| 42 | sourceCompatibility = JavaVersion.VERSION_21 |
| 43 | targetCompatibility = JavaVersion.VERSION_21 |
| 44 | } |
| 45 | |
| 46 | buildTypes { |
| 47 | debug { |
| 48 | // So a debug build can sit next to a release build on the same |
| 49 | // device. Referenced by the adb commands in the README. |
| 50 | applicationIdSuffix = ".debug" |
| 51 | } |
| 52 | release { |
| 53 | // No reflection anywhere in this app, and Compose/AndroidX ship |
| 54 | // consumer ProGuard rules, so minification can be turned on later |
| 55 | // without ever creating a proguard-rules.pro. Costs a few MB of APK. |
| 56 | isMinifyEnabled = false |
| 57 | signingConfig = signingConfigs.findByName("release") |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | signingConfigs { |
| 62 | // Release signing is configured only when the (gitignored) properties |
| 63 | // file exists, so a fresh clone can still build debug. |
| 64 | val props = rootProject.file("keystore.properties") |
| 65 | if (props.exists()) { |
| 66 | create("release") { |
| 67 | val p = Properties().apply { props.inputStream().use(::load) } |
| 68 | storeFile = rootProject.file(p.getProperty("storeFile")) |
| 69 | storePassword = p.getProperty("storePassword") |
| 70 | keyAlias = p.getProperty("keyAlias") |
| 71 | keyPassword = p.getProperty("keyPassword") |
| 72 | // v1 signatures are only needed below API 24. Schemes v2/v3 are |
| 73 | // enough at minSdk 29 and keep the APK's zip entries untouched. |
| 74 | enableV1Signing = false |
| 75 | enableV2Signing = true |
| 76 | enableV3Signing = true |
| 77 | } |
| 78 | } |
| 79 | } |
| 80 | |
| 81 | lint { |
| 82 | disable += setOf( |
| 83 | // There is no res/values/strings.xml on purpose: this app is not |
| 84 | // localized, and UI strings live as Kotlin constants next to the |
| 85 | // code that uses them. |
| 86 | "HardcodedText", |
| 87 | "MissingDefaultResource", |
| 88 | |
| 89 | // "a newer version is available" checks. Every version here is |
| 90 | // pinned deliberately, for reproducible builds; being told daily |
| 91 | // that a newer one exists is noise, and with warningsAsErrors it |
| 92 | // would break the build the moment Google publishes anything. |
| 93 | // Upgrades are a decision, not a lint finding. |
| 94 | "AndroidGradlePluginVersion", |
| 95 | "GradleDependency", |
| 96 | "NewerVersionAvailable", |
| 97 | "OldTargetApi", |
| 98 | |
| 99 | // Suggests replacing android:allowBackup="false" with a |
| 100 | // dataExtractionRules XML resource. There is nothing to configure: |
| 101 | // this app's data directory holds the device's token key, and |
| 102 | // backup/transfer of it is exactly what must not happen. Following |
| 103 | // the advice would add a res/ file that says "back up nothing". |
| 104 | "DataExtractionRules", |
| 105 | ) |
| 106 | // A lint failure should stop the build rather than scroll past. |
| 107 | warningsAsErrors = true |
| 108 | abortOnError = true |
| 109 | } |
| 110 | |
| 111 | packaging { |
| 112 | resources.excludes += setOf( |
| 113 | "META-INF/{AL2.0,LGPL2.1}", |
| 114 | "DebugProbesKt.bin", |
| 115 | ) |
| 116 | } |
| 117 | |
| 118 | testOptions { |
| 119 | unitTests.isReturnDefaultValues = true |
| 120 | } |
| 121 | |
| 122 | sourceSets["test"].resources.srcDir("../../crates/otproto/tests") |
| 123 | } |
| 124 | |
| 125 | dependencies { |
| 126 | implementation(platform("androidx.compose:compose-bom:2026.06.01")) |
| 127 | implementation("androidx.compose.material3:material3") |
| 128 | implementation("androidx.activity:activity-compose:1.13.0") |
| 129 | implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.11.0") |
| 130 | // The map. FOSS, no Play Services, raster tiles, points straight at our own |
| 131 | // /tiles proxy. Hosted in Compose via AndroidView. |
| 132 | implementation("org.osmdroid:osmdroid-android:6.1.20") |
| 133 | |
| 134 | // Everything non-trivial in this app (sampling policy, frame codec, queue, |
| 135 | // AEAD) is pure Kotlin and tested on the JVM. No androidTest/, no emulator |
| 136 | // in the loop. |
| 137 | // Spelled out rather than `kotlin("test")`: AGP's built-in Kotlin does not |
| 138 | // apply the Kotlin Gradle plugin's version-inferring `kotlin()` helper, so |
| 139 | // that form resolves to a versionless coordinate and silently contributes |
| 140 | // nothing. The `-junit` variant brings the JUnit 4 runner AGP's unit tests |
| 141 | // expect. Version must track the KGP that AGP bundles. |
| 142 | testImplementation("org.jetbrains.kotlin:kotlin-test-junit:2.2.10") |
| 143 | // Test-only. The app itself parses its one JSON response (the login reply) |
| 144 | // with the framework's org.json, but unit tests run against android.jar |
| 145 | // stubs where those methods return defaults, so the golden-vector test needs |
| 146 | // a real parser. Never reaches the APK. |
| 147 | testImplementation("org.json:json:20260719") |
| 148 | } |
| 149 |