diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 4b82c5df..42cbf5cc 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,13 +1,13 @@ import java.util.Properties plugins { - id("com.android.application") - id("org.jetbrains.kotlin.android") - id("org.jetbrains.kotlin.plugin.compose") - id("com.google.gms.google-services") - id("com.google.firebase.crashlytics") - id("com.google.dagger.hilt.android") - id("com.google.devtools.ksp") + alias(libs.plugins.android.application) + alias(libs.plugins.kotlin.android) + alias(libs.plugins.kotlin.compose) + alias(libs.plugins.google.services) + alias(libs.plugins.firebase.crashlytics) + alias(libs.plugins.hilt.android) + alias(libs.plugins.ksp) } // Secrets resolve from local.properties (gitignored) first, then -P/gradle.properties, then env. @@ -149,114 +149,111 @@ ksp { } dependencies { - val composeBom = platform("androidx.compose:compose-bom:2025.01.01") + val composeBom = platform(libs.androidx.compose.bom) implementation(composeBom) - implementation("androidx.core:core-ktx:1.15.0") - implementation("androidx.core:core-splashscreen:1.0.1") - implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7") - implementation("androidx.activity:activity-compose:1.9.3") + implementation(libs.androidx.core.ktx) + implementation(libs.androidx.core.splashscreen) + implementation(libs.androidx.lifecycle.runtime.ktx) + implementation(libs.androidx.activity.compose) // Compose - implementation("androidx.compose.ui:ui") - implementation("androidx.compose.ui:ui-graphics") - implementation("androidx.compose.ui:ui-tooling-preview") - implementation("androidx.compose.material3:material3") - implementation("androidx.compose.material:material-icons-extended") + implementation(libs.androidx.compose.ui) + implementation(libs.androidx.compose.ui.graphics) + implementation(libs.androidx.compose.ui.tooling.preview) + implementation(libs.androidx.compose.material3) + implementation(libs.androidx.compose.material.icons.extended) // Navigation - implementation("androidx.navigation:navigation-compose:2.8.5") + implementation(libs.androidx.navigation.compose) - // ViewModel - implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7") - // Lifecycle-aware Compose state collection (collectAsStateWithLifecycle) - implementation("androidx.lifecycle:lifecycle-runtime-compose:2.8.7") + // ViewModel + lifecycle-aware Compose state collection (collectAsStateWithLifecycle) + implementation(libs.androidx.lifecycle.viewmodel.compose) + implementation(libs.androidx.lifecycle.runtime.compose) // Firebase - implementation(platform("com.google.firebase:firebase-bom:33.8.0")) - implementation("com.google.firebase:firebase-auth-ktx") - implementation("com.google.firebase:firebase-firestore-ktx") - implementation("com.google.firebase:firebase-messaging-ktx") - implementation("com.google.firebase:firebase-config-ktx") - implementation("com.google.firebase:firebase-analytics-ktx") - implementation("com.google.firebase:firebase-crashlytics-ktx") - implementation("com.google.firebase:firebase-appcheck-ktx") - implementation("com.google.firebase:firebase-appcheck-playintegrity") - debugImplementation("com.google.firebase:firebase-appcheck-debug") + implementation(platform(libs.firebase.bom)) + implementation(libs.firebase.auth.ktx) + implementation(libs.firebase.firestore.ktx) + implementation(libs.firebase.messaging.ktx) + implementation(libs.firebase.config.ktx) + implementation(libs.firebase.analytics.ktx) + implementation(libs.firebase.crashlytics.ktx) + implementation(libs.firebase.appcheck.ktx) + implementation(libs.firebase.appcheck.playintegrity) + debugImplementation(libs.firebase.appcheck.debug) // Hilt - implementation("com.google.dagger:hilt-android:2.53.1") - ksp("com.google.dagger:hilt-android-compiler:2.53.1") - implementation("androidx.hilt:hilt-navigation-compose:1.2.0") + implementation(libs.hilt.android) + ksp(libs.hilt.android.compiler) + implementation(libs.androidx.hilt.navigation.compose) // Room - implementation("androidx.room:room-runtime:2.6.1") - implementation("androidx.room:room-ktx:2.6.1") - ksp("androidx.room:room-compiler:2.6.1") + implementation(libs.androidx.room.runtime) + implementation(libs.androidx.room.ktx) + ksp(libs.androidx.room.compiler) // DataStore - implementation("androidx.datastore:datastore-preferences:1.1.2") + implementation(libs.androidx.datastore.preferences) // Glance — home-screen "Today" widget (content-free: daily-question state + streak only) - implementation("androidx.glance:glance-appwidget:1.1.1") - implementation("androidx.glance:glance-material3:1.1.1") + implementation(libs.androidx.glance.appwidget) + implementation(libs.androidx.glance.material3) // Encrypted storage - implementation("androidx.security:security-crypto:1.0.0") + implementation(libs.androidx.security.crypto) // Play Integrity API — runtime device integrity check - implementation("com.google.android.play:integrity:1.4.0") + implementation(libs.google.play.integrity) - // Firebase Functions — callable for server-side integrity token verification - implementation("com.google.firebase:firebase-storage-ktx") - implementation("com.google.firebase:firebase-functions-ktx") + // Firebase Storage + Functions (callable for server-side integrity token verification) + implementation(libs.firebase.storage.ktx) + implementation(libs.firebase.functions.ktx) - // RevenueCat native Android SDK (group: com.revenuecat.purchases, artifact: purchases) - implementation("com.revenuecat.purchases:purchases:8.20.0") + // RevenueCat native Android SDK + implementation(libs.revenuecat.purchases) - // Image loading - implementation("io.coil-kt:coil-compose:2.7.0") - // Animated GIF / WebP support (for GIFs, stickers, Bitmoji from the keyboard) - implementation("io.coil-kt:coil-gif:2.7.0") + // Image loading (+ animated GIF/WebP for stickers/Bitmoji) + implementation(libs.coil.compose) + implementation(libs.coil.gif) - // AppCompat — required by BiometricPrompt (needs FragmentActivity base) - implementation("androidx.appcompat:appcompat:1.7.0") - // Biometric auth — recovery phrase reveal + app-level login lock - implementation("androidx.biometric:biometric:1.1.0") + // AppCompat (BiometricPrompt needs FragmentActivity) + biometric app-lock + implementation(libs.androidx.appcompat) + implementation(libs.androidx.biometric) // Google Sign-In via Credential Manager - 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(libs.androidx.credentials) + implementation(libs.androidx.credentials.play.services.auth) + implementation(libs.google.identity.googleid) // Coroutines - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0") + implementation(libs.kotlinx.coroutines.android) // E2EE: Google Tink (AEAD) + Bouncy Castle (Argon2id KDF) - implementation("com.google.crypto.tink:tink-android:1.13.0") - implementation("org.bouncycastle:bcprov-jdk18on:1.78.1") + implementation(libs.tink.android) + implementation(libs.bouncycastle.bcprov) // Debug - debugImplementation("androidx.compose.ui:ui-tooling") - debugImplementation("androidx.compose.ui:ui-test-manifest") + debugImplementation(libs.androidx.compose.ui.tooling) + debugImplementation(libs.androidx.compose.ui.test.manifest) // Unit tests — JVM only (no device/emulator required) - testImplementation("junit:junit:4.13.2") + testImplementation(libs.junit) // Reflection over the RetentionEvent sealed hierarchy (NoPlaintextInAnalyticsTest schema check) testImplementation(kotlin("reflect")) - testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0") - testImplementation("io.mockk:mockk:1.13.14") + testImplementation(libs.kotlinx.coroutines.test) + testImplementation(libs.mockk) // Real org.json on the JVM test classpath so codec round-trips run (Android's stubbed JSONObject // returns defaults under unitTests.isReturnDefaultValues). - testImplementation("org.json:json:20240303") + testImplementation(libs.org.json) // Canonical-vector capture harness (paired-CI for iOS↔Android E2EE fixture fill) - androidTestImplementation("androidx.test.ext:junit:1.1.5") - androidTestImplementation("androidx.test:runner:1.5.2") + androidTestImplementation(libs.androidx.test.ext.junit) + androidTestImplementation(libs.androidx.test.runner) // Instrumented Compose render smoke (first-run screens) — the on-device net for the // "composes fine, crashes on first paint" class (e.g. O-ONBOARD-001). Needs the BOM so the // ui-test version matches the app's Compose; ui-test-manifest (debug, above) hosts the ComposeRule. androidTestImplementation(composeBom) - androidTestImplementation("androidx.compose.ui:ui-test-junit4") + androidTestImplementation(libs.androidx.compose.ui.test.junit4) } diff --git a/build.gradle.kts b/build.gradle.kts index 6403d696..0a488c91 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,9 +1,9 @@ plugins { - id("com.android.application") version "8.7.3" apply false - id("org.jetbrains.kotlin.android") version "2.0.21" apply false - id("org.jetbrains.kotlin.plugin.compose") version "2.0.21" apply false - id("com.google.gms.google-services") version "4.4.2" apply false - id("com.google.firebase.crashlytics") version "3.0.2" apply false - id("com.google.dagger.hilt.android") version "2.53.1" apply false - id("com.google.devtools.ksp") version "2.0.21-1.0.28" apply false + alias(libs.plugins.android.application) apply false + alias(libs.plugins.kotlin.android) apply false + alias(libs.plugins.kotlin.compose) apply false + alias(libs.plugins.google.services) apply false + alias(libs.plugins.firebase.crashlytics) apply false + alias(libs.plugins.hilt.android) apply false + alias(libs.plugins.ksp) apply false } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml new file mode 100644 index 00000000..c57256b8 --- /dev/null +++ b/gradle/libs.versions.toml @@ -0,0 +1,129 @@ +[versions] +# Build plugins +agp = "8.7.3" +kotlin = "2.0.21" +ksp = "2.0.21-1.0.28" +googleServices = "4.4.2" +crashlyticsPlugin = "3.0.2" +hilt = "2.53.1" + +# AndroidX / Compose +coreKtx = "1.15.0" +splashscreen = "1.0.1" +lifecycle = "2.8.7" +activityCompose = "1.9.3" +composeBom = "2025.01.01" +navigationCompose = "2.8.5" +hiltNavigationCompose = "1.2.0" +room = "2.6.1" +datastore = "1.1.2" +glance = "1.1.1" +securityCrypto = "1.0.0" +appcompat = "1.7.0" +biometric = "1.1.0" +credentials = "1.3.0" + +# Firebase / Google +firebaseBom = "33.8.0" +playIntegrity = "1.4.0" +googleid = "1.1.1" + +# Third-party +revenuecat = "8.20.0" +coil = "2.7.0" +coroutines = "1.9.0" +tink = "1.13.0" +bouncycastle = "1.78.1" + +# Test +junit = "4.13.2" +mockk = "1.13.14" +orgJson = "20240303" +androidxTestJunit = "1.1.5" +androidxTestRunner = "1.5.2" + +[libraries] +# AndroidX core +androidx-core-ktx = { module = "androidx.core:core-ktx", version.ref = "coreKtx" } +androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "splashscreen" } +androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "activityCompose" } +androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" } +androidx-biometric = { module = "androidx.biometric:biometric", version.ref = "biometric" } + +# Lifecycle +androidx-lifecycle-runtime-ktx = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "lifecycle" } +androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycle" } +androidx-lifecycle-runtime-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "lifecycle" } + +# Compose (versions from the BOM) +androidx-compose-bom = { module = "androidx.compose:compose-bom", version.ref = "composeBom" } +androidx-compose-ui = { module = "androidx.compose.ui:ui" } +androidx-compose-ui-graphics = { module = "androidx.compose.ui:ui-graphics" } +androidx-compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" } +androidx-compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling" } +androidx-compose-ui-test-manifest = { module = "androidx.compose.ui:ui-test-manifest" } +androidx-compose-ui-test-junit4 = { module = "androidx.compose.ui:ui-test-junit4" } +androidx-compose-material3 = { module = "androidx.compose.material3:material3" } +androidx-compose-material-icons-extended = { module = "androidx.compose.material:material-icons-extended" } + +# Navigation / Hilt +androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } +hilt-android = { module = "com.google.dagger:hilt-android", version.ref = "hilt" } +hilt-android-compiler = { module = "com.google.dagger:hilt-android-compiler", version.ref = "hilt" } +androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hiltNavigationCompose" } + +# Room +androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" } +androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" } +androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" } + +# DataStore / Glance / Security +androidx-datastore-preferences = { module = "androidx.datastore:datastore-preferences", version.ref = "datastore" } +androidx-glance-appwidget = { module = "androidx.glance:glance-appwidget", version.ref = "glance" } +androidx-glance-material3 = { module = "androidx.glance:glance-material3", version.ref = "glance" } +androidx-security-crypto = { module = "androidx.security:security-crypto", version.ref = "securityCrypto" } + +# Credentials (Google Sign-In) +androidx-credentials = { module = "androidx.credentials:credentials", version.ref = "credentials" } +androidx-credentials-play-services-auth = { module = "androidx.credentials:credentials-play-services-auth", version.ref = "credentials" } +google-identity-googleid = { module = "com.google.android.libraries.identity.googleid:googleid", version.ref = "googleid" } + +# Firebase (versions from the BOM) +firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" } +firebase-auth-ktx = { module = "com.google.firebase:firebase-auth-ktx" } +firebase-firestore-ktx = { module = "com.google.firebase:firebase-firestore-ktx" } +firebase-messaging-ktx = { module = "com.google.firebase:firebase-messaging-ktx" } +firebase-config-ktx = { module = "com.google.firebase:firebase-config-ktx" } +firebase-analytics-ktx = { module = "com.google.firebase:firebase-analytics-ktx" } +firebase-crashlytics-ktx = { module = "com.google.firebase:firebase-crashlytics-ktx" } +firebase-appcheck-ktx = { module = "com.google.firebase:firebase-appcheck-ktx" } +firebase-appcheck-playintegrity = { module = "com.google.firebase:firebase-appcheck-playintegrity" } +firebase-appcheck-debug = { module = "com.google.firebase:firebase-appcheck-debug" } +firebase-storage-ktx = { module = "com.google.firebase:firebase-storage-ktx" } +firebase-functions-ktx = { module = "com.google.firebase:firebase-functions-ktx" } +google-play-integrity = { module = "com.google.android.play:integrity", version.ref = "playIntegrity" } + +# Third-party +revenuecat-purchases = { module = "com.revenuecat.purchases:purchases", version.ref = "revenuecat" } +coil-compose = { module = "io.coil-kt:coil-compose", version.ref = "coil" } +coil-gif = { module = "io.coil-kt:coil-gif", version.ref = "coil" } +kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" } +tink-android = { module = "com.google.crypto.tink:tink-android", version.ref = "tink" } +bouncycastle-bcprov = { module = "org.bouncycastle:bcprov-jdk18on", version.ref = "bouncycastle" } + +# Test +junit = { module = "junit:junit", version.ref = "junit" } +kotlinx-coroutines-test = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-test", version.ref = "coroutines" } +mockk = { module = "io.mockk:mockk", version.ref = "mockk" } +org-json = { module = "org.json:json", version.ref = "orgJson" } +androidx-test-ext-junit = { module = "androidx.test.ext:junit", version.ref = "androidxTestJunit" } +androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidxTestRunner" } + +[plugins] +android-application = { id = "com.android.application", version.ref = "agp" } +kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } +kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" } +google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" } +firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "crashlyticsPlugin" } +hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } +ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }