Skip to content

Commit

Permalink
Integrate with Firebase and update to AndroidX
Browse files Browse the repository at this point in the history
Added Crashlytics and Firebase App Distribution.
Updated projects from old support libraries to androidx equivalents.
  • Loading branch information
vgaidarji committed Mar 15, 2024
1 parent 48b19f6 commit 69cf527
Show file tree
Hide file tree
Showing 12 changed files with 116 additions and 47 deletions.
16 changes: 6 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,19 +89,15 @@ In few words:

---

### Fabric/Crashlytics project configuration
### Firebase App Distribution project configuration

In order to upload APK to Crashlytics project should have following configuration:
`${projectDir}/fabric.properties` file with `apiSecret` and `io.fabric.ApiKey` in AndroidManifest.xml([1](https://github.com/vgaidarji/ci-matters/blob/master/app/src/main/AndroidManifest.xml#L17),
[2](https://github.com/vgaidarji/ci-matters/blob/master/app/build.gradle#L59)) file.
**Both keys should not be uploaded to the repository for security reasons!**
In order to distribute Android builds to Firebase, Google Service Account ([link](https://firebase.google.com/docs/app-distribution/android/distribute-gradle#authenticate)) credentials should be created.
Service Account then needs to be saved in secure place and exported in `FIREBASE_APP_DISTRIBUTION_SERVICE_ACCOUNT_JSON={service-account.json}` environment variable on CI.
Use secret variables on CI/repo level for this.

Pass both parameters to your build from command line:
Distribute the build using following command:

./gradlew -PfabricApiKey="YOUR_API_KEY" -PfabricApiSecret="YOUR_API_SECRET" crashlyticsUploadDistributionDebug

or export these keys as environment variables on a build machine
and they will be automatically read from there during the build (no need to pass keys as parameters in this case).
./gradlew appDistributionUpload{Debug|Release}

------

Expand Down
36 changes: 27 additions & 9 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
apply plugin: 'com.android.application'
apply plugin: 'org.jetbrains.kotlin.android'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.appdistribution'
apply plugin: 'com.google.firebase.crashlytics'
apply from: 'config/quality.gradle'
apply from: 'config/jacoco.gradle'
//apply from: 'config/coveralls.gradle'
Expand Down Expand Up @@ -46,12 +49,24 @@ android {
targetSdkVersion 34
versionCode getVersionCode()
versionName getVersionName()
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
debug {
applicationIdSuffix ".debug"
debuggable true
firebaseAppDistribution {
artifactType="APK"
serviceCredentialsFile = System.getenv('FIREBASE_APP_DISTRIBUTION_SERVICE_ACCOUNT_JSON')
}
}
release {
minifyEnabled false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
firebaseAppDistribution {
artifactType="APK"
serviceCredentialsFile = System.getenv('FIREBASE_APP_DISTRIBUTION_SERVICE_ACCOUNT_JSON')
}
}
}
buildFeatures {
Expand All @@ -70,12 +85,15 @@ android {

dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
// todo: firebase app distribution
testImplementation 'junit:junit:4.12'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
implementation platform('com.google.firebase:firebase-bom:32.7.4')
implementation 'com.google.firebase:firebase-analytics'
implementation 'com.google.firebase:firebase-crashlytics'
implementation 'androidx.test.ext:junit-ktx:1.1.5'
testImplementation 'junit:junit:4.13.2'
testImplementation 'org.mockito:mockito-core:5.11.0'
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test:rules:1.5.0'
}
48 changes: 48 additions & 0 deletions app/google-services.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
{
"project_info": {
"project_number": "3225927432",
"project_id": "ci-matters-a498c",
"storage_bucket": "ci-matters-a498c.appspot.com"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:3225927432:android:1299f0babc1137e8b2b6bd",
"android_client_info": {
"package_name": "com.vgaidarji.cimatters"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyC-6cjWLUnfo7hd0dXRW_Gd2E_CxUrXOEk"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:3225927432:android:10d44974fc042c1eb2b6bd",
"android_client_info": {
"package_name": "com.vgaidarji.cimatters.debug"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyC-6cjWLUnfo7hd0dXRW_Gd2E_CxUrXOEk"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package com.vgaidarji.cimatters

import android.support.test.espresso.Espresso
import android.support.test.espresso.action.ViewActions
import android.support.test.espresso.assertion.ViewAssertions
import android.support.test.espresso.matcher.ViewMatchers
import android.support.test.rule.ActivityTestRule
import android.support.test.runner.AndroidJUnit4
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.ext.junit.rules.ActivityScenarioRule
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.hamcrest.CoreMatchers
import org.junit.Rule
import org.junit.Test
Expand All @@ -14,35 +16,35 @@ import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class LoginActivityTest {
@get:Rule
val activityTestRule = ActivityTestRule(
val activityTestRule = ActivityScenarioRule(
LoginActivity::class.java
)

@Test
@Throws(Exception::class)
fun onLoginClick_shouldOpenNextActivityForAllowedCredentials() {
Espresso.onView(ViewMatchers.withId(R.id.edit_text_email))
onView(withId(R.id.edit_text_email))
.perform(ViewActions.typeText("[email protected]"))
Espresso.onView(ViewMatchers.withId(R.id.edit_text_password))
onView(withId(R.id.edit_text_password))
.perform(ViewActions.typeText("1111"))
Espresso.onView(ViewMatchers.withId(R.id.button_login)).perform(ViewActions.click())
Espresso.onView(ViewMatchers.withText("OK"))
.check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
onView(withId(R.id.button_login)).perform(ViewActions.click())
onView(withText("OK"))
.check(ViewAssertions.matches(isDisplayed()))
}

@Test
@Throws(Exception::class)
fun onLoginClick_shouldShowErrorForIncorrectCredentials() {
Espresso.onView(ViewMatchers.withId(R.id.edit_text_email))
onView(withId(R.id.edit_text_email))
.perform(ViewActions.typeText("[email protected]"))
Espresso.onView(ViewMatchers.withId(R.id.edit_text_password))
onView(withId(R.id.edit_text_password))
.perform(ViewActions.typeText("not_a_password"))
Espresso.onView(ViewMatchers.withId(R.id.button_login)).perform(ViewActions.click())
Espresso.onView(
onView(withId(R.id.button_login)).perform(ViewActions.click())
onView(
CoreMatchers.allOf(
ViewMatchers.withId(android.support.design.R.id.snackbar_text),
ViewMatchers.withText("Wrong credentials.")
withId(R.id.snackbar_text), // Update this ID based on your layout
withText("Wrong credentials.")
)
).check(ViewAssertions.matches(ViewMatchers.isDisplayed()))
).check(ViewAssertions.matches(isDisplayed()))
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@ import android.app.Application
class CiMattersApplication : Application() {
override fun onCreate() {
super.onCreate()
// TODO: integrate with Crashlytics
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package com.vgaidarji.cimatters

import android.content.Intent
import android.os.Bundle
import android.support.design.widget.Snackbar
import android.support.v7.app.AppCompatActivity
import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.snackbar.Snackbar
import com.vgaidarji.cimatters.databinding.ActivityLoginBinding

class LoginActivity : AppCompatActivity(), LoginView {
Expand All @@ -12,7 +12,7 @@ class LoginActivity : AppCompatActivity(), LoginView {
private lateinit var presenter: LoginPresenter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityLoginBinding.inflate(layoutInflater);
binding = ActivityLoginBinding.inflate(layoutInflater)
setContentView(binding.root)
title = getString(R.string.activity_login)
presenter = LoginPresenter(this)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.vgaidarji.cimatters

import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Expand Down
8 changes: 4 additions & 4 deletions app/src/main/res/layout/activity_login.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/coordinator"
Expand All @@ -26,22 +26,22 @@
android:ems="10"
android:hint="@string/email"
android:inputType="textEmailAddress"
/>
android:autofillHints="" />
<EditText
android:id="@+id/edit_text_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/password"
android:inputType="textPassword"
/>
android:autofillHints="" />
<Button
android:id="@+id/button_login"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/login"
/>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>


3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ buildscript {
classpath 'com.android.tools.build:gradle:8.3.0'
classpath 'org.kt3k.gradle.plugin:coveralls-gradle-plugin:2.7.1'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "com.google.gms:google-services:4.4.1"
classpath "com.google.firebase:firebase-appdistribution-gradle:4.2.0"
classpath "com.google.firebase:firebase-crashlytics-gradle:2.9.9"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
Expand Down
3 changes: 3 additions & 0 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@
android.nonFinalResIds=false
android.nonTransitiveRClass=false
org.gradle.jvmargs=-Xmx1536m
android.useAndroidX=true
android.enableJetifier=true

# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

0 comments on commit 69cf527

Please sign in to comment.