From 5039b90bc68cd73221e4713f4c91cf88b2c2d2d9 Mon Sep 17 00:00:00 2001 From: Mathieu Moran Date: Sat, 15 Aug 2026 20:44:45 -0400 Subject: [PATCH] feat(android): release signing + R8-verified signed APK (Phase 28.8) --- PLAN.md | 15 +++++++-------- android/.gitignore | 2 ++ android/app/build.gradle.kts | 21 +++++++++++++++++++++ android/plan.md | 26 +++++++++++++------------- 4 files changed, 43 insertions(+), 21 deletions(-) diff --git a/PLAN.md b/PLAN.md index 66dea30..954814a 100644 --- a/PLAN.md +++ b/PLAN.md @@ -2229,14 +2229,13 @@ The app reuses the existing Pi REST API surface with no changes: dark top bar. - Follow-up polish deferred: card shadows, blue `btn-primary` buttons, page titles. -**28.8 — Polish + distribution** -- App icon, splash screen, dark theme polish -- Error handling: Pi unreachable, server unreachable, no cellular data, storage full -- APK signing + release build -- Distribution: APK sideload (simplest — host the APK on the Pi itself at - `http://yaris.local/app/yaris-obd.apk` so the user just opens that URL on the phone - to install/update). Play Store is out of scope (Google developer account fee, review - process, updates lag behind development). +**28.8 — Polish + distribution 🚧 IN PROGRESS (2026-08-15)** +- ✅ App icon (adaptive, red bg + white gauge), native splash screen (API 31+), "Yaris OBD" + label, OS-following dark mode. +- ✅ Release build: local signing keystore (gitignored), R8-verified — `app-release.apk` + (~43 MB) installs and launches on the Pixel. +- ⬜ Distribution: host the APK on the Pi (`GET /app` → `static/app/` + download page). + Play Store out of scope. #### Open questions (resolve during 28.1) diff --git a/android/.gitignore b/android/.gitignore index a58baf0..2633228 100644 --- a/android/.gitignore +++ b/android/.gitignore @@ -8,5 +8,7 @@ local.properties *.aab *.jks *.keystore +keystore/ +keystore.properties !gradle/wrapper/gradle-wrapper.jar !gradle/wrapper/gradle-wrapper.properties diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index f32d0e3..2599399 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -1,3 +1,6 @@ +import java.io.FileInputStream +import java.util.Properties + plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) @@ -6,6 +9,12 @@ plugins { alias(libs.plugins.ksp) } +// Release signing config loaded from keystore.properties (gitignored). +val keystorePropertiesFile = rootProject.file("keystore.properties") +val keystoreProperties = Properties().apply { + if (keystorePropertiesFile.exists()) load(FileInputStream(keystorePropertiesFile)) +} + android { namespace = "com.yarisobd.app" compileSdk = 35 @@ -20,6 +29,17 @@ android { testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" } + signingConfigs { + create("release") { + if (keystorePropertiesFile.exists()) { + storeFile = rootProject.file(keystoreProperties["storeFile"] as String) + storePassword = keystoreProperties["storePassword"] as String + keyAlias = keystoreProperties["keyAlias"] as String + keyPassword = keystoreProperties["keyPassword"] as String + } + } + } + buildTypes { debug { applicationIdSuffix = ".debug" @@ -32,6 +52,7 @@ android { getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro" ) + signingConfig = signingConfigs.getByName("release") } } diff --git a/android/plan.md b/android/plan.md index 9d826df..ed1926d 100644 --- a/android/plan.md +++ b/android/plan.md @@ -250,28 +250,28 @@ android/ - [ ] FontAwesome-style icons where they map to the web nav - [ ] Page titles (`h3 fw-bold`) at the top of each screen -### 28.8 — Polish + distribution ⬜ NOT STARTED +### 28.8 — Polish + distribution 🚧 IN PROGRESS **Depends on: 28.1–28.7 (whichever subset ships).** -#### 28.8.1 — App polish -- [ ] App icon (adaptive icon: foreground + background layers) -- [ ] Splash screen (Android 12+ SplashScreen API) -- [ ] Dark theme verified on all screens (default, car-friendly) -- [ ] Error handling audit: every network call has a user-visible error state + retry -- [ ] Accessibility: content descriptions on icons, reasonable touch targets (≥48dp) -- [ ] Commit: `feat: app polish` +#### 28.8.1 — App polish ✅ (2026-08-15) +- [x] App icon: adaptive icon (red `#e63946` bg + white gauge foreground) +- [x] Splash screen: native API 31+ (`values-v31/themes.xml`, dark bg + gauge icon) +- [x] Dark theme follows the OS setting (done in 28.7) +- [x] App label: "Yaris OBD" +- [ ] Error handling audit — most screens have error states; dashboard still shows "—" when offline +- [ ] Accessibility audit — content descriptions present on most icons; touch targets ≥48dp mostly -#### 28.8.2 — Build + distribution -- [ ] Release build signing key (keystore generated, not committed) -- [ ] ProGuard/R8 rules verified (Retrofit, OkHttp, Coil, MapLibre keep rules) -- [ ] APK output: `app/build/outputs/apk/release/app-release.apk` +#### 28.8.2 — Build + distribution 🚧 (release APK signed; hosting pending) +- [x] Release signing key: `android/keystore/release.keystore` (gitignored) + `keystore.properties` +- [x] ProGuard/R8 rules verified — release APK builds AND launches on the Pixel +- [x] APK output: `app/build/outputs/apk/release/app-release.apk` (~43 MB, signed) - [ ] Self-hosted distribution: - Copy APK to Pi: `scp app-release.apk yaris@yaris.local:~/app/yaris-obd/static/app/` - Add a lightweight Pi route: `GET /app` → serves `static/app/index.html` with download link + install instructions - Phone flow: connect to Pi hotspot → open `http://yaris.local/app` → download APK → install (user must enable "Install unknown apps" once) -- [ ] Commit: `feat: release build + self-hosted distribution` +- [ ] Commit: `feat: self-hosted distribution` ## Working notes