## [Setup](https://firebase.google.com/docs/android/setup)
- Describe project
- Create Firebase project
- Describe console
- Root `build.gradle`
```groovy=
buildscript {
dependencies {
classpath 'com.google.gms:google-services:4.3.15'
}
}
```
- App module `build.gradle`
```groovy=
plugins {
...
...
id 'com.google.gms.google-services'
}
```
- Try to run
- Create app and download `google-services.json`
- Mention that all apps that we want to run need to be there
- Mention Firebase assistant
## [App distribution](https://firebase.google.com/docs/app-distribution)
- Build APK
- Drag & drop
- Create testers
- Download APK or FAD app
- You can distribute app bundle with Google Play (but it's annoying)
- Only 150 days
## [Crashlytics](https://firebase.google.com/docs/crashlytics/get-started?platform=android)
- BoM
```groovy=
implementation platform('com.google.firebase:firebase-bom:31.5.0')
implementation 'com.google.firebase:firebase-crashlytics'
implementation 'com.google.firebase:firebase-analytics'
```
```groovy=
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.5'
plugins {
id 'com.google.firebase.crashlytics'
}
```
- Customize init
```xml=
<meta-data
android:name="firebase_crashlytics_collection_enabled"
android:value="false" />
```
```kotlin=
if (!BuildConfig.DEBUG) {
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(true)
}
```
- Mention filtering non-crashes, crashes, devices atd.
## [Analytics](https://firebase.google.com/docs/analytics/get-started?platform=android)
```groovy=
implementation 'com.google.firebase:firebase-analytics-ktx'
```
- Automatically gathering some info like user retention
- Basically same as Google Analytics
### Debugging
- It takes a while to receive analytics simply to not drain battery
- Enable debug mode
```bash=
adb shell setprop debug.firebase.analytics.app cz.cvut.fit.pesekmic.lecture11
```
### Events
- Mention Custom Metrics
- There are default events `FirebaseAnalytics.Event.`
```kotlin=
analytics.logEvent("add_btn_clicked", bundleOf("random_num" to (1..10).random()))
```
### User properties
- Mention Custom Definitions
```kotlin=
analytics.setUserProperty("something", "something else")
```
### Track screens
```kotlin=
firebaseAnalytics.logEvent(FirebaseAnalytics.Event.SCREEN_VIEW) {
param(FirebaseAnalytics.Param.SCREEN_NAME, screenName)
param(FirebaseAnalytics.Param.SCREEN_CLASS, "MainActivity")
}
```
### User id
> ...Analytics in conjunction with BigQuery to associate analytics data for the same user across multiple apps...
```kotlin=
analytics.setUserId("abcdefg")
```
## [Firebase Auth](https://firebase.google.com/docs/auth/android/start)
- Mention Browser, Firebase Auth UI, Manual
- Explain more providers
```kotlin=
data class User(
val id: String,
val name: String
)
val userStream: MutableStateFlow<User?> = MutableStateFlow(null)
init {
auth.addAuthStateListener {
val user = it.currentUser?.toUser()
userStream.value = user
analytics.setUserId(user?.id)
}
}
private fun FirebaseUser.toUser(): User {
val displayName = providerData.firstOrNull { !it.displayName.isNullOrBlank() }?.displayName
return User(
id = uid,
name = displayName ?: ""
)
}
```
```kotlin=
fun signIn(activity: Activity) {
val scopes = listOf("email", "profile")
val provider = OAuthProvider.newBuilder(GoogleAuthProvider.PROVIDER_ID, auth)
.setScopes(scopes)
.build()
auth.startActivityForSignInWithProvider(activity, provider)
}
fun signOut() {
auth.signOut()
}
```
- Enable auth in Console
- Add fingerprint
# Firestore
- Collections, documents
- Ways to structure our problem. One big collection or subcollections
- Create database
- Go to rules in Firestore
### Add
```kotlin=
suspend fun addNote(note: Note) {
val user = userRepository.userStream.first() ?: return
firestore.collection(USERS_COLLECTION)
.document(user.id)
.collection(NOTES_COLLECTION)
.add(note)
}
```
```
if request.auth != null;
```
### Get
```kotlin=
fun getAllNotesStream(): Flow<List<Note>> {
return userRepository.userStream.flatMapLatest { user ->
if (user != null) {
firestore.collection(USERS_COLLECTION)
.document(user.id)
.collection(NOTES_COLLECTION)
.toFlow()
} else {
flowOf(emptyList())
}
}
}
```
- Real time updates
## [Remote Config](https://firebase.google.com/docs/remote-config/get-started?platform=android)
- App
```kotlin=
val configSettings = FirebaseRemoteConfigSettings.Builder()
.setMinimumFetchIntervalInSeconds(0)
.build()
remoteConfig.setConfigSettingsAsync(configSettings)
remoteConfig.fetchAndActivate()
```
- UserRepo
```kotlin=
private val _userStream: MutableStateFlow<User?> = MutableStateFlow(null)
val userStream = _userStream.map { user ->
if (remoteConfig.getBoolean("hide_name")) {
user?.copy(name = "HIDDEN")
} else {
user
}
}
```
- AB Testing
- Conditions
### FCM
- Background
```xml=
<meta-data
android:name="com.google.firebase.messaging.default_notification_icon"
android:resource="@drawable/ic_notes" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_color"
android:resource="@color/red" />
<meta-data
android:name="com.google.firebase.messaging.default_notification_channel_id"
android:value="@string/general_channel_id" />
```
- Foreground
```xml=
<service
android:name=".FCMService"
android:exported="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
```
```kotlin=
class FCMService : FirebaseMessagingService() {
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
val title = message.notification?.title
val body = message.notification?.body
val notification =
NotificationCompat.Builder(this, getString(R.string.general_channel_id))
.setContentTitle(title)
.setContentText(body)
.setSmallIcon(R.drawable.ic_notes)
.setColor(Color.BLUE)
.setContentIntent(getContentIntent())
.build()
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(Random.nextInt(), notification)
}
private fun getContentIntent(): PendingIntent {
val notifyIntent = Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
}
return PendingIntent.getActivity(this, 0, notifyIntent, PendingIntent.FLAG_IMMUTABLE)
}
}
```