1plugins {2 id "com.android.application"3 id "org.jetbrains.kotlin.android"4 id "org.jetbrains.kotlin.plugin.compose"5 id "org.jetbrains.kotlin.plugin.parcelize"6 id "org.jetbrains.kotlin.plugin.serialization"7 id "kotlin-kapt"8 id "com.google.dagger.hilt.android"9}1011android {12 namespace "com.peachpasswords"13 compileSdk 351415 // VERSION is the single semantic-version authority. versionCode remains16 // Android/Gradle metadata because Kepr's manifest does not carry it; the17 // updater enforces exact VERSION plus a strictly newer code.18 def versionAuthorityFile = rootProject.file("VERSION")19 if (!versionAuthorityFile.isFile()) {20 throw new GradleException("VERSION is missing; it is the semantic-version authority")21 }22 def semanticVersion = versionAuthorityFile.getText("UTF-8").trim()23 if (!(semanticVersion ==~ /^(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)$/)) {24 throw new GradleException("VERSION must contain exactly one strict major.minor.patch value")25 }26 def versionParts = semanticVersion.split("\\.")27 def vMajor = versionParts[0].toInteger()28 def vMinor = versionParts[1].toInteger()29 def vPatch = versionParts[2].toInteger()30 if (vMajor > 99 || vMinor > 99 || vPatch > 999) {31 throw new GradleException("VERSION components exceed versionCode encoding range (major/minor <= 99, patch <= 999)")32 }33 // Derive a strictly-monotonic versionCode from semver so it can never34 // accidentally stay flat across releases (which breaks the updater's35 // versionCode > currentVersionCode identity gate).36 def derivedVersionCode = vMajor * 1000000 + vMinor * 10000 + vPatch * 103738 defaultConfig {39 applicationId "com.peachpasswords"40 minSdk 2641 targetSdk 3542 versionCode derivedVersionCode43 versionName semanticVersion4445 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"46 vectorDrawables {47 useSupportLibrary true48 }49 }5051 signingConfigs {52 release {53 // Release authority comes only from process-scoped protected54 // injection. local.properties is limited to non-secret local SDK55 // configuration and is never a signing-secret store.56 def signingEnvironment = [57 storeFile: System.getenv("PEACH_RELEASE_STORE_FILE"),58 storePassword: System.getenv("PEACH_RELEASE_STORE_PASSWORD"),59 keyAlias: System.getenv("PEACH_RELEASE_KEY_ALIAS"),60 keyPassword: System.getenv("PEACH_RELEASE_KEY_PASSWORD"),61 ]62 def configuredValues = signingEnvironment.values().count { it != null && !it.isEmpty() }63 if (configuredValues != 0 && configuredValues != signingEnvironment.size()) {64 throw new GradleException("incomplete protected release signing environment")65 }66 if (configuredValues == signingEnvironment.size()) {67 storeFile file(signingEnvironment.storeFile)68 storePassword signingEnvironment.storePassword69 keyAlias signingEnvironment.keyAlias70 keyPassword signingEnvironment.keyPassword71 }72 // v3 is the only escape hatch from "this key is forever": it carries a73 // rotation lineage, so a future key can be proven to descend from this74 // one instead of forcing every tester to uninstall and lose their75 // vault. AGP leaves it off unless asked.76 enableV2Signing true77 enableV3Signing true78 }79 }8081 splits {82 abi {83 enable true84 reset()85 include "arm64-v8a", "x86_64"86 universalApk false87 }88 }8990 buildTypes {91 debug {92 applicationIdSuffix ".debug"93 versionNameSuffix "-debug"94 debuggable true95 }96 release {97 signingConfig signingConfigs.release98 minifyEnabled false99 proguardFiles getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro"100 }101 }102103 // Never let a release task silently emit an unsigned artifact. Debug tasks104 // remain usable in fresh workspaces that do not hold release authority.105 gradle.taskGraph.whenReady { graph ->106 if (graph.allTasks.any { it.project == project && it.name.toLowerCase().contains("release") } &&107 !signingConfigs.release.isSigningReady()) {108 throw new GradleException(109 "release signing requires PEACH_RELEASE_STORE_FILE, " +110 "PEACH_RELEASE_STORE_PASSWORD, PEACH_RELEASE_KEY_ALIAS, and " +111 "PEACH_RELEASE_KEY_PASSWORD",112 )113 }114 }115116 compileOptions {117 sourceCompatibility JavaVersion.VERSION_17118 targetCompatibility JavaVersion.VERSION_17119 }120121 kotlinOptions {122 jvmTarget = "17"123 }124125 buildFeatures {126 compose true127 buildConfig true128 }129130 composeOptions {131 kotlinCompilerExtensionVersion "1.5.15"132 }133134 packaging {135 resources {136 excludes += [137 "/META-INF/{AL2.0,LGPL2.1}",138 "META-INF/DEPENDENCIES",139 "META-INF/LICENSE*",140 "META-INF/NOTICE*",141 ]142 }143 }144145 testOptions {146 unitTests.returnDefaultValues = true147 unitTests.all {148 def coreDir = rootProject.file("../peach-browser/crypto-core")149 def nativeLibrary = new File(150 coreDir,151 "target/release/libpeach_noise_core.dylib",152 )153 if (coreDir.exists()) {154 dependsOn rootProject.tasks.named("buildRustNoiseHost")155 systemProperty "peach.noise.library.path", nativeLibrary.absolutePath156 }157 }158 }159}160161dependencies {162 implementation platform("androidx.compose:compose-bom:2024.06.00")163 androidTestImplementation platform("androidx.compose:compose-bom:2024.06.00")164165 implementation "androidx.activity:activity-compose:1.9.2"166 implementation "androidx.autofill:autofill:1.1.0"167 implementation "androidx.biometric:biometric:1.1.0"168 implementation "androidx.core:core-splashscreen:1.0.1"169 implementation "androidx.appcompat:appcompat:1.7.0"170 implementation "androidx.credentials:credentials:1.5.0"171 implementation "androidx.credentials:credentials-play-services-auth:1.5.0"172 implementation "androidx.glance:glance-appwidget:1.1.0"173 implementation "androidx.glance:glance-material3:1.1.0"174 implementation "androidx.hilt:hilt-navigation-compose:1.2.0"175 // In-app updates: a periodic release check that survives Doze without a176 // service. hilt-work lets the worker take UpdateRepository by injection177 // rather than reaching for a global.178 implementation "androidx.hilt:hilt-work:1.2.0"179 kapt "androidx.hilt:hilt-compiler:1.2.0"180 implementation "androidx.work:work-runtime-ktx:2.9.1"181 implementation "androidx.lifecycle:lifecycle-runtime-compose:2.8.4"182 implementation "androidx.lifecycle:lifecycle-viewmodel-compose:2.8.4"183 implementation "androidx.lifecycle:lifecycle-process:2.8.4"184 implementation "androidx.navigation:navigation-compose:2.8.0"185 // Deliberately pinned to 1.1.0-alpha06 (audit M-12). 1.1.0 stable exists186 // now and GradleDependency lint flags this coordinate; a bump is a policy187 // choice (Tink runtime vs. baseline AES, master-key rotation semantics),188 // not an oversight, and is tracked separately. Exact alpha (never a floating189 // range) so the resolved artifact is reproducible. Used only for the190 // duress/license EncryptedSharedPreferences; the vault slots are sealed by191 // VaultCrypto, not this.192 implementation "androidx.security:security-crypto:1.1.0-alpha06"193 implementation "androidx.compose.material3:material3"194 implementation "androidx.compose.ui:ui"195 implementation "androidx.compose.ui:ui-tooling-preview"196 implementation "androidx.compose.foundation:foundation"197 implementation "androidx.compose.material:material-icons-extended"198 implementation "androidx.core:core-ktx:1.13.1"199 implementation "com.google.android.material:material:1.12.0"200201 implementation "com.google.dagger:hilt-android:2.51.1"202 kapt "com.google.dagger:hilt-compiler:2.51.1"203 implementation "com.google.zxing:core:3.5.3"204 implementation "androidx.camera:camera-core:1.3.4"205 implementation "androidx.camera:camera-camera2:1.3.4"206 implementation "androidx.camera:camera-lifecycle:1.3.4"207 implementation "androidx.camera:camera-view:1.3.4"208 implementation "com.squareup.okhttp3:okhttp:4.12.0"209 implementation "org.bouncycastle:bcprov-jdk18on:1.84"210 // zstd-jni (reference libzstd) — per-section compression for the PeachScript211 // PCHS binary format (MF-2). Required for cross-platform interop with the212 // browser's zstd sections. The @aar variant ships the Android jniLibs213 // (lib/<abi>/libzstd-jni.so); the plain jar bundles only desktop natives and214 // would fail with UnsatisfiedLinkError on-device.215 implementation "com.github.luben:zstd-jni:1.5.6-3@aar"216 // The @aar ships only Android .so; the plain jar carries the desktop natives217 // the host JVM unit tests need.218 testImplementation "com.github.luben:zstd-jni:1.5.6-3"219 implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1"220 implementation "org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.1"221222 debugImplementation "androidx.compose.ui:ui-tooling"223 debugImplementation "androidx.compose.ui:ui-test-manifest"224225 androidTestImplementation "androidx.test.ext:junit:1.2.1"226 androidTestImplementation "androidx.test:core:1.6.1"227 androidTestImplementation "androidx.test.espresso:espresso-core:3.6.1"228 androidTestImplementation "androidx.test.uiautomator:uiautomator:2.3.0"229 androidTestImplementation "androidx.compose.ui:ui-test-junit4"230231 testImplementation "junit:junit:4.13.2"232 // Real org.json on the unit-test classpath; android.jar's is a throwing stub,233 // so the release-manifest parser would otherwise be untestable off-device.234 testImplementation "org.json:json:20240303"235 testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.8.1"236}