347 lines
12 KiB
Plaintext
347 lines
12 KiB
Plaintext
import java.net.URI
|
|
import java.nio.file.Files
|
|
import java.nio.file.Path
|
|
import org.gradle.api.tasks.compile.JavaCompile
|
|
|
|
plugins {
|
|
id("com.android.application")
|
|
}
|
|
|
|
val releaseBaseUrl = providers.gradleProperty("WNH_BASE_URL").orElse("")
|
|
val debugBaseUrl = providers.gradleProperty("WNH_DEBUG_BASE_URL").orElse("")
|
|
val trackingMinTimeMs = providers.gradleProperty("WNH_TRACKING_MIN_TIME_MS").orElse("0")
|
|
val trackingHttpTimeoutMs =
|
|
providers.gradleProperty("WNH_TRACKING_HTTP_TIMEOUT_MS").orElse("0")
|
|
val instrumentationBuildType =
|
|
providers.gradleProperty("WNH_TEST_BUILD_TYPE").orElse("debug")
|
|
val androidVersionCode = providers.gradleProperty("WNH_ANDROID_VERSION_CODE").orElse("1")
|
|
val androidVersionName = providers.gradleProperty("WNH_ANDROID_VERSION_NAME").orElse("0.1.0")
|
|
val firebaseApplicationId = providers.gradleProperty("WNH_FIREBASE_APPLICATION_ID").orElse("")
|
|
val firebaseApiKey = providers.gradleProperty("WNH_FIREBASE_API_KEY").orElse("")
|
|
val firebaseProjectId = providers.gradleProperty("WNH_FIREBASE_PROJECT_ID").orElse("")
|
|
val firebaseSenderId = providers.gradleProperty("WNH_FIREBASE_GCM_SENDER_ID").orElse("")
|
|
val releaseSigningStoreFile =
|
|
providers.environmentVariable("WNH_ANDROID_SIGNING_STORE_FILE").orNull
|
|
val releaseSigningPasswordFile =
|
|
providers.environmentVariable("WNH_ANDROID_SIGNING_PASSWORD_FILE").orNull
|
|
val releaseSigningKeyAlias =
|
|
providers.environmentVariable("WNH_ANDROID_SIGNING_KEY_ALIAS").orNull
|
|
|
|
fun nonBlank(value: String?): String? = value?.trim()?.takeIf(String::isNotEmpty)
|
|
|
|
fun quotedBuildConfig(value: String): String =
|
|
"\"${value.replace("\\", "\\\\").replace("\"", "\\\"")}\""
|
|
|
|
val firebaseInputs =
|
|
listOf(
|
|
firebaseApplicationId.get(),
|
|
firebaseApiKey.get(),
|
|
firebaseProjectId.get(),
|
|
firebaseSenderId.get()
|
|
)
|
|
val firebaseConfigured = firebaseInputs.all { it.isNotBlank() }
|
|
val firebasePartiallyConfigured = firebaseInputs.any { it.isNotBlank() }
|
|
|
|
fun validateFirebaseConfiguration() {
|
|
if (firebasePartiallyConfigured && !firebaseConfigured) {
|
|
throw GradleException(
|
|
"WNH_FIREBASE_APPLICATION_ID, WNH_FIREBASE_API_KEY, "
|
|
+ "WNH_FIREBASE_PROJECT_ID, and WNH_FIREBASE_GCM_SENDER_ID "
|
|
+ "must either all be set or all be empty"
|
|
)
|
|
}
|
|
}
|
|
|
|
val releaseSigningInputs =
|
|
listOf(
|
|
nonBlank(releaseSigningStoreFile),
|
|
nonBlank(releaseSigningPasswordFile),
|
|
nonBlank(releaseSigningKeyAlias)
|
|
)
|
|
val releaseSigningConfigured = releaseSigningInputs.all { it != null }
|
|
val releaseSigningPartiallyConfigured = releaseSigningInputs.any { it != null }
|
|
|
|
fun readSigningPassword(): String {
|
|
val passwordPath =
|
|
nonBlank(releaseSigningPasswordFile)
|
|
?: throw GradleException("Android release signing password file is not configured")
|
|
val path = Path.of(passwordPath)
|
|
|
|
if (!Files.isRegularFile(path)) {
|
|
throw GradleException("Android release signing password file does not exist")
|
|
}
|
|
|
|
val password = Files.readString(path).trimEnd('\r', '\n')
|
|
if (password.isBlank() || password.contains('\n') || password.contains('\r')) {
|
|
throw GradleException("Android release signing password file is invalid")
|
|
}
|
|
|
|
return password
|
|
}
|
|
|
|
fun manifestOrigin(value: String): URI? =
|
|
runCatching { URI(value) }
|
|
.getOrNull()
|
|
?.takeIf { uri ->
|
|
(uri.scheme == "http" || uri.scheme == "https") && !uri.host.isNullOrBlank()
|
|
}
|
|
|
|
fun isOriginPath(uri: URI): Boolean =
|
|
uri.path.isNullOrEmpty() || uri.path == "/"
|
|
|
|
val debugManifestOrigin = manifestOrigin(debugBaseUrl.get())
|
|
val releaseManifestOrigin = manifestOrigin(releaseBaseUrl.get())
|
|
|
|
android {
|
|
namespace = "org.whoneedhelp.mobile"
|
|
compileSdk = 37
|
|
buildToolsVersion = "37.0.0"
|
|
testBuildType = instrumentationBuildType.get()
|
|
|
|
defaultConfig {
|
|
applicationId = "org.whoneedhelp.mobile"
|
|
minSdk = 24
|
|
targetSdk = 37
|
|
versionCode =
|
|
androidVersionCode.get().toIntOrNull()?.takeIf { it > 0 }
|
|
?: throw GradleException("WNH_ANDROID_VERSION_CODE must be a positive integer")
|
|
versionName =
|
|
androidVersionName.get().trim().takeIf(String::isNotEmpty)
|
|
?: throw GradleException("WNH_ANDROID_VERSION_NAME must not be empty")
|
|
buildConfigField("long", "TRACKING_MIN_TIME_MS", "${trackingMinTimeMs.get()}L")
|
|
buildConfigField(
|
|
"long",
|
|
"TRACKING_HTTP_TIMEOUT_MS",
|
|
"${trackingHttpTimeoutMs.get()}L"
|
|
)
|
|
buildConfigField("boolean", "FIREBASE_CONFIGURED", firebaseConfigured.toString())
|
|
buildConfigField(
|
|
"String",
|
|
"FIREBASE_APPLICATION_ID",
|
|
quotedBuildConfig(firebaseApplicationId.get())
|
|
)
|
|
buildConfigField("String", "FIREBASE_API_KEY", quotedBuildConfig(firebaseApiKey.get()))
|
|
buildConfigField(
|
|
"String",
|
|
"FIREBASE_PROJECT_ID",
|
|
quotedBuildConfig(firebaseProjectId.get())
|
|
)
|
|
buildConfigField(
|
|
"String",
|
|
"FIREBASE_GCM_SENDER_ID",
|
|
quotedBuildConfig(firebaseSenderId.get())
|
|
)
|
|
|
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
|
}
|
|
|
|
signingConfigs {
|
|
if (releaseSigningConfigured) {
|
|
create("release") {
|
|
val storePath = Path.of(nonBlank(releaseSigningStoreFile)!!)
|
|
if (!Files.isRegularFile(storePath)) {
|
|
throw GradleException("Android release signing keystore does not exist")
|
|
}
|
|
|
|
storeFile = storePath.toFile()
|
|
storePassword = readSigningPassword()
|
|
keyAlias = nonBlank(releaseSigningKeyAlias)
|
|
keyPassword = storePassword
|
|
}
|
|
}
|
|
}
|
|
|
|
buildTypes {
|
|
debug {
|
|
applicationIdSuffix = ".debug"
|
|
versionNameSuffix = "-debug"
|
|
buildConfigField(
|
|
"String",
|
|
"BASE_URL",
|
|
"\"${debugBaseUrl.get().replace("\\", "\\\\").replace("\"", "\\\"")}\""
|
|
)
|
|
manifestPlaceholders["usesCleartextTraffic"] = "true"
|
|
manifestPlaceholders["deepLinkHost"] =
|
|
debugManifestOrigin?.host ?: "invalid.whoneedhelp.local"
|
|
}
|
|
|
|
create("development") {
|
|
initWith(getByName("debug"))
|
|
applicationIdSuffix = ".development"
|
|
versionNameSuffix = "-development"
|
|
isDebuggable = false
|
|
if (releaseSigningConfigured) {
|
|
signingConfig = signingConfigs.getByName("release")
|
|
}
|
|
buildConfigField(
|
|
"String",
|
|
"BASE_URL",
|
|
"\"${releaseBaseUrl.get().replace("\\", "\\\\").replace("\"", "\\\"")}\""
|
|
)
|
|
manifestPlaceholders["usesCleartextTraffic"] = "false"
|
|
manifestPlaceholders["deepLinkHost"] =
|
|
releaseManifestOrigin?.host ?: "invalid.whoneedhelp.local"
|
|
matchingFallbacks += listOf("debug")
|
|
}
|
|
|
|
create("staging") {
|
|
initWith(getByName("debug"))
|
|
applicationIdSuffix = ".staging"
|
|
versionNameSuffix = "-staging"
|
|
isDebuggable = false
|
|
if (releaseSigningConfigured) {
|
|
signingConfig = signingConfigs.getByName("release")
|
|
}
|
|
buildConfigField(
|
|
"String",
|
|
"BASE_URL",
|
|
"\"${releaseBaseUrl.get().replace("\\", "\\\\").replace("\"", "\\\"")}\""
|
|
)
|
|
manifestPlaceholders["usesCleartextTraffic"] = "false"
|
|
manifestPlaceholders["deepLinkHost"] =
|
|
releaseManifestOrigin?.host ?: "invalid.whoneedhelp.local"
|
|
matchingFallbacks += listOf("debug")
|
|
}
|
|
|
|
release {
|
|
isMinifyEnabled = true
|
|
isShrinkResources = true
|
|
if (releaseSigningConfigured) {
|
|
signingConfig = signingConfigs.getByName("release")
|
|
}
|
|
buildConfigField(
|
|
"String",
|
|
"BASE_URL",
|
|
"\"${releaseBaseUrl.get().replace("\\", "\\\\").replace("\"", "\\\"")}\""
|
|
)
|
|
manifestPlaceholders["usesCleartextTraffic"] = "false"
|
|
manifestPlaceholders["deepLinkHost"] =
|
|
releaseManifestOrigin?.host ?: "invalid.whoneedhelp.local"
|
|
proguardFiles(
|
|
getDefaultProguardFile("proguard-android-optimize.txt"),
|
|
"proguard-rules.pro"
|
|
)
|
|
}
|
|
}
|
|
|
|
buildFeatures {
|
|
buildConfig = true
|
|
}
|
|
|
|
compileOptions {
|
|
sourceCompatibility = JavaVersion.VERSION_17
|
|
targetCompatibility = JavaVersion.VERSION_17
|
|
}
|
|
|
|
testOptions {
|
|
unitTests.isIncludeAndroidResources = false
|
|
}
|
|
}
|
|
|
|
tasks.matching {
|
|
it.name == "preDevelopmentBuild" ||
|
|
it.name == "preStagingBuild" ||
|
|
it.name == "preReleaseBuild"
|
|
}.configureEach {
|
|
doFirst {
|
|
validateFirebaseConfiguration()
|
|
if (!releaseSigningConfigured) {
|
|
val detail =
|
|
if (releaseSigningPartiallyConfigured) {
|
|
"Android signing is only partially configured"
|
|
} else {
|
|
"Android signing is not configured"
|
|
}
|
|
throw GradleException(
|
|
"$detail; set WNH_ANDROID_SIGNING_STORE_FILE, "
|
|
+ "WNH_ANDROID_SIGNING_PASSWORD_FILE, and "
|
|
+ "WNH_ANDROID_SIGNING_KEY_ALIAS"
|
|
)
|
|
}
|
|
|
|
val value = releaseBaseUrl.orNull.orEmpty()
|
|
val uri = runCatching { URI(value) }.getOrNull()
|
|
|
|
if (
|
|
uri == null ||
|
|
uri.scheme != "https" ||
|
|
uri.host.isNullOrBlank() ||
|
|
uri.userInfo != null ||
|
|
!isOriginPath(uri) ||
|
|
uri.query != null ||
|
|
uri.fragment != null
|
|
) {
|
|
throw GradleException(
|
|
"Staging and release builds require "
|
|
+ "-PWNH_BASE_URL=https://your-real-deployment.example"
|
|
)
|
|
}
|
|
|
|
validateTrackingConfiguration()
|
|
}
|
|
}
|
|
|
|
tasks.matching { it.name == "preDebugBuild" }.configureEach {
|
|
doFirst {
|
|
validateFirebaseConfiguration()
|
|
val value = debugBaseUrl.orNull.orEmpty()
|
|
val uri = runCatching { URI(value) }.getOrNull()
|
|
|
|
if (
|
|
uri == null ||
|
|
(uri.scheme != "http" && uri.scheme != "https") ||
|
|
uri.host.isNullOrBlank() ||
|
|
uri.userInfo != null ||
|
|
!isOriginPath(uri) ||
|
|
uri.query != null ||
|
|
uri.fragment != null
|
|
) {
|
|
throw GradleException(
|
|
"Debug builds require -PWNH_DEBUG_BASE_URL=http(s)://your-development-host"
|
|
)
|
|
}
|
|
|
|
validateTrackingConfiguration()
|
|
}
|
|
}
|
|
|
|
fun validateTrackingConfiguration() {
|
|
val minTime = trackingMinTimeMs.orNull?.toLongOrNull()
|
|
val httpTimeout = trackingHttpTimeoutMs.orNull?.toLongOrNull()
|
|
|
|
if (minTime == null || minTime <= 0) {
|
|
throw GradleException(
|
|
"Builds require -PWNH_TRACKING_MIN_TIME_MS=POSITIVE_MILLISECONDS"
|
|
)
|
|
}
|
|
|
|
if (httpTimeout == null || httpTimeout <= 0 || httpTimeout > Int.MAX_VALUE) {
|
|
throw GradleException(
|
|
"Builds require -PWNH_TRACKING_HTTP_TIMEOUT_MS=POSITIVE_INT_MILLISECONDS"
|
|
)
|
|
}
|
|
}
|
|
|
|
tasks.withType<JavaCompile>().configureEach {
|
|
options.compilerArgs.add("-Xlint:deprecation")
|
|
}
|
|
|
|
dependencies {
|
|
implementation("androidx.activity:activity:1.13.0")
|
|
implementation("androidx.credentials:credentials:1.6.0")
|
|
implementation("androidx.credentials:credentials-play-services-auth:1.6.0")
|
|
implementation("androidx.fragment:fragment:1.8.9")
|
|
implementation("androidx.webkit:webkit:1.16.0")
|
|
implementation("com.google.android.libraries.identity.googleid:googleid:1.2.0")
|
|
implementation(platform("com.google.firebase:firebase-bom:34.16.0"))
|
|
implementation("com.google.firebase:firebase-messaging")
|
|
testImplementation("junit:junit:4.13.2")
|
|
androidTestImplementation("androidx.test:core:1.7.0")
|
|
androidTestImplementation("androidx.test:runner:1.7.0")
|
|
androidTestImplementation("androidx.test:rules:1.7.0")
|
|
androidTestImplementation("androidx.test.ext:junit:1.3.0")
|
|
androidTestImplementation("androidx.test.espresso:espresso-core:3.7.0")
|
|
androidTestImplementation("androidx.test.espresso:espresso-web:3.7.0")
|
|
androidTestImplementation("androidx.test.uiautomator:uiautomator:2.4.0")
|
|
}
|