Labor07 - Firebase¶
Bevezető¶
A labor során a meglévő feladatkezelő alkalmazás kerül továbbfejlesztésre a Firebase Backend as a Service (BaaS) felhasználásával. A feladat célja, hogy szemléltesse, hogyan lehet közös backendet használó alkalmazást fejleszteni saját backend kód fejlesztése nélkül.
A Firebase manapság az egyik legnépszerűbb Backend as a Service megoldás Android, iOS és web kliensek támogatásával, mely számos szolgáltatást biztosít, például: - real-time adatbáziskezelés - storage - authentikáció - push értesítések - analytics - crash reporting
További általános információk a Firebase-ről: https://firebase.google.com/.
A laborfoglalkozás célja, hogy bemutassa a Firebase legfontosabb szolgáltatásait egy komplett alkalmazás megvalósítása keretében. Az alkalmazás az alábbi fő funkciókat támogatja: - regisztráció, bejelentkezés - üzenetek listázása - üzenetírás - képek csatolása üzenetekhez - üzenetek megjelenítése valós időben - crash reporting - analitika
Az anyag részletes megértéséhez javasoljuk, hogy figyelje a laborvezető utasításait és labor után is 10-20 percet szánjon a kódrészek megértésére.
Előkészületek¶
A feladatok megoldása során ne felejtsd el követni a feladat beadás folyamatát.
Git repository létrehozása és letöltése¶
-
Moodle-ben keresd meg a laborhoz tartozó meghívó URL-jét és annak segítségével hozd létre a saját repository-dat.
-
Várd meg, míg elkészül a repository, majd checkout-old ki.
Egyetemi laborokban, ha a checkout során nem kér a rendszer felhasználónevet és jelszót, és nem sikerül a checkout, akkor valószínűleg a gépen korábban megjegyzett felhasználónévvel próbálkozott a rendszer. Először töröld ki a mentett belépési adatokat (lásd itt), és próbáld újra.
-
Hozz létre egy új ágat
megoldas
néven, és ezen az ágon dolgozz. -
A
neptun.txt
fájlba írd bele a Neptun kódodat. A fájlban semmi más ne szerepeljen, csak egyetlen sorban a Neptun kód 6 karaktere.
A Firebase megismeréséhez ebben a laborban egy előre elkészített projektbe fogjuk integrálni a különböző szolgáltatásokat. Ez megtalálható a repository-n belül is, valamilyen probléma esetén a kezdőprojektet erről a linkről érhető el.
Ezután indítsuk el az Android Studio-t, majd nyissuk meg a kicsomagolt projektet.
FILE PATH
A projekt a repository-ban lévő Todo
könyvtárba kerüljön, és beadásnál legyen is felpusholva! A kód nélkül nem tudunk maximális pontot adni a laborra!
Ellenőrízzük, hogy a létrejött projekt lefordul és helyesen működik!
Projekt előkészítése, konfiguráció¶
Első lépésként létre kell hozni egy Firebase projektet a Firebase admin felületén (Firebase console), majd egy Android Studio projektet és a kettőt össze kell kötni:
- Navigáljunk a Firebase console felületére: https://console.firebase.google.com/ !
- Jelentkezzünk be!
- Hozzunk létre egy új projektet a Create project elemet választva!
- A projekt neve legyen BMETodoNEPTUN_KOD, ahol a
NEPTUN_KOD
helyére a saját Neptun kódunkat helyettesítsük! - A Gemini
- Az analitikát most még nem szükséges konfigurálni.
projekt név
A Neptun kódra azért van szükség, mert ugyanazon laborgép kulcsával ugyanolyan nevű projektet nem hozhatunk létre többször, és több laborcsoport lévén ebből probléma adódhatna. Ugyanerre lesz majd szükség a package név esetén is.
Sikeres projekt létrehozás után fussák át a laborvezetővel közösen a Firebase console felületét az alábbi elemekre kitérve:
- Authentication
- Cloud Firestore
- Storage.
Warning
Nézzük át a megnyitott projektet! Kiemelt figyelemmel vizsgáljuk át a projekt mostani felépítését, az új részeket (authentication, data package), illetve hogy hogyan lehetséges ebben átállni a Firebase szolgáltatásaira.
Adjuk hozzá az AndroidManifest.xml
fájlhoz az internet használati engedélyt:
<uses-permission android:name="android.permission.INTERNET" />
Firebase inicilaizáció, Authentication¶
Ezek után válasszuk Android Studioban a Tools -> Firebase menüpontot, melynek hatására jobb oldalt megnyílik a Firebase Assistant funkció.
A Firebase Assistant akkor fogja megtalálni a Firebase console-on létrehozott projektet, ha Android Studioba is ugyanazzal a Google accounttal vagyunk bejelentkezve, mint amivel a console-on létrehoztuk a projektet. Ellenőrizzük ezt mindkét helyen! Amennyiben a Firebase Assistant-ot nem sikerül beüzemelni, manuálisan is összeköthető a két projekt. A leírásban ismertetni fogjuk a lépéseket, amelyeket az Assistant végez el.
Válasszuk az Assistant-ban az Authentication szakaszt és azon belül az Authenticate using a custom authentication system-t, majd a Connect to Firebase gombot. Ezt követően egy dialog nyílik meg, ahol ha megfelelőek az accountok, a második szakaszt (Choose an existing Firebase or Google project) választva kiválaszthatjuk a projektet, amit a Firebase console-on már létrehoztunk. Itt egyébként lehetőség van új projektet is létrehozni. (Ha elsőre hibát látunk a projekttel való összekapcsolásnál, próbáljuk újra, másodszorra általában sikeresen megtörténik az Android Studio projekt szinkronizálása a Firebase projekttel.)
A háttérben valójában annyi történik, hogy az alkalmazásunk package neve és az aláíró kulcs SHA-1 hash-e alapján hozzáadódik egy Android alkalmazás a Firebase console-on lévő projektünkhöz, és az ahhoz tartozó konfigurációs (google-services.json
) fájl letöltődik a projektünk könyvtárába az alapértelmezett (app
) modul alá.
Ezt a lépéssorozatot manuálisan is végrehajthatjuk a Firebase console-on az Add Firebase to your Android app-et választva. A debug kulcs SHA-1 lenyomata ilyenkor a jobb oldalon található Gradle fülön generálható. A fenti sorban kattintsunk az Execute Gradle Task menüpontra, majd a felugró ablakban Írjuk be a gradle signingreport-ot, és nyomjunk egy entert. Ezek utánaz alsó Run ablakban megtalálható az SHA-1 kulcs.
Következő lépésben szintén az Assistant-ban az Authenticate using a custom authentication system alatt válasszuk az Add the Firebase Authentication SDK to your app elemet, itt látható is, hogy milyen módosítások történnek a projekt és modul szintű build.gradle.kts
fájlokban.
Sajnos a Firebase plugin nincs rendszeresen frissítve, és így előfordul, hogy a függőségek régi verzióját adja hozzá a build.gradle.kts
fájlokhoz. Ezért most frissíteni fogjuk az imént automatikusan felvett függőségeket, valamint innentől manuálisan fogjuk hozzáadni az újabbakat az Assistant használata helyett. Fontos, hogy mindenből az itt leírt verziót használjuk.
Ellenőrizzük a projekt szintű build.gradle.kts
fájlban a szerepel-e a google-services
plugins {
alias(libs.plugins.google.gms.google.services) apply false
...
}
Illetve, hogy a libs.versions.toml
fájlban megfelelő-e a verzió:
[versions]
googleGmsGoogleServices = "4.4.2"
...
[plugins]
google-gms-google-services = { id = "com.google.gms.google-services", version.ref = "googleGmsGoogleServices" }
...
A Firebase BoM segítségével egységesen tudjuk kezelni az összes firebase könyvtárunk verziószámát.
Vegyük fel a Firebase BoM-ot a libs.versions.toml
fájlba:
[versions]
firebaseBom = "33.11.0"
[libraries]
firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" }
Majd a modul szintű build.gradle.kts
fájlba is:
dependencies {
implementation(platform(libs.firebase.bom))
}
Ezek után cseréljük le a firebase-auth
függőségeket a következőre:
libs.versions.toml
:
[libraries]
firebase-auth = { group = "com.google.firebase", name = "firebase-auth-ktx" }
build.gradle.kts
:
dependencies {
implementation(libs.firebase.auth)
}
A generált projektváz többi általános függősége (pl. appcompat és ktx-core könyvtárak) is elavult lehet, ezt az Android Studio jelzi is sötétsárga háttérrel. Ezekre ráállva a kurzorral az Alt-Enter gyorsbillenytűvel kiválaszthatjuk ezeknek a frissítését.
Ahhoz, hogy az e-mail alapú regisztráció és authentikáció megfelelően működjön, a Firebase console-ban az Authentication -> Sign-in method alatt az Email/Password providert engedélyezni kell.
Készítsük el a megfelelő Service
osztályt. Hozzunk létre a data.auth
package-en belül a FirebaseAuthService
osztályt! Valósítsuk meg az AuthService
interfész egyes metódusait! Ehhez szükségünk lesz egy FirebaseAuth
objektumra, melyet külső forrásból fogunk megkapni:
package hu.bme.aut.android.todo.data.auth
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.UserProfileChangeRequest
import hu.bme.aut.android.todo.domain.model.User
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.tasks.await
class FirebaseAuthService(private val firebaseAuth: FirebaseAuth) : AuthService {
override val currentUserId: String? get() = firebaseAuth.currentUser?.uid
override val hasUser: Boolean get() = firebaseAuth.currentUser != null
override val currentUser: Flow<User?> get() = callbackFlow {
this.trySend(currentUserId?.let { User(it) })
val listener =
FirebaseAuth.AuthStateListener { auth ->
this.trySend(auth.currentUser?.let { User(it.uid) })
}
firebaseAuth.addAuthStateListener(listener)
awaitClose { firebaseAuth.removeAuthStateListener(listener) }
}
override suspend fun signUp(email: String, password: String) {
firebaseAuth.createUserWithEmailAndPassword(email,password)
.addOnSuccessListener { result ->
val user = result.user
val profileChangeRequest = UserProfileChangeRequest.Builder()
.setDisplayName(user?.email?.substringBefore('@'))
.build()
user?.updateProfile(profileChangeRequest)
}.await()
}
override suspend fun authenticate(email: String, password: String) {
firebaseAuth.signInWithEmailAndPassword(email, password).await()
}
override suspend fun sendRecoveryEmail(email: String) {
firebaseAuth.sendPasswordResetEmail(email).await()
}
override suspend fun deleteAccount() {
firebaseAuth.currentUser!!.delete().await()
}
override suspend fun signOut() {
firebaseAuth.signOut()
}
}
AuthService
interfészhez illeszteni a Firebase által biztosított API-t!
Sokszor a biztosított API közvetlenül megfeleltethető az általunk definiált szolgáltatásokkal, mint például a currentUser
vagy hasUser
mezőknél. Itt egyedül arra kell figyelnünk, hogy ne maradjon le a get()
definíció, akkor ugyanis a Service
létrejöttekor történne egy értékadás, nem pedig minden egyes kiolvasásnál egy függvényhívás.
Ha szerencsénk van, akkor a felhasznált API beépítetten támogatni fogja a Kotlin különböző funkcionalitásait, mint például a Kotlinos típusok, contract-ok és coroutine-ok. Sokszor viszont Java alapú könyvtárakat kapunk, amiket nekünk kell adaptálni Kotlin környezetre. Erre egy jó példa a currentUser
mező. Ez a callbackFlow
metódust használja, melynek segítségével a callback jellegű API-t tudjuk átalakítani Flow
jellegű eredménnyé. A blokkon belül egy listenert regisztrálunk be, mellyel meg tudjuk figyelni, ha változás történik az aktuális felhasználók körében. Ekkor a trySend()
metódussal tudjuk a Flow-ra feliratkozó felé elküldeni az új felhasználó adatait. Érdemes arra is figyelni, hogy ez a listener csak a feliratkozás után kezd el értékeket kiküldeni. Annak érdekében, hogy a UI egyből megkapja az aktuális értéket, a feliratkozás előtt kiküldjük az aktuális felhasználó adatait is.
A többi utasításnál a Firebase egy Task
típusú eredmény objektummal tér vissza. A Java világában erre fel tudunk iratkozni, és az eredményét egy Callback-ben le tudjuk kezelni. Szerencsére azonban a Firebase általános könytvárában megtalálható egy Kotlin kiegészítő metódus, mellyel a Task
bevárható coroutine kontextusban az await()
kulcsszóval. A teljesség kedvéért nézzük meg az alábbi példát, hogyan lehet egy Callback jellegű API-t átalakítani suspend
fajtájú függvénnyé:
override suspend fun authenticate(email: String, password: String) = suspendCoroutine { continuation ->
firebaseAuth
.signInWithEmailAndPassword(email, password)
.addOnSuccessListener { continuation.resume(Unit) }
.addOnFailureListener { continuation.resumeWithException(it) }
}
A suspendCoroutine
metódus le fogja futtatni a benne megadott blokkot, majd addig várakoztatja a coroutine-t, ameddig a blokkban megkapott Continuation
objektumon keresztül nem jelezzük a hívás végeredményét. Ezzel a függvénnyel könnyedén át tudjuk alakítani a Callback
jellegű működéseket suspend
alapúra. Arra azonban figyeljünk, hogy minden esetben meghívódjon a Continuation
valamelyik resume
metódusa, ellenkező esetben ugyanis befagy az adott coroutine, sose fog tudni továbblépni. Hasonlóan hasznos függvény a suspendCancellableCoroutine
, mellyel azokat az eseteket is le tudjuk kezelni, ha a coroutine-t a folyamat közben törlik.
Állítsuk át az alkalmazásunkat, hogy ezt az új FirebaseAuthService
-t használja! Ehhez módosítsuk a TodoApplication
osztályunkat:
package hu.bme.aut.android.todo
import android.app.Application
import com.google.firebase.auth.FirebaseAuth
import hu.bme.aut.android.todo.data.auth.AuthService
import hu.bme.aut.android.todo.data.auth.FirebaseAuthService
import hu.bme.aut.android.todo.data.todos.MemoryTodoService
import hu.bme.aut.android.todo.data.todos.TodoService
class TodoApplication : Application(){
override fun onCreate() {
super.onCreate()
authService = FirebaseAuthService(FirebaseAuth.getInstance())
todoService = MemoryTodoService()
}
companion object{
lateinit var authService: AuthService
lateinit var todoService: TodoService
}
}
Próbáljuk ki az alkalmazást! Hozzunk létre egy új felhasználót!
jelszó
Ugyan nem kapunk semmi visszajelzést, de a Firebase nem fogad el 6 karakternél rövidebb jelszót. Így amennyiben rövid a jelszavunk, úgy tűnhet, hogy a gombnyomás hatására nem történik semmi, nem működik a regisztráció. Ilyenkor ellenőrizzük, hogy mindenképpen legalább 6 hosszú jelszót adtunk-e meg.
BEADANDÓ (1 pont)
Készíts egy képernyő képet, amin látszódik Firebase Authentication oldalán a beregisztrált felhasználó, illetve a FirebaseAuthService
forráskódja, melyben a Neptun-kód komment formájában látható. A képernyőkép szükséges feltétele a pontszám megszerzésének.
Feladatok listázása, készítése¶
Következő lépésben a feladatok listázását fogjuk implementálni a projekten belül.
Adjuk hozzá a projekthez a Cloud Firestore támogatást:
libs.versions.toml
:
[libraries]
firebase-firestore-ktx = { group = "com.google.firebase", name = "firebase-firestore-ktx" }
Modul szintű build.gradle.kts
:
dependencies {
implementation(libs.firebase.firestore.ktx)
}
Kapcsoljuk be a Cloud Firestore-t a Firebase console-on is . Az adatbázist test mode-ban fogjuk használni, így egyelőre publikusan írható/olvasható lesz, de cserébe nem kell konfigurálnunk a hozzáférés-szabályozást. Ezt természetesen később mindenképp meg kellene tenni egy éles projektben.
Warning
A test mode-ban konfigurált adatbázis ugyan publikusan írtahó/olvasható, de alapértelmezés szerint egy időbeni korlát van rá. Ez a mostani labornál nem okoz problémát, de egy hosszabb projektnél, mint például a házi feladat elkészítése, erre érdemes figyelni.
Locationnek válasszunk egy hozzánk közel eső opciót.
Hozzuk létre a data.todos
package-en belül a firebase
package-et. Ebben két osztályt fogunk definiálni: a Firestore-ban tárolt adatobjektum osztály modelljét, illetve a kommunikációt megvalósító service kódját.
Hozzuk először létre az adatot reprezentáló osztályt FirebaseTodo
néven:
package hu.bme.aut.android.todo.data.todos.firebase.model
import com.google.firebase.Timestamp
import com.google.firebase.firestore.DocumentId
import hu.bme.aut.android.todo.domain.model.Priority
import hu.bme.aut.android.todo.domain.model.Todo
import kotlinx.datetime.*
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.Date
data class FirebaseTodo(
@DocumentId val id: String = "",
val title: String = "",
val priority: Priority = Priority.NONE,
val dueDate: Timestamp = Timestamp.now(),
val description: String = ""
)
fun FirebaseTodo.asTodo() = Todo(
id = id,
title = title,
priority = priority,
dueDate = LocalDateTime
.ofInstant(Instant.ofEpochSecond(dueDate.seconds), ZoneId.systemDefault())
.toKotlinLocalDateTime()
.date,
description = description,
)
fun Todo.asFirebaseTodo() = FirebaseTodo(
id = id,
title = title,
priority = priority,
dueDate = Timestamp(Date.from(dueDate.atStartOfDayIn(TimeZone.currentSystemDefault()).toJavaInstant())),
description = description,
)
Ebben a fájlban definiáltuk a két átalakító függvényt is, mellyel a Firebase és az alkalmazás többi részében használt Todo osztály között tudunk átalakítani. Az egyedüli bonyolult rész a Firebase által használt Timestamp
osztály használata az időpont eltárolására, erre most részletesen nem térünk ki.
Hozzuk létre a feladatok tárolását végző FirebaseTodoService
osztályt is ebben a package-ben:
package hu.bme.aut.android.todo.data.todos.firebase
import com.google.firebase.firestore.FirebaseFirestore
import com.google.firebase.firestore.ktx.snapshots
import com.google.firebase.firestore.ktx.toObjects
import com.google.firebase.firestore.ktx.toObject
import hu.bme.aut.android.todo.data.auth.AuthService
import hu.bme.aut.android.todo.data.todos.TodoService
import hu.bme.aut.android.todo.data.todos.firebase.model.FirebaseTodo
import hu.bme.aut.android.todo.data.todos.firebase.model.asFirebaseTodo
import hu.bme.aut.android.todo.data.todos.firebase.model.asTodo
import hu.bme.aut.android.todo.domain.model.Todo
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.tasks.await
class FirebaseTodoService(
private val firestore: FirebaseFirestore,
private val authService: AuthService
) : TodoService {
override val todos: Flow<List<Todo>> = authService.currentUser.flatMapLatest { user ->
if (user == null) flow { emit(emptyList()) }
else currentCollection(user.id)
.snapshots()
.map { snapshot ->
snapshot
.toObjects<FirebaseTodo>()
.map {
it.asTodo()
}
}
}
override suspend fun getTodo(id: String): Todo? =
authService.currentUserId?.let {
currentCollection(it).document(id).get().await().toObject<FirebaseTodo>()?.asTodo()
}
override suspend fun saveTodo(todo: Todo) {
authService.currentUserId?.let {
currentCollection(it).add(todo.asFirebaseTodo()).await()
}
}
override suspend fun updateTodo(todo: Todo) {
authService.currentUserId?.let {
currentCollection(it).document(todo.id).set(todo.asFirebaseTodo()).await()
}
}
override suspend fun deleteTodo(id: String) {
authService.currentUserId?.let {
currentCollection(it).document(id).delete().await()
}
}
private fun currentCollection(userId: String) =
firestore.collection(USER_COLLECTION).document(userId).collection(TODO_COLLECTION)
companion object {
private const val USER_COLLECTION = "users"
private const val TODO_COLLECTION = "todos"
}
}
Végül ne felejtsük el befrissíteni a TodoApplication
osztályunkat, hogy a Firestoreban tárolt feladatokat használja az alkalmazás:
package hu.bme.aut.android.todo
import android.app.Application
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.firestore.FirebaseFirestore
import hu.bme.aut.android.todo.data.auth.AuthService
import hu.bme.aut.android.todo.data.auth.FirebaseAuthService
import hu.bme.aut.android.todo.data.todos.TodoService
import hu.bme.aut.android.todo.data.todos.firebase.FirebaseTodoService
class TodoApplication : Application(){
override fun onCreate() {
super.onCreate()
authService = FirebaseAuthService(FirebaseAuth.getInstance())
todoService = FirebaseTodoService(FirebaseFirestore.getInstance(), authService)
}
companion object{
lateinit var authService: AuthService
lateinit var todoService: TodoService
}
}
Próbáljuk ki az alkalmazásunkat! Ellenőrizzük, hogy tényleg létrejönnek az adatbázisban is a feladatok.
BEADANDÓ (1 pont)
Készíts egy képernyő képet, amin látszódik Firebase Firestore oldalán a létrehozott feladat, illetve a futó alkalmazás, melyben az egyik létrehozott feladat tartalmazza a Neptun-kódot. A képernyőkép szükséges feltétele a pontszám megszerzésének.
Messaging, Crashlytics, Analytics
A kövezkező technológiák átfutási ideje sajnos hosszabb, így az eredményre nem ritkán órákat is várni kell. (A notificationnek néhány perc alatt meg kell jönnie.)
Push értesítések¶
Adjuk hozzá a projektünkhöz a Firebase Messaging függőséget:
libs.versions.toml
:
[libraries]
firebase-messaging-ktx = { group = "com.google.firebase", name="firebase-messaging-ktx" }
build.gradle.kts
:
dependencies {
implementation(libs.firebase.messaging.ktx)
}
Csupán ennyi elegendő a push alapvető működéséhez, ha így újrafordítjuk az alkalmazást, a Firebase felületéről vagy API-jával küldött push üzeneteket automatikusan megkapják a mobil kliensek és egy Notification-ben megjelenítik.
Próbáljuk ki a push küldést a Firebase console-ról (Cloud messaging menüpont alatt Send your first message), és vizsgáljuk meg, hogyan érkezik meg telefonra, ha nem fut az alkalmazás. (Amikor fut az alkalmazás, akkor tőlünk várja az üzenet lekezelését az API.)
A Notification
szekció alatt írjuk be az üzenet címét és szövegét, a Target
résznél pedig
válasszuk ki az alkalmazást, hogy minden futó példány megkapja az üzenetet.
Természetesen lehetőség van saját push üzenet feldolgozó szolgáltatás készítésére is egy FirebaseMessagingService
létrehozásával, melyről további részletek itt olvashatók.
Crashlytics¶
A Firebase Console-on először navigáljunk a Crashlytics menüpontra, és kapcsoljuk be a funkciót. Válasszuk az új Firebase alkalmazás integrációját.
Adjuk hozzá a projekthez a függőségeket:
libs.versions.toml
:
[versions]
crashlytics = "3.0.3"
[libraries]
firebase-analytics-ktx = { module = "com.google.firebase:firebase-analytics-ktx" }
firebase-crashlytics-ktx = { module = "com.google.firebase:firebase-crashlytics-ktx" }
[plugins]
firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "crashlytics" }
Ezekkel a módosításokkal többek között egy Gradle plugint adtunk hozzá a projektünkhöz, amit a projekt szintű build.gradle
fájl elején be kell kapcsolnunk a már meglévők után:
plugins {
alias(libs.plugins.firebase.crashlytics) apply false
}
Majd a modul szintű build.gradle.kts
fájlban:
plugins {
alias(libs.plugins.firebase.crashlytics)
}
Végül pedig szükségünk van két egyszerű Gradle függőségre is, amit a meglévő Firebase függőségek mellé helyezhetünk, a modul szintű build.gradle.kts
fájlban:
dependencies {
implementation(libs.firebase.crashlytics.ktx)
implementation(libs.firebase.analytics.ktx)
}
Vegyünk fel egy új akciót TodoListScreen
TodoAppBar
részébe, amivel az alkalmazást hibával be tudjuk zárni:
TodoAppBar(
title = UiText.StringResource(id = R.string.app_bar_title_todos).asString(context),
actions = {
IconButton(onClick = {
viewModel.signOut()
onSignOut()
}) {
Icon(
imageVector = Icons.AutoMirrored.Filled.Logout,
contentDescription = null
)
}
IconButton(onClick = {
throw RuntimeException("Test crash!")
}) {
Icon(
imageVector = Icons.Default.Close,
contentDescription = null
)
}
}
)
Végül a Firebase console-ban is engedélyezzük a funkciót a Crashlytics menüpont alatt.
Próbáljuk ki saját hibajelzések készítését a menü eseménykezelőjében. Vizsgáljuk meg, megérkezik-e a Firebase Console-ba a hibaüzenet!
Analitika¶
Most engedélyezzük az analitikát a Firebase console Analytics menüpontja alatt!
Az SDK-t már beállítottuk, ezért a megfelelő Account kiválasztása után a Nextre, majd a Finishre kattinhatunk.
Ezután az alkalmazás már naplóz alapvető analitikákat, használati statisztikákat, melyek ugyanezen menüpont alatt lesznek elérhetők.
Emellett természetesen lehetőség van az analitika kibővítésére és testreszabására is. Az ehhez szükséges Firebase függőséget a Crashlyticsnél már felvettük.
Készítsünk saját analitika üzeneteket egy újabb akcióból küldve, ami szintén a TopAppBar
-ba kerül:
IconButton(onClick = {
val bundle = Bundle()
bundle.putString("demo_key", "idabc")
bundle.putString("data_key", "mydata")
FirebaseAnalytics.getInstance(context)
.logEvent(FirebaseAnalytics.Event.LOGIN, bundle)
}) {
Icon(imageVector = Icons.AutoMirrored.Filled.Message, contentDescription = null)
}
Fontos kiemelni, hogy nem garantált, hogy az analitika valós időben látszik a Firebase console-on. 30 percig vagy akár tovább is tarthat, mire egy-egy esemény itt megjelenik.
BEADANDÓ (1 pont)
Készíts egy képernyő képet, amin látszódik a futó alkalmazás a lista oldalon, illetve a két új akciógomb forráskódja, melyben a Neptun-kód komment formájában látható. A képernyőkép szükséges feltétele a pontszám megszerzésének.
Önálló feladatok¶
Automatikus bejelentkezés¶
Valósítsuk meg, hogy a bejelentkező képernyő helyett egyből a lista oldalra ugorjunk, ha a felhasználó kijelentkezés helyett csak bezárta az alkalmazást!
BEADANDÓ (1 pont)
Készíts egy képernyő képet, amin látszódik a futó alkalmazás a lista oldalon, az automatikus bejelentkezést megvalósító forráskód, melyben a Neptun-kód komment formájában látható. A képernyőkép szükséges feltétele a pontszám megszerzésének.
Navigáció esemény jelzése¶
Küldjünk egy analitikai eseményt abban az esetben, ha a felhasználó megnyitja valamelyik feladatát! Az esemény tartalmazza a feladat azonosítóját is. Figyeljünk arra, hogy megfelelő névvel küldjük el az eseményt!
BEADANDÓ (1 pont)
Készíts egy képernyő képet, amin látszódik a futó alkalmazás a lista oldalon, a navigáció során az eseményt kiküldő forráskód, melyben a Neptun-kód komment formájában látható. A képernyőkép szükséges feltétele a pontszám megszerzésének.