Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,11 @@ local.properties
.DS_Store
captures/
.kotlin/
CLAUDE.md

# Release signing — never commit keystores or their credentials.
store-assets/
app/keystore.properties
*.jks
*.keystore
*.pepk
66 changes: 64 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import java.util.Properties

plugins {
// AGP 9 built-in Kotlin (see :metawear-core) — org.jetbrains.kotlin.android
// must NOT be applied. The Compose compiler Gradle plugin (2.2.10, matching
Expand All @@ -6,6 +8,21 @@ plugins {
alias(libs.plugins.kotlin.compose)
}

// Release signing reads from an UNTRACKED file so no secret ever enters git.
// Create app/keystore.properties (gitignored) with:
// storeFile=/absolute/path/to/mbientlab_android_apps.jks
// storePassword=…
// keyAlias=…
// keyPassword=…
// When the file is absent (CI, fresh clones) the release build type still
// assembles — unsigned — so the AAB task exists everywhere and only a machine
// holding the upload key can produce a signed artifact.
val keystoreProperties = Properties().apply {
val f = rootProject.file("app/keystore.properties")
if (f.exists()) f.inputStream().use { load(it) }
}
val hasReleaseKeystore = keystoreProperties.getProperty("storeFile") != null

// Jetpack Compose MetaWear app: scanning, live streaming with ring-buffer
// decimation, on-device logging + download, session history with CSV export,
// LED/haptic controls, device settings, and firmware updates.
Expand All @@ -17,8 +34,53 @@ android {
applicationId = "com.mbientlab.metawear.app"
minSdk = 26
targetSdk = 36
versionCode = 1
versionName = "1.0"
// This package already ships on Google Play (the previous-generation
// app reached versionCode 25 / 4.0.0). Play rejects any upload whose
// versionCode is not higher than the live one, so the new app line
// starts at 100 / 5.0.0 — clear headroom above the old series and an
// unmistakable major bump for existing users.
versionCode = 100
versionName = "5.0.0"
}

signingConfigs {
if (hasReleaseKeystore) {
create("release") {
storeFile = file(keystoreProperties.getProperty("storeFile"))
storePassword = keystoreProperties.getProperty("storePassword")
keyAlias = keystoreProperties.getProperty("keyAlias")
keyPassword = keystoreProperties.getProperty("keyPassword")
}
}
}

buildTypes {
release {
// R8: shrink + obfuscate. Keep rules for Room, the Nordic BLE/DFU
// libraries, and Compose live in proguard-rules.pro.
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
// Without an upload keystore on this machine, sign with the debug
// key so the minified build can still be installed and smoke-tested
// on a device. Play never sees this artifact — the store AAB is
// produced only where keystore.properties exists.
signingConfig = if (hasReleaseKeystore) signingConfigs.getByName("release") else signingConfigs.getByName("debug")
}
debug {
// Distinct id so a debug build can sit beside the store app.
applicationIdSuffix = ".debug"
versionNameSuffix = "-debug"
}
}

bundle {
// Let Play deliver per-device splits; language splits stay off so
// the app is fully usable offline in any locale (single-language app).
language.enableSplit = false
}

buildFeatures {
Expand Down
32 changes: 32 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# R8 keep rules for the MetaWear app release build.

# --- Room ---------------------------------------------------------------
# Entities / DAOs are reached via generated code and reflection-free, but the
# @Database subclass and its generated _Impl are located by name.
-keep class * extends androidx.room.RoomDatabase { <init>(); }
-keep @androidx.room.Entity class * { *; }
-dontwarn androidx.room.paging.**

# --- Nordic Kotlin BLE library + Android DFU library --------------------
# The DFU library instantiates the app's DfuBaseService subclass by name and
# talks to it over broadcasts; the BLE library uses reflection-free coroutines
# but ships some optional pieces R8 warns about.
-keep class com.mbientlab.metawear.firmware.MetaWearDfuService { *; }
-keep class no.nordicsemi.android.dfu.** { *; }
-dontwarn no.nordicsemi.android.**

# --- kotlinx.coroutines / datetime ---------------------------------------
-dontwarn kotlinx.coroutines.**
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler
-keepclassmembers class kotlinx.coroutines.** { volatile <fields>; }
-dontwarn kotlinx.datetime.**

# --- App: keep the persisted-record codec's data classes stable ------------
# LogSessionRecord / RememberedDevice round-trip through hand-rolled string
# codecs (no reflection), so nothing here needs keeping — listed for clarity.

# --- General ---------------------------------------------------------------
-keepattributes *Annotation*, Signature, InnerClasses, EnclosingMethod
-keepattributes SourceFile, LineNumberTable
-renamesourcefileattribute SourceFile
Loading