Settings -> App -> "Install from USB stick" (car build only). It looks in the top folder of every mounted volume (found from the app's own folder on it, since the head unit has no file picker) for NilsMG4-unstable-<version>.apk, takes the newest one newer than the installed version, checks it against its .sha256 sidecar and the package name, and installs it with the same PackageInstaller session as the over-the-air update. The stable build gets a stub and no button. Tested on the emulator: 2.18.0 installed a 2.18.1 from the storage's top folder without a confirmation, as the system user, so the OTA install step should not need one either. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QLJfZQZeAKAwSqViTmLzki
149 lines
6.1 KiB
Groovy
149 lines
6.1 KiB
Groovy
plugins {
|
|
alias(libs.plugins.android.application)
|
|
}
|
|
|
|
android {
|
|
namespace 'com.evsuite.abrp'
|
|
compileSdk {
|
|
version = release(36) {
|
|
minorApiLevel = 1
|
|
}
|
|
}
|
|
|
|
// APK signing — nothing to do with the user's ABRP credentials. These values are ONLY for
|
|
// the build: they prove the APK comes from us and never enter the app. The ABRP API key and
|
|
// token are entered in the app and stored encrypted on the device (see SecurePrefs).
|
|
// The car build is signed by build-car (flake.nix) with the AOSP platform key instead.
|
|
//
|
|
// A keystore holds several keys: SIGNING_STORE_PASSWORD opens the container,
|
|
// SIGNING_KEY_ALIAS picks the key, SIGNING_KEY_PASSWORD opens that key.
|
|
// Read from the environment (CI) or a local gradle.properties — NEVER committed.
|
|
def keystorePath = System.getenv("SIGNING_KEYSTORE") ?: project.findProperty("signing.keystore")
|
|
signingConfigs {
|
|
if (keystorePath != null && file(keystorePath).exists()) {
|
|
platform {
|
|
storeFile file(keystorePath)
|
|
storePassword System.getenv("SIGNING_STORE_PASSWORD") ?: project.findProperty("signing.store.password")
|
|
keyAlias System.getenv("SIGNING_KEY_ALIAS") ?: project.findProperty("signing.key.alias") ?: "platform"
|
|
keyPassword System.getenv("SIGNING_KEY_PASSWORD") ?: project.findProperty("signing.key.password")
|
|
}
|
|
}
|
|
}
|
|
|
|
defaultConfig {
|
|
applicationId "com.stinnesbeck.nilsmg4"
|
|
minSdk 27
|
|
targetSdk 36
|
|
versionCode 95
|
|
versionName "2.18.0"
|
|
|
|
// Nothing about the server ships in the APK: its URL and ingest token are entered in
|
|
// the app's Server tab (ServerConfig), and the database is only reachable by the server.
|
|
|
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
|
}
|
|
|
|
// null when no keystore is configured: the build still works on a machine without
|
|
// secrets (unsigned APK) instead of failing.
|
|
def platformSigning = signingConfigs.findByName("platform")
|
|
|
|
// Two distribution channels:
|
|
// - stable: no self-update at all. The user installs it; nothing in the app fetches an
|
|
// APK (the updater is not even in this APK).
|
|
// - unstable: the car build (system UID, platform-signed), with the OTA updater, so the
|
|
// car can update itself from published pre-releases. Installs next to stable.
|
|
flavorDimensions += "channel"
|
|
productFlavors {
|
|
stable {
|
|
dimension "channel"
|
|
buildConfigField "boolean", "OTA_ENABLED", "false"
|
|
}
|
|
unstable {
|
|
dimension "channel"
|
|
applicationIdSuffix ".unstable"
|
|
// Three-part versions, the same as defaultConfig ("2.8.1-unstable"). Upstream added
|
|
// a fourth part for its CI build number; there is no CI here and the OTA updater is
|
|
// off. The version code keeps upstream's x100000 so it never drops below codes
|
|
// already installed on the car (2.8.0.0 was 6600000): a lower code is a downgrade,
|
|
// which the installer refuses.
|
|
versionCode defaultConfig.versionCode * 100000
|
|
versionName defaultConfig.versionName
|
|
versionNameSuffix "-unstable"
|
|
buildConfigField "boolean", "OTA_ENABLED", "true"
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
debug {
|
|
if (platformSigning != null) signingConfig platformSigning
|
|
}
|
|
release {
|
|
// [T-918] R8 enabled. CarPropertyAdapter reaches the car purely by
|
|
// reflection, so proguard-rules.pro must keep those targets; a release build
|
|
// needs an on-vehicle check of the car reads before it ships.
|
|
minifyEnabled true
|
|
if (platformSigning != null) signingConfig platformSigning
|
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
|
}
|
|
}
|
|
// BuildConfig.DEBUG gates the debug-only logging (T-912).
|
|
buildFeatures {
|
|
buildConfig true
|
|
}
|
|
|
|
lint {
|
|
abortOnError true
|
|
checkReleaseBuilds true
|
|
warningsAsErrors false
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility JavaVersion.VERSION_11
|
|
targetCompatibility JavaVersion.VERSION_11
|
|
}
|
|
}
|
|
|
|
// The version build-car puts in the APK's file name ("NilsMG4-unstable-2.8.1.apk"), which
|
|
// is also the name the OTA updater reads a release's version from.
|
|
tasks.register('printUnstableVersion') {
|
|
doLast {
|
|
println android.defaultConfig.versionName
|
|
}
|
|
}
|
|
|
|
// Names the APKs by channel so the build outputs are readable.
|
|
androidComponents {
|
|
onVariants(selector().all()) { variant ->
|
|
variant.outputs.each { output ->
|
|
if (variant.buildType == "release") {
|
|
// A fixed name for the unstable release output. What is published for the car
|
|
// comes from build-car, named NilsMG4-unstable-<version>.apk: that file name is
|
|
// where the updater reads a release's version from.
|
|
output.outputFileName.set(variant.flavorName == "unstable"
|
|
? "NilsMG4-unstable.apk"
|
|
: "NilsMG4-${variant.flavorName}-${output.versionName.get()}.apk")
|
|
} else {
|
|
// Debug builds: NilsMG4-stable-debug.apk, NilsMG4-unstable-debug.apk.
|
|
output.outputFileName.set("NilsMG4-${variant.flavorName}-${variant.buildType}.apk")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
dependencies {
|
|
implementation project(':evhardware')
|
|
implementation libs.appcompat
|
|
implementation libs.material
|
|
implementation libs.activity
|
|
implementation libs.constraintlayout
|
|
implementation libs.viewpager2
|
|
implementation libs.security.crypto
|
|
implementation 'com.google.zxing:core:3.5.3'
|
|
// android.jar's org.json is a stub in unit tests; the real implementation lets the
|
|
// telemetry payload tests parse what the app actually builds.
|
|
testImplementation libs.junit
|
|
testImplementation libs.json
|
|
androidTestImplementation libs.ext.junit
|
|
androidTestImplementation libs.espresso.core
|
|
}
|