Files
echolot/echolot-app/app/build.gradle.kts
T
mrambossekandClaude Opus 5 c5f3c2e7af
server-release / image (push) Successful in 15s
server-release / release (push) Successful in 31s
app: learn probe durations per device; stop claiming VPN after teardown
The ETA table was fixed at compile time, but real durations are a property
of this phone and the network it stands in - ICMPv6 answers in
milliseconds where IPv6 works and waits out its timeout where it does
not. Estimates now prefer an EMA (70/30) of what this device actually
measured, seeded by the old constants on first run.

VPN detection had two lies in it, both found on hardware: a disconnected
tunnel lingers in allNetworks while tearing down, so 'VPN active' is now
judged from the ACTIVE network only; and any bind failure counted as
'per-network blocked', so a network dying mid-run flipped a healthy run
to INCONCLUSIVE - now only a genuine EPERM refusal counts. Banner and
finding split three ways (VPN + blocked / blocked only / VPN only) so the
app never names a VPN the user just turned off.

App version 0.2.3.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-02 13:41:10 +02:00

92 lines
4.0 KiB
Kotlin

// SPDX-FileCopyrightText: 2026 Echolot contributors
// SPDX-License-Identifier: GPL-3.0-or-later
plugins {
// AGP 9 built-in Kotlin — no kotlin.android here (see core-probe note).
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.kotlin.serialization)
}
// The app's version is SemVer and lives here, once. versionCode is derived from it rather than
// maintained alongside: Play/F-Droid need a monotonically increasing integer, but a second number
// that a human has to remember to bump is a number that eventually disagrees with the first — and
// the version is now load-bearing, since the server decides whether to serve us by it.
//
// major*1_000_000 + minor*10_000 + patch*10 leaves room for 9 patch-level rebuilds (the trailing
// digit) without disturbing the mapping, and stays inside the 2_100_000_000 ceiling until major 2100.
val appVersionName = "0.2.3"
fun versionCodeOf(semver: String): Int {
val (major, minor, patch) = semver.substringBefore('-').split(".").map(String::toInt)
return major * 1_000_000 + minor * 10_000 + patch * 10
}
android {
namespace = "app.echo_lot.app"
compileSdk = 36
defaultConfig {
applicationId = "app.echo_lot.app"
minSdk = 26
targetSdk = 36
versionCode = versionCodeOf(appVersionName)
versionName = appVersionName
// Automation: `adb shell am start -n app.echo_lot.app/.MainActivity --ez autorun true`
// runs a measurement immediately and POSTs the report here (dev collection endpoint).
// Empty: the collection endpoint this pointed at was the adb-beacon receiver, which held
// 0.0.0.0:443 in cleartext. That service is gone and echolot-server owns 443 with TLS, so
// posting plaintext there now fails as "client sent an HTTP request to an HTTPS server" —
// an alarming error for a debugging convenience that is no longer needed, since autorun
// reports are read straight off the device with `run-as cat`.
//
// Deliberately not repointed at /v1/runs. That is the consent-gated upload, and a
// debugging shortcut must not be able to satisfy it by accident.
buildConfigField("String", "REPORT_UPLOAD_URL", "\"\"")
buildConfigField("String", "REPORT_UPLOAD_SECRET", "\"\"")
// The bare SemVer, without the debug build's "-dev" suffix stripped away by the server's
// parser anyway — sent to servers so they can apply their compatibility window.
buildConfigField("String", "APP_SEMVER", "\"$appVersionName\"")
}
buildTypes {
release { isMinifyEnabled = false }
debug {
// The dev build is a separate app: own package (installs alongside a real
// Echolot), own label and a DEV-badged icon (src/debug/res).
applicationIdSuffix = ".dev"
versionNameSuffix = "-dev"
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
buildFeatures {
compose = true
buildConfig = true
}
}
dependencies {
implementation(project(":core-measurement"))
implementation(project(":core-protocol"))
implementation(project(":core-engine"))
implementation(project(":core-probe"))
implementation(project(":core-shizuku"))
implementation(project(":core-privacy"))
implementation(project(":core-archive"))
implementation(libs.kotlinx.serialization.json)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.viewmodel.compose)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
debugImplementation(libs.androidx.ui.tooling)
}