Challenge 1: Configuring Release Signing in Gradle — Solution // app/build.gradle.kts android { signingConfigs { create("release") { storeFile = file(project.property("RELEASE_STORE_FILE") as String) storePassword = project.property("RELEASE_STORE_PASSWORD") as String keyAlias = project.property("RELEASE_KEY_ALIAS") as String keyPassword = project.property("RELEASE_KEY_PASSWORD") as String } } buildTypes { release { isMinifyEnabled = true signingConfig = signingConfigs.getByName("release") proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") } } } // local.properties (NOT committed to git — already in .gitignore by default // for Android Studio projects) // RELEASE_STORE_FILE=../keystore/release.jks // RELEASE_STORE_PASSWORD=actualPasswordHere // RELEASE_KEY_ALIAS=myapp // RELEASE_KEY_PASSWORD=actualPasswordHere // Why these values come from local.properties / environment variables // rather than being hardcoded directly in build.gradle.kts: // // build.gradle.kts is a source file, normally committed to version // control and visible to anyone with repository access (or, for an // open-source project, visible to literally anyone). Hardcoding the // keystore password and key password directly in it would expose the // exact credentials needed to sign a build that Google Play (and every // user's device) would trust as coming from the real developer — the // same category of secret-management mistake the Node.js courses // covered with .env files and dotenv (never commit real secrets to // source control). local.properties is specifically excluded from // version control by Android Studio's default .gitignore, which is why // it's the standard place for this kind of local-machine-only secret. Notes: - project.property("RELEASE_STORE_FILE") reads values that Gradle loads automatically from local.properties (or from CI-provided environment variables in a real pipeline, sourced differently there but with the same underlying goal: keep secrets out of the committed source). - isMinifyEnabled = true is included here alongside signingConfig because a real release buildType, per Chapter 5, should have both R8 shrinking AND proper signing configured together — they're separate concerns but both belong in this same release block.