| 57c08243 Andy McCall ↶ |
1 |
import java.io.File |
| 2 |
import java.util.Properties |
| 3 |
|
| e0e4a1c4 Andy McCall ↶ |
4 |
plugins { |
| 5 |
alias(libs.plugins.android.application) |
| 6 |
alias(libs.plugins.kotlin.compose) |
| 5ae4af48 Andy McCall ↶ |
7 |
alias(libs.plugins.kotlin.serialization) |
| 0a2858c0 Andy McCall ↶ |
8 |
alias(libs.plugins.screenshot) |
| e0e4a1c4 Andy McCall ↶ |
9 |
} |
| 57c08243 Andy McCall ↶ |
10 |
|
| 11 |
// The Rust core (rust-core/, over oxy-core) is built for the app's ABIs by |
| 12 |
// cargo-ndk and its Kotlin bindings generated by uniffi at build time |
| 13 |
// (oxygenhub-android#1): nothing built is kept in the repository. |
| 14 |
val rustCore: File = rootProject.file("rust-core") |
| 15 |
val rustJniLibs = layout.buildDirectory.dir("rustJniLibs") |
| 16 |
val rustBindings = layout.buildDirectory.dir("generated/uniffi/kotlin") |
| 17 |
|
| 18 |
fun androidNdk(): String { |
| 19 |
System.getenv("ANDROID_NDK_HOME")?.let { return it } |
| 20 |
val props = Properties() |
| 21 |
val local = rootProject.file("local.properties") |
| 22 |
if (local.exists()) local.inputStream().use { input -> props.load(input) } |
| 23 |
val sdk = props.getProperty("sdk.dir") ?: System.getenv("ANDROID_HOME") |
| 24 |
?: error("set sdk.dir in local.properties or ANDROID_HOME") |
| 25 |
val ndks = File(sdk, "ndk").listFiles()?.filter { f -> f.isDirectory }?.sortedBy { f -> f.name }.orEmpty() |
| 26 |
return ndks.lastOrNull()?.path ?: error("no NDK under $sdk/ndk: install one with the SDK manager") |
| 27 |
} |
| 28 |
|
| 29 |
val buildRustCore = tasks.register("buildRustCore", Exec::class) { |
| 30 |
description = "Builds rust-core for arm64-v8a and x86_64 with cargo-ndk." |
| 31 |
workingDir = rustCore |
| 32 |
inputs.dir(rustCore.resolve("src")) |
| 33 |
inputs.files(rustCore.resolve("Cargo.toml"), rustCore.resolve("Cargo.lock")) |
| 34 |
outputs.dir(rustJniLibs) |
| 35 |
environment("ANDROID_NDK_HOME", androidNdk()) |
| 36 |
commandLine("cargo", "ndk", "-t", "arm64-v8a", "-t", "x86_64", "--platform", "31", "-o", rustJniLibs.get().asFile.path, "build", "--release") |
| 37 |
} |
| 38 |
|
| 39 |
val generateRustBindings = tasks.register("generateRustBindings", Exec::class) { |
| 40 |
description = "Generates rust-core's Kotlin bindings with uniffi." |
| 41 |
dependsOn(buildRustCore) |
| 42 |
workingDir = rustCore |
| 43 |
inputs.dir(rustJniLibs) |
| 1dfa6314 Andy McCall ↶ |
44 |
inputs.file(rustCore.resolve("uniffi.toml")) |
| 57c08243 Andy McCall ↶ |
45 |
outputs.dir(rustBindings) |
| 46 |
commandLine( |
| 47 |
"cargo", "run", "--release", "--bin", "uniffi-bindgen", "generate", |
| 48 |
"--library", rustJniLibs.get().file("arm64-v8a/librust_core.so").asFile.path, |
| 49 |
"--language", "kotlin", "--out-dir", rustBindings.get().asFile.path, |
| 50 |
) |
| 51 |
} |
| 52 |
|
| 53 |
tasks.named("preBuild") { dependsOn(generateRustBindings) } |
| e0e4a1c4 Andy McCall ↶ |
54 |
|
| aa21eb5e Andy McCall ↶ |
55 |
// The Firebase client settings alerts need (#16), read from |
| 56 |
// app/google-services.json without Google's Gradle plugin: none, no alerts. |
| 57 |
val firebase: Map<String, String> = run { |
| 58 |
val file = File(projectDir, "google-services.json") |
| 59 |
if (!file.isFile) return@run emptyMap() |
| 60 |
@Suppress("UNCHECKED_CAST") |
| 61 |
val json = groovy.json.JsonSlurper().parse(file) as Map<String, Any?> |
| 62 |
val project = json["project_info"] as Map<String, Any?> |
| 63 |
val client = (json["client"] as List<Map<String, Any?>>).first { |
| 64 |
((it["client_info"] as Map<String, Any?>)["android_client_info"] as Map<String, Any?>)["package_name"] == "io.oxygenhub.android" |
| 65 |
} |
| 66 |
mapOf( |
| 67 |
"app_id" to (client["client_info"] as Map<String, Any?>)["mobilesdk_app_id"].toString(), |
| 68 |
"project_id" to project["project_id"].toString(), |
| 69 |
"sender_id" to project["project_number"].toString(), |
| 70 |
"api_key" to ((client["api_key"] as List<Map<String, Any?>>).first()["current_key"]).toString(), |
| 71 |
) |
| 72 |
} |
| 73 |
|
| bfa2eff0 Andy McCall ↶ |
74 |
// The version, from gradle.properties (#7). |
| 75 |
val appVersion: String = providers.gradleProperty("oxygenhub.version").get() |
| 76 |
val appVersionCode: Int = appVersion.split(".").map { it.toInt() }.let { (a, b, c) -> a * 10000 + b * 100 + c } |
| 77 |
|
| 78 |
// Release signing (#7): the release job decodes its keystore secret to a |
| 79 |
// file and names it and the password's file here. Without them a release |
| 80 |
// build is unsigned, so nothing else needs the key. |
| 81 |
val releaseKeystore: File? = providers.environmentVariable("OXYGENHUB_KEYSTORE").orNull?.let { File(it) }?.takeIf { it.isFile } |
| 82 |
val releasePassword: String? = |
| 83 |
providers.environmentVariable("OXYGENHUB_KEYSTORE_PASSWORD_FILE").orNull?.let { File(it).readText().trim() } |
| 84 |
|
| e0e4a1c4 Andy McCall ↶ |
85 |
android { |
| 57c08243 Andy McCall ↶ |
86 |
namespace = "io.oxygenhub.android" |
| e0e4a1c4 Andy McCall ↶ |
87 |
compileSdk { |
| 88 |
version = release(37) |
| 89 |
} |
| 90 |
|
| 91 |
defaultConfig { |
| 57c08243 Andy McCall ↶ |
92 |
applicationId = "io.oxygenhub.android" |
| 93 |
minSdk = 31 |
| e0e4a1c4 Andy McCall ↶ |
94 |
targetSdk = 37 |
| bfa2eff0 Andy McCall ↶ |
95 |
versionCode = appVersionCode |
| 96 |
versionName = appVersion |
| e0e4a1c4 Andy McCall ↶ |
97 |
|
| 98 |
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" |
| aa21eb5e Andy McCall ↶ |
99 |
// Firebase for alerts (#16), from google-services.json when the |
| 100 |
// build has one (the release job writes it from a secret); empty, |
| 101 |
// the app takes no alerts. |
| 102 |
buildConfigField("String", "FIREBASE_APP_ID", "\"${firebase["app_id"].orEmpty()}\"") |
| 103 |
buildConfigField("String", "FIREBASE_PROJECT_ID", "\"${firebase["project_id"].orEmpty()}\"") |
| 104 |
buildConfigField("String", "FIREBASE_SENDER_ID", "\"${firebase["sender_id"].orEmpty()}\"") |
| 105 |
buildConfigField("String", "FIREBASE_API_KEY", "\"${firebase["api_key"].orEmpty()}\"") |
| bfa2eff0 Andy McCall ↶ |
106 |
} |
| 107 |
|
| 108 |
signingConfigs { |
| 109 |
if (releaseKeystore != null && releasePassword != null) { |
| 110 |
create("release") { |
| 111 |
storeFile = releaseKeystore |
| 112 |
storeType = "PKCS12" |
| 113 |
storePassword = releasePassword |
| 114 |
keyAlias = "oxygenhub" |
| 115 |
keyPassword = releasePassword |
| 116 |
} |
| 117 |
} |
| e0e4a1c4 Andy McCall ↶ |
118 |
} |
| 119 |
|
| 120 |
buildTypes { |
| 8a657957 Andy McCall ↶ |
121 |
// Unit test coverage as JaCoCo XML for the hub's Coverage tab |
| 122 |
// (#19): ./gradlew :app:createDebugUnitTestCoverageReport. |
| 123 |
debug { |
| 124 |
enableUnitTestCoverage = true |
| 125 |
} |
| e0e4a1c4 Andy McCall ↶ |
126 |
release { |
| 127 |
optimization { |
| 128 |
enable = false |
| 129 |
} |
| bfa2eff0 Andy McCall ↶ |
130 |
signingConfigs.findByName("release")?.let { signingConfig = it } |
| e0e4a1c4 Andy McCall ↶ |
131 |
} |
| 132 |
} |
| 133 |
compileOptions { |
| 134 |
sourceCompatibility = JavaVersion.VERSION_11 |
| 135 |
targetCompatibility = JavaVersion.VERSION_11 |
| 136 |
} |
| 490d1806 Andy McCall ↶ |
137 |
packaging { |
| 138 |
jniLibs { |
| 139 |
useLegacyPackaging = true |
| 140 |
} |
| 141 |
} |
| e0e4a1c4 Andy McCall ↶ |
142 |
buildFeatures { |
| 143 |
compose = true |
| aa21eb5e Andy McCall ↶ |
144 |
buildConfig = true |
| e0e4a1c4 Andy McCall ↶ |
145 |
} |
| 0a2858c0 Andy McCall ↶ |
146 |
// README screenshots, rendered from previews without an emulator: |
| 147 |
// ./gradlew :app:updateDebugScreenshotTest (src/screenshotTest). |
| 148 |
experimentalProperties["android.experimental.enableScreenshotTest"] = true |
| 57c08243 Andy McCall ↶ |
149 |
sourceSets["main"].jniLibs.directories.add(rustJniLibs.get().asFile.path) |
| 150 |
sourceSets["main"].kotlin.directories.add(rustBindings.get().asFile.path) |
| e0e4a1c4 Andy McCall ↶ |
151 |
} |
| 152 |
|
| 153 |
dependencies { |
| 5ae4af48 Andy McCall ↶ |
154 |
implementation(libs.jna) { artifact { type = "aar" } } |
| e0e4a1c4 Andy McCall ↶ |
155 |
implementation(platform(libs.androidx.compose.bom)) |
| aa21eb5e Andy McCall ↶ |
156 |
implementation(platform(libs.firebase.bom)) |
| 157 |
implementation(libs.firebase.messaging) |
| e0e4a1c4 Andy McCall ↶ |
158 |
implementation(libs.androidx.activity.compose) |
| 159 |
implementation(libs.androidx.compose.material3) |
| 5ae4af48 Andy McCall ↶ |
160 |
implementation(libs.androidx.compose.material3.navigation.suite) |
| 161 |
implementation(libs.androidx.compose.material3.adaptive) |
| 162 |
implementation(libs.androidx.compose.material3.adaptive.layout) |
| 163 |
implementation(libs.androidx.compose.material3.adaptive.navigation) |
| 164 |
implementation(libs.androidx.compose.material.icons.extended) |
| e0e4a1c4 Andy McCall ↶ |
165 |
implementation(libs.androidx.compose.ui) |
| 166 |
implementation(libs.androidx.compose.ui.graphics) |
| 167 |
implementation(libs.androidx.compose.ui.tooling.preview) |
| 168 |
implementation(libs.androidx.core.ktx) |
| 169 |
implementation(libs.androidx.lifecycle.runtime.ktx) |
| 5ae4af48 Andy McCall ↶ |
170 |
implementation(libs.androidx.lifecycle.runtime.compose) |
| 171 |
implementation(libs.androidx.lifecycle.viewmodel.compose) |
| 172 |
implementation(libs.androidx.navigation.compose) |
| 173 |
implementation(libs.kotlinx.serialization.json) |
| 174 |
implementation(libs.kotlinx.coroutines.android) |
| 175 |
implementation(libs.okhttp) |
| 98dbd122 Andy McCall ↶ |
176 |
implementation(libs.markdown.renderer.m3) |
| 177 |
implementation(libs.highlights) |
| e0e4a1c4 Andy McCall ↶ |
178 |
testImplementation(libs.junit) |
| 5ae4af48 Andy McCall ↶ |
179 |
testImplementation(libs.kotlinx.coroutines.test) |
| 180 |
testImplementation(libs.okhttp.mockwebserver) |
| e0e4a1c4 Andy McCall ↶ |
181 |
androidTestImplementation(platform(libs.androidx.compose.bom)) |
| 182 |
androidTestImplementation(libs.androidx.compose.ui.test.junit4) |
| 183 |
androidTestImplementation(libs.androidx.espresso.core) |
| 184 |
androidTestImplementation(libs.androidx.junit) |
| 0a2858c0 Andy McCall ↶ |
185 |
screenshotTestImplementation(libs.screenshot.validation.api) |
| 186 |
screenshotTestImplementation(libs.androidx.compose.ui.tooling) |
| 187 |
screenshotTestImplementation(libs.kotlinx.coroutines.test) |
| e0e4a1c4 Andy McCall ↶ |
188 |
debugImplementation(libs.androidx.compose.ui.test.manifest) |
| 189 |
debugImplementation(libs.androidx.compose.ui.tooling) |
| 5ae4af48 Andy McCall ↶ |
190 |
} |