import java.net.HttpURLConnection import java.net.URL import java.util.Properties plugins { alias(libs.plugins.android.application) alias(libs.plugins.kotlin.android) alias(libs.plugins.kotlin.compose) } /** local.properties (gitignored) as a shared source for dev-machine settings (backend host, keys). */ val localProps = Properties().apply { val f = rootProject.file("local.properties") if (f.exists()) f.inputStream().use { load(it) } } /** * Google Maps Android SDK key. The SDK reads it from the merged manifest at build time, so a * compiled APK carries a fixed key there is no runtime key swap for the base map. * * Source of truth is the admin **config vault** (config-service `maps.androidKey`): the admin sets * the key in the super-admin dashboard, and each build pulls it from here. `local.properties` and * the `MAPS_API_KEY` env var remain fallbacks so offline / CI builds still work without the vault. * * Never committed same rule as every other third-party credential in this repo. Absent is a * supported state: the home screen falls back to the nearby list rather than a dead grey tile. */ val localMapsKey: String? = localProps.getProperty("MAPS_API_KEY") ?: System.getenv("MAPS_API_KEY") /** Best-effort fetch of a config-service vault value at build time; null on any failure so builds never break. */ fun fetchFromVault(key: String): String? { val base = (project.findProperty("configServiceUrl") as String?) ?: System.getenv("CONFIG_URL") ?: "http://localhost:8091" return try { val url = URL("$base/api/v1/config/$key/value") val conn = (url.openConnection() as HttpURLConnection).apply { connectTimeout = 2000 readTimeout = 2000 requestMethod = "GET" } if (conn.responseCode != 200) return null val body = conn.inputStream.bufferedReader().use { it.readText() } // Lightweight parse of {"key":"...","value":"..."} to avoid a JSON dep here. Regex("\"value\"\\s*:\\s*\"([^\"]*)\"").find(body)?.groupValues?.get(1)?.takeIf { it.isNotBlank() } } catch (_: Exception) { null } } // Vault is primary (admin-managed); local.properties / env are the offline fallback. val mapsApiKey: String = fetchFromVault("maps.androidKey") ?: localMapsKey ?: "" // Social sign-in ids, baked in at build time. The app uses the Google Web (server) client id as its // serverClientId, and the Apple Services id to build the Apple web-OAuth URL. Empty = that button is // hidden until the super-admin adds the credential in the API Keys vault. val googleClientId: String = fetchFromVault("auth.google.clientId") ?: "" val appleServiceId: String = fetchFromVault("auth.apple.serviceId") ?: "" // MyFatoorah needs NO build-time config: the app never holds the API token (payment-service creates // the session server-side from the vault key the super-admin sets), and whether the SDK card view // is shown, plus the portal country + TEST/LIVE environment, all come from the /topups response at // runtime. So there are no MyFatoorah build flags here it's entirely dashboard-driven. android { namespace = "com.beebbeeb.rider" compileSdk = 35 defaultConfig { applicationId = "com.beebbeeb.rider" minSdk = 26 targetSdk = 35 versionCode = 1 versionName = "1.0" vectorDrawables { useSupportLibrary = true } // Backend base URLs. The host defaults to 10.0.2.2 (the dev machine as seen from the Android // EMULATOR). For a REAL device on the same Wi-Fi, override with the machine's LAN IP: // ./gradlew :app:assembleDebug -PbackendHost=192.168.1.107 // (or set backendHost in local.properties / the BACKEND_HOST env var). In production these // point at the API gateway. val backendHost = (project.findProperty("backendHost") as String?) ?: localProps.getProperty("BACKEND_HOST") ?: System.getenv("BACKEND_HOST") ?: "10.0.2.2" buildConfigField("String", "AUTH_URL", "\"http://$backendHost:8081\"") buildConfigField("String", "FLEET_URL", "\"http://$backendHost:8082\"") buildConfigField("String", "BOOKING_URL", "\"http://$backendHost:8084\"") buildConfigField("String", "WALLET_URL", "\"http://$backendHost:8085\"") buildConfigField("String", "PAYMENT_URL", "\"http://$backendHost:8086\"") buildConfigField("String", "ZONE_URL", "\"http://$backendHost:8087\"") buildConfigField("String", "MAPS_API_KEY", "\"$mapsApiKey\"") manifestPlaceholders["MAPS_API_KEY"] = mapsApiKey // Social sign-in ids (from the vault at build time). Empty hides the corresponding button. buildConfigField("String", "GOOGLE_CLIENT_ID", "\"$googleClientId\"") buildConfigField("String", "APPLE_SERVICE_ID", "\"$appleServiceId\"") // Redirect the Apple web-OAuth flow back into the app (see AndroidManifest intent-filter). manifestPlaceholders["appAuthRedirectScheme"] = "beebbeeb" } buildTypes { release { isMinifyEnabled = false proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } compileOptions { sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } kotlinOptions { jvmTarget = "17" } buildFeatures { compose = true buildConfig = true } } dependencies { implementation(libs.androidx.core.ktx) implementation(libs.androidx.lifecycle.runtime.ktx) implementation(libs.androidx.lifecycle.viewmodel.compose) implementation(libs.androidx.activity.compose) implementation(libs.androidx.appcompat) implementation(platform(libs.androidx.compose.bom)) implementation(libs.androidx.ui) // Professional vector icons (Rounded set) replaces emoji glyphs across the app. implementation("androidx.compose.material:material-icons-extended") implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.material3) implementation(libs.androidx.navigation.compose) debugImplementation(libs.androidx.ui.tooling) implementation(libs.retrofit) implementation(libs.retrofit.gson) implementation(libs.okhttp.logging) implementation(libs.androidx.datastore.preferences) implementation(libs.kotlinx.coroutines.android) implementation(libs.androidx.lifecycle.runtime.compose) implementation(libs.androidx.camera.core) implementation(libs.androidx.camera.camera2) implementation(libs.androidx.camera.lifecycle) implementation(libs.androidx.camera.view) implementation(libs.mlkit.barcode.scanning) implementation(libs.maps.compose) implementation(libs.play.services.location) // MyFatoorah native SDK for the in-app (embedded) top-up card view. implementation(libs.myfatoorah) // The SDK's payment activity uses Material (View) components + a Material theme. implementation("com.google.android.material:material:1.12.0") // Social sign-in: Google via Credential Manager, Apple via a Custom Tabs web-OAuth flow. implementation("androidx.credentials:credentials:1.3.0") implementation("androidx.credentials:credentials-play-services-auth:1.3.0") implementation("com.google.android.libraries.identity.googleid:googleid:1.1.1") implementation("androidx.browser:browser:1.8.0") }