From 973aed4547d6cf8e7fadb1b6d2e4fd6f0f1fc3cd Mon Sep 17 00:00:00 2001
From: Robin Singh <robin.singh@grofers.com>
Date: Mon, 1 Jan 2024 20:52:24 +0530
Subject: [PATCH] Added firebase crashlytics support and migrated gradle groovy
 to kotlin dsl (#53)

---
 .github/workflows/build.yml                   |   5 +-
 .github/workflows/prod-release.yml            |  11 ++
 .gitignore                                    |   1 +
 app/build.gradle                              | 166 -----------------
 app/build.gradle.kts                          | 174 ++++++++++++++++++
 app/proguard-rules.pro                        |   4 +-
 .../{build.gradle => build.gradle.kts}        |  22 +--
 build.gradle => build.gradle.kts              |   8 +-
 fastlane/Fastfile                             |   8 +-
 gradle/libs.versions.toml                     |   7 +
 settings.gradle => settings.gradle.kts        |   4 +-
 11 files changed, 222 insertions(+), 188 deletions(-)
 delete mode 100644 app/build.gradle
 create mode 100644 app/build.gradle.kts
 rename baselineprofile/{build.gradle => build.gradle.kts} (59%)
 rename build.gradle => build.gradle.kts (66%)
 rename settings.gradle => settings.gradle.kts (87%)

diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 16a47b9..1c41aa4 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -28,7 +28,10 @@ jobs:
       - name: Decode google-services.json
         env:
           GOOGLE_SERVICES: ${{ secrets.GOOGLE_SERVICES }}
-        run: echo $GOOGLE_SERVICES > app/src/debug/google-services.json
+          GOOGLE_SERVICES_DEBUG: ${{ secrets.GOOGLE_SERVICES_DEBUG }}
+        run: |
+          echo $GOOGLE_SERVICES > app/google-services.json
+          echo $GOOGLE_SERVICES_DEBUG > app/src/debug/google-services.json
 
       - name: Build debug APK and run jvm tests
         env:
diff --git a/.github/workflows/prod-release.yml b/.github/workflows/prod-release.yml
index 9ccd0a8..cec2d8f 100644
--- a/.github/workflows/prod-release.yml
+++ b/.github/workflows/prod-release.yml
@@ -34,6 +34,17 @@ jobs:
           echo $KEYSTORE_PROPERTIES | base64 --decode > keystore.properties
           echo $KEYSTORE_FILE | base64 --decode > Keystore.p12
 
+      - name: Create debug directory
+        run: mkdir -p app/src/debug
+
+      - name: Decode google-services.json
+        env:
+          GOOGLE_SERVICES: ${{ secrets.GOOGLE_SERVICES }}
+          GOOGLE_SERVICES_DEBUG: ${{ secrets.GOOGLE_SERVICES_DEBUG }}
+        run: |
+          echo $GOOGLE_SERVICES > app/google-services.json
+          echo $GOOGLE_SERVICES_DEBUG > app/src/debug/google-services.json    
+
       - name: Set up Ruby
         uses: ruby/setup-ruby@v1
         with:
diff --git a/.gitignore b/.gitignore
index 8fac899..348ecaf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,3 +18,4 @@ keystore.properties
 Keystore.p12
 app/release
 news-feed-play-config.json
+google-services.json
diff --git a/app/build.gradle b/app/build.gradle
deleted file mode 100644
index 92d5794..0000000
--- a/app/build.gradle
+++ /dev/null
@@ -1,166 +0,0 @@
-plugins {
-    alias(libs.plugins.android.application)
-    alias(libs.plugins.kotlin.android)
-    alias(libs.plugins.ksp)
-    alias(libs.plugins.kotlin.parcelize)
-    alias(libs.plugins.androidx.baselineprofile)
-    alias(libs.plugins.detekt)
-}
-
-def final debugAppSuffix = ".debug"
-
-def localPropertiesFile = rootProject.file('local.properties')
-Properties localProperties = new Properties()
-def newsFeedApiKey = ""
-
-if (localPropertiesFile.exists()) {
-    localProperties.load(new FileInputStream(localPropertiesFile))
-    newsFeedApiKey = localProperties['NEWS_FEED_API_KEY'] ?: "\"${System.getenv('NEWS_FEED_API_KEY')}\""
-} else {
-    newsFeedApiKey = "\"${System.getenv('NEWS_FEED_API_KEY')}\""
-}
-
-def keystorePropertiesFile = rootProject.file('keystore.properties')
-def keystoreProperties = new Properties()
-if (keystorePropertiesFile.exists()) {
-    keystoreProperties.load(keystorePropertiesFile.newDataInputStream())
-}
-
-android {
-    experimentalProperties["android.experimental.art-profile-r8-rewriting"] = true
-    experimentalProperties["android.experimental.r8.dex-startup-optimization"] = true
-    signingConfigs {
-        release {
-            release {
-                storeFile file('../Keystore.p12')
-                storePassword keystoreProperties['storePassword']
-                keyAlias keystoreProperties['keyAlias']
-                keyPassword keystoreProperties['keyPassword']
-            }
-        }
-    }
-    compileSdk 34
-
-    defaultConfig {
-        applicationId "com.rob729.newsfeed"
-        minSdk 24
-        targetSdk 34
-        versionCode 1
-        versionName "1.0.0"
-
-        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
-        vectorDrawables {
-            useSupportLibrary true
-        }
-
-        buildConfigField("String", "NEWS_FEED_API_KEY", newsFeedApiKey)
-
-        ksp {
-            arg("room.schemaLocation", "$projectDir/schemas")
-        }
-    }
-
-    buildTypes {
-        debug {
-            applicationIdSuffix = debugAppSuffix
-        }
-        release {
-            shrinkResources true
-            minifyEnabled true
-            debuggable false
-            signingConfig signingConfigs.release
-            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
-        }
-        benchmark {
-            initWith(release)
-            signingConfig = signingConfigs.getByName("debug")
-            proguardFiles("benchmark-rules.pro")
-            matchingFallbacks = ['release']
-        }
-    }
-    compileOptions {
-        sourceCompatibility JavaVersion.VERSION_17
-        targetCompatibility JavaVersion.VERSION_17
-    }
-    kotlin {
-        jvmToolchain(17)
-    }
-    buildFeatures {
-        compose true
-        buildConfig true
-    }
-    composeOptions {
-        kotlinCompilerExtensionVersion = "1.5.7"
-    }
-    packagingOptions {
-        resources {
-            excludes += '/META-INF/{AL2.0,LGPL2.1}'
-        }
-    }
-    namespace 'com.rob729.newsfeed'
-}
-
-baselineProfile {
-    dexLayoutOptimization(true)
-    baselineProfileRulesRewrite(true)
-    baselineProfileOutputDir = "../../src/main/baselineProfiles"
-}
-
-dependencies {
-
-    implementation libs.androidx.core.ktx
-    implementation platform(libs.compose.bom)
-    implementation libs.compose.ui
-    implementation libs.androidx.compose.ui.tooling.preview
-    implementation libs.androidx.lifecycle.runtime.ktx
-    implementation libs.androidx.activity.compose
-    testImplementation libs.junit
-
-    androidTestImplementation libs.androidx.junit
-    androidTestImplementation libs.androidx.espresso.core
-    baselineProfile project(':baselineprofile')
-    debugImplementation libs.androidx.compose.ui.tooling
-    debugImplementation libs.androidx.compose.ui.test.manifest
-    implementation libs.androidx.navigation.compose
-    implementation libs.androidx.compose.material.icons.extended
-    implementation libs.androidx.compose.material3
-    implementation libs.androidx.lifecycle.viewmodel.compose
-    implementation libs.androidx.constraintlayout.compose
-
-    implementation libs.androidx.work.runtime.ktx
-    implementation libs.androidx.browser
-    implementation libs.androidx.profileinstaller
-    implementation libs.androidx.startup.runtime
-    implementation libs.androidx.datastore.preferences
-    implementation libs.androidx.runtime.tracing
-
-    implementation libs.coil
-    implementation libs.coil.compose
-
-    implementation libs.moshi.kotlin
-    ksp libs.moshi.codegen
-
-    implementation libs.orbit.viewmodel
-    implementation libs.orbit.compose
-
-    implementation libs.retrofit
-    implementation libs.converter.moshi
-    implementation libs.logging.interceptor
-
-    implementation libs.kotlinx.datetime
-
-    implementation libs.androidx.room.runtime
-    ksp libs.androidx.room.compiler
-    implementation libs.androidx.room.ktx
-
-    implementation libs.koin.android
-    implementation libs.koin.androidx.compose
-
-    debugImplementation libs.pluto
-    benchmarkImplementation libs.pluto.no.op
-    releaseImplementation libs.pluto.no.op
-
-    debugImplementation libs.bundle.core
-    benchmarkImplementation libs.bundle.core.no.op
-    releaseImplementation libs.bundle.core.no.op
-}
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
new file mode 100644
index 0000000..1b8d477
--- /dev/null
+++ b/app/build.gradle.kts
@@ -0,0 +1,174 @@
+@file:Suppress("UnstableApiUsage")
+
+import java.util.Properties
+
+plugins {
+    alias(libs.plugins.android.application)
+    alias(libs.plugins.kotlin.android)
+    alias(libs.plugins.ksp)
+    alias(libs.plugins.kotlin.parcelize)
+    alias(libs.plugins.androidx.baselineprofile)
+    alias(libs.plugins.detekt)
+    if (File("app/google-services.json").exists() && File("app/src/debug/google-services.json").exists()) {
+        alias(libs.plugins.google.services)
+        alias(libs.plugins.firebase.crashlytics)
+    }
+}
+
+val debugAppSuffix = ".debug"
+
+val localPropertiesFile = rootProject.file("local.properties")
+val localProperties = Properties()
+var newsFeedApiKey = if (localPropertiesFile.exists()) {
+    localProperties.load(localPropertiesFile.inputStream())
+    localProperties.getProperty("NEWS_FEED_API_KEY") ?: "\"${System.getenv("NEWS_FEED_API_KEY")}\""
+} else {
+    "\"${System.getenv("NEWS_FEED_API_KEY")}\""
+}
+
+val keystorePropertiesFile = rootProject.file("keystore.properties")
+val keystoreProperties = Properties()
+if (keystorePropertiesFile.exists()) {
+    keystoreProperties.load(keystorePropertiesFile.inputStream())
+}
+
+android {
+    experimentalProperties["android.experimental.art-profile-r8-rewriting"] = true
+    experimentalProperties["android.experimental.r8.dex-startup-optimization"] = true
+    signingConfigs {
+        create("release") {
+                storeFile = file("../Keystore.p12")
+                storePassword = keystoreProperties["storePassword"] as? String
+                keyAlias = keystoreProperties["keyAlias"] as? String
+                keyPassword = keystoreProperties["keyPassword"] as? String
+        }
+    }
+    compileSdk = 34
+
+    defaultConfig {
+        applicationId = "com.rob729.newsfeed"
+        minSdk = 24
+        targetSdk = 34
+        versionCode = 1
+        versionName = "1.0.0"
+
+        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+        vectorDrawables.useSupportLibrary = true
+
+        buildConfigField("String", "NEWS_FEED_API_KEY", newsFeedApiKey)
+
+        ksp {
+            arg("room.schemaLocation", "$projectDir/schemas")
+        }
+    }
+
+    buildTypes {
+        getByName("debug") {
+            applicationIdSuffix = debugAppSuffix
+        }
+        getByName("release") {
+            isShrinkResources = true
+            isMinifyEnabled = true
+            isDebuggable = false
+
+            signingConfig = signingConfigs.getByName("release")
+            proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
+        }
+        create("benchmark") {
+            initWith(getByName("release"))
+            signingConfig = signingConfigs.getByName("debug")
+            proguardFiles("benchmark-rules.pro")
+            matchingFallbacks += listOf("release")
+        }
+    }
+
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_17
+        targetCompatibility = JavaVersion.VERSION_17
+    }
+    kotlinOptions {
+        jvmTarget = "17"
+    }
+    buildFeatures {
+        compose = true
+        buildConfig = true
+    }
+    composeOptions {
+        kotlinCompilerExtensionVersion = "1.5.7"
+    }
+    packaging {
+        resources {
+            excludes += "/META-INF/{AL2.0,LGPL2.1}"
+        }
+    }
+    namespace = "com.rob729.newsfeed"
+}
+
+baselineProfile {
+    dexLayoutOptimization = true
+    baselineProfileRulesRewrite = true
+    baselineProfileOutputDir = "../../src/main/baselineProfiles"
+}
+
+dependencies {
+
+    implementation(libs.androidx.core.ktx)
+    implementation(platform(libs.compose.bom))
+    implementation(libs.compose.ui)
+    implementation(libs.androidx.compose.ui.tooling.preview)
+    implementation(libs.androidx.lifecycle.runtime.ktx)
+    implementation(libs.androidx.activity.compose)
+    testImplementation(libs.junit)
+
+    androidTestImplementation(libs.androidx.junit)
+    androidTestImplementation(libs.androidx.espresso.core)
+    "baselineProfile"(project(":baselineprofile"))
+    debugImplementation(libs.androidx.compose.ui.tooling)
+    debugImplementation(libs.androidx.compose.ui.test.manifest)
+    implementation(libs.androidx.navigation.compose)
+    implementation(libs.androidx.compose.material.icons.extended)
+    implementation(libs.androidx.compose.material3)
+    implementation(libs.androidx.lifecycle.viewmodel.compose)
+    implementation(libs.androidx.constraintlayout.compose)
+
+    implementation(libs.androidx.work.runtime.ktx)
+    implementation(libs.androidx.browser)
+    implementation(libs.androidx.profileinstaller)
+    implementation(libs.androidx.startup.runtime)
+    implementation(libs.androidx.datastore.preferences)
+    implementation(libs.androidx.runtime.tracing)
+
+    implementation(libs.coil)
+    implementation(libs.coil.compose)
+
+    implementation(libs.moshi.kotlin)
+    ksp(libs.moshi.codegen)
+
+    implementation(libs.orbit.viewmodel)
+    implementation(libs.orbit.compose)
+
+    implementation(libs.retrofit)
+    implementation(libs.converter.moshi)
+    implementation(libs.logging.interceptor)
+
+    implementation(libs.kotlinx.datetime)
+
+    implementation(libs.androidx.room.runtime)
+    ksp(libs.androidx.room.compiler)
+    implementation(libs.androidx.room.ktx)
+
+    implementation(libs.koin.android)
+    implementation(libs.koin.androidx.compose)
+
+    debugImplementation(libs.pluto)
+    releaseImplementation(libs.pluto.no.op)
+
+    debugImplementation(libs.bundle.core)
+    releaseImplementation(libs.bundle.core.no.op)
+
+    if (File("${project.projectDir}/google-services.json").exists() && File("${project.projectDir}/src/debug/google-services.json").exists()) {
+        implementation(platform(libs.firebase.bom))
+        implementation(libs.firebase.analytics)
+        implementation(libs.firebase.crashlytics)
+    }
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index e39deed..eeb9117 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -1,6 +1,6 @@
 # Add project specific ProGuard rules here.
 # You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
+# proguardFiles setting in build.gradle.kts.
 #
 # For more details, see
 #   http://developer.android.com/guide/developing/tools/proguard.html
@@ -31,4 +31,4 @@
  # With R8 full mode generic signatures are stripped for classes that are not
  # kept. Suspend functions are wrapped in continuations where the type argument
  # is used.
- -keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
\ No newline at end of file
+ -keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
diff --git a/baselineprofile/build.gradle b/baselineprofile/build.gradle.kts
similarity index 59%
rename from baselineprofile/build.gradle
rename to baselineprofile/build.gradle.kts
index 624e844..8d640c3 100644
--- a/baselineprofile/build.gradle
+++ b/baselineprofile/build.gradle.kts
@@ -5,8 +5,8 @@ plugins {
 }
 
 android {
-    namespace 'com.rob729.baselineprofile'
-    compileSdk 34
+    namespace = "com.rob729.baselineprofile"
+    compileSdk = 34
 
     compileOptions {
         sourceCompatibility = JavaVersion.VERSION_1_8
@@ -18,10 +18,10 @@ android {
     }
 
     defaultConfig {
-        minSdk 28
-        targetSdk 34
+        minSdk =  28
+        targetSdk = 34
 
-        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
     }
 
     targetProjectPath = ":app"
@@ -35,9 +35,9 @@ baselineProfile {
 }
 
 dependencies {
-    implementation libs.androidx.junit
-    implementation libs.androidx.espresso.core
-    implementation libs.androidx.uiautomator
-    implementation libs.androidx.benchmark.macro.junit4
-    implementation libs.androidx.rules
-}
\ No newline at end of file
+    implementation(libs.androidx.junit)
+    implementation(libs.androidx.espresso.core)
+    implementation(libs.androidx.uiautomator)
+    implementation(libs.androidx.benchmark.macro.junit4)
+    implementation(libs.androidx.rules)
+}
diff --git a/build.gradle b/build.gradle.kts
similarity index 66%
rename from build.gradle
rename to build.gradle.kts
index 936e25a..0c7c035 100644
--- a/build.gradle
+++ b/build.gradle.kts
@@ -7,6 +7,10 @@ plugins {
     alias(libs.plugins.androidx.baselineprofile) apply false
     alias(libs.plugins.kotlin.parcelize) apply false
     alias(libs.plugins.detekt)
+    if (File("app/google-services.json").exists() && File("app/src/debug/google-services.json").exists()) {
+        alias(libs.plugins.google.services) apply false
+        alias(libs.plugins.firebase.crashlytics) apply false
+    }
 }
 
 dependencies {
@@ -20,6 +24,6 @@ detekt {
     config.setFrom("$projectDir/config/detekt/detekt.yml")
 }
 
-task clean(type: Delete) {
-    delete rootProject.buildDir
+tasks.register("clean", Delete::class) {
+    delete(rootProject.buildDir)
 }
diff --git a/fastlane/Fastfile b/fastlane/Fastfile
index 9004cfc..0b6b5b6 100644
--- a/fastlane/Fastfile
+++ b/fastlane/Fastfile
@@ -28,15 +28,15 @@ platform :android do
     require 'fileutils'
 
     fastfile_directory = __dir__
-    gradle_file_path = File.join(fastfile_directory, '../app/build.gradle')
+    gradle_file_path = File.join(fastfile_directory, '../app/build.gradle.kts')
      UI.message("xyz #{fastfile_directory}")
     gradle_file = File.read(gradle_file_path)
 
     type = options[:type]
 
     # Regex to find the versionName
-    version_name_regex = /versionName "(\d+\.\d+\.\d+)"/
-    version_code_regex = /versionCode (\d+)/
+    version_name_regex = /versionName = "(\d+\.\d+\.\d+)"/
+    version_code_regex = /versionCode = (\d+)/
 
     version_name = gradle_file[version_name_regex, 1]
     version_code = gradle_file[version_code_regex, 1].to_i
@@ -68,7 +68,7 @@ platform :android do
 
 
     git_commit(
-        path: "./app/build.gradle",
+        path: "./app/build.gradle.kts",
         message: "Version bumped from #{version_name} to #{new_version_name}"
       )
   end
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 7cf43bb..ca225bc 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -10,6 +10,8 @@ coreKtx = "1.12.0"
 datastorePreferences = "1.0.0"
 detekt = "1.23.4"
 espressoCore = "3.5.1"
+firebaseCrashlytics = "2.9.9"
+googleServices = "4.4.0"
 junit = "4.13.2"
 junitVersion = "1.1.5"
 koinAndroid = "3.5.0"
@@ -65,6 +67,9 @@ compose-bom = "androidx.compose:compose-bom:2023.10.01"
 compose-ui = { module = "androidx.compose.ui:ui" }
 converter-moshi = { module = "com.squareup.retrofit2:converter-moshi", version.ref = "retrofit" }
 detekt-formatting = { module = "io.gitlab.arturbosch.detekt:detekt-formatting", version.ref = "detekt" }
+firebase-bom = "com.google.firebase:firebase-bom:32.7.0"
+firebase-analytics = { module = "com.google.firebase:firebase-analytics" }
+firebase-crashlytics = { module = "com.google.firebase:firebase-crashlytics" }
 junit = { module = "junit:junit", version.ref = "junit" }
 koin-android = { module = "io.insert-koin:koin-android", version.ref = "koinAndroid" }
 koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koinAndroid" }
@@ -87,3 +92,5 @@ ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
 kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
 androidx-baselineprofile = { id = "androidx.baselineprofile", version.ref = "baselineProfile" }
 detekt = { id = "io.gitlab.arturbosch.detekt", version.ref = "detekt" }
+firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlytics"}
+google-services = { id = 'com.google.gms.google-services', version.ref = "googleServices"}
diff --git a/settings.gradle b/settings.gradle.kts
similarity index 87%
rename from settings.gradle
rename to settings.gradle.kts
index 1e213cc..46af24e 100644
--- a/settings.gradle
+++ b/settings.gradle.kts
@@ -13,5 +13,5 @@ dependencyResolutionManagement {
     }
 }
 rootProject.name = "News Feed"
-include ':app'
-include ':baselineprofile'
+include(":app")
+include(":baselineprofile")