I am trying to read a String field that I recently updated. My function that should return the String, returns an empty String.
Here is my codes:
Function that returns the updated String:
fun readUsersOfficeHoursList(email: String, callback: (String) -> Unit) {
val database = FirebaseFirestore.getInstance()
val ref = database.collection("Users").document(email)
ref.get()
.addOnSuccessListener { document ->
if (document != null) {
val officeHoursList = document.get("office_hours_list") as String
callback(officeHoursList)
Log.d(TAG, "office_hours_list successfully read")
} else {
Log.d(TAG, "Is empty")
callback("")
}
}
.addOnFailureListener { exception ->
if (exception is FirebaseFirestoreException) {
Log.e(TAG, "Error getting document: ", exception)
}
callback("")
}
}
Function that updates the field:
fun updateUserOfficeHoursList(email: String, code: String){
val database = FirebaseFirestore.getInstance()
val ref = database.collection("Users").document(email)
var list = ""
ref.get()
.addOnSuccessListener { document ->
if (document != null) {
list = document.get("office_hours_list") as String? ?: ""
Log.d(TAG, "office_hours_list successfully read")
if(!list.contains(code)){
if (list.isEmpty()){
list = code
}
else{
list = "$list, $code"
}
ref.update("office_hours_list", list)
.addOnSuccessListener { Log.d(TAG, "List successfully updated") }
.addOnFailureListener { e -> Log.w(TAG, "Error updating list", e) }
}else{
Log.d(TAG, "code already in the list")
}
} else {
Log.d(TAG, "Is empty")
}
}
.addOnFailureListener { exception ->
if (exception is FirebaseFirestoreException) {
Log.e(TAG, "Error getting document: ", exception)
}
}
}
My test code:
myClass.updateUserOfficeHoursList("tfh#gmail.com", "1VVNFxSGbYaauk3iLV80,
1a79bhnaJsY5OhHwaYhH")
myClass.readUsersOfficeHoursList("tfh#gmail.com") {fieldValue1 ->
textView.text = fieldValue1
Log.d(TAG, "fieldValue1: $fieldValue1")
}
The error I get:
**2023-01-16 13:43:50.523 8529-8529/com.example.myapplication E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.myapplication, PID: 8529
java.lang.NullPointerException: null cannot be cast to non-null type kotlin.String
at com.example.myapplication.RepositoryMockup$readUsersOfficeHoursList$1.invoke(RespositoryMockup.kt:239)
at com.example.myapplication.RepositoryMockup$readUsersOfficeHoursList$1.invoke(RespositoryMockup.kt:237)
at com.example.myapplication.RepositoryMockup.readUsersOfficeHoursList$lambda$15(RespositoryMockup.kt:237)
at com.example.myapplication.RepositoryMockup.$r8$lambda$Rz1CeV4qQ243JiYTVZ8j2Ijj1y0(Unknown Source:0)
at com.example.myapplication.RepositoryMockup$$ExternalSyntheticLambda16.onSuccess(Unknown Source:2)
at com.google.android.gms.tasks.zzm.run(com.google.android.gms:play-services-tasks##18.0.1:1)
at android.os.Handler.handleCallback(Handler.java:942)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7898)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:936)
**
This is the wrong way to check whether a document you tried to read exists:
if (document != null) {
When no document exists, the Firestore API returns a DocumentSnapshot (so not null) with its exists property set to false. So the correct check would be:
if (document.exists) {
Related
I am currently trying to read data from Cloud Firestore, but while doing this I get this error message:
For-loop range must have an 'iterator()' method
And I don't know what to do to get rid of it. Maybe there is an easy way to fix this I haven't thought of yet...
I have been using this google firebase tutorial but with no success.
The error message comes on the documents in the for (document in documents) part of the code
The code I am using is this:
`
fun Varer() {
var firestore: FirebaseFirestore = FirebaseFirestore.getInstance()
var docRef = firestore.collection("varer").document("varer")
var source = Source.DEFAULT
docRef.get(source).addOnSuccessListener { documents ->
for (document in documents) {
Log.d(TAG, "${document.id} => ${document.data}")
var v1 = VareFB(
tittel = document["tittel"].toString(),
pris = document.getDouble("pris"),
beskrivelse = document["beskrivelse"].toString(),
bildeID = document["bildeID"].toString(),
)
varerListe.add(v1)
Log.d(TAG, document["tittel"].toString())
Log.d(TAG, v1.toString())
}
}
.addOnFailureListener { exception ->
Log.w(TAG, "Feil med henting av varer: ", exception)
}
}
`
data class VareFB (
val tittel: String,
val pris: Double?,
val beskrivelse: String,
val bildeID: String,
) {
#Exclude
fun toMap(): Map<String, Any?> {
return mapOf(
"tittel" to tittel,
"pris" to pris,
"beskrivelse" to beskrivelse,
"bildeID" to bildeID,
)
}
}
`
object VarerObject {
var varerListe = mutableListOf<VareFB>()
}
`
Edit:
fun Varer() {
var firestore: FirebaseFirestore = FirebaseFirestore.getInstance()
var docRef = firestore.collection("varer").document("varer")
var source = Source.DEFAULT
docRef.get(source).addOnSuccessListener { snapshot ->
for (document in snapshot.documents) {
Log.d(TAG, "${document.id} => ${document.data}")
var v1 = VareFB(
tittel = document["tittel"].toString(),
pris = document.getDouble("pris"),
beskrivelse = document["beskrivelse"].toString(),
bildeID = document["bildeID"].toString(),
)
varerListe.add(v1)
Log.d(TAG, document["tittel"].toString())
Log.d(TAG, v1.toString())
}
}
.addOnFailureListener { exception ->
Log.w(TAG, "Feil med henting av varer: ", exception)
}
}
documents is a QuerySnapshot object so there is no way you can iterate over it. To be able to iterate through the documents, you have to get the documents out of the QuerySnapshot object like this:
firestore.collection("varer").get(source).addOnSuccessListener { documents ->
for (document in documents.documents) {
//
}
}
But in my opinion, it's a little confusing. So I would rather name the object that comes from the lambda expression snapshot:
// 👇
firestore.collection("varer").get(source).addOnSuccessListener { snapshot ->
for (document in snapshot.documents) {
// 👆
}
}
I created a suspended function that I am calling from ViewModel, the function takes a set of MyFile abstract class and then iterates through the set to get values and eventually insert them via content resolver.
There is no way the set values are being changed. It is immutable set after all. But still somehow, as the execution reaches insert function it throws ConcurrentModificationException
NOTE: The first iteration went smooth. It's the second one that causes the crash.
Please can anyone help me with this?
Thank you
suspend fun unhideSelectedFiles(files: Set<MyFile>, address: String? = null): Flow<UIEvent> = flow {
val context = App.getContext()
files.forEach { currentFile ->
Log.i(TAG, "unhideSelectedFiles: currentFile: ${currentFile.name}")
if (currentFile.currentPath.isBlank()) {
Log.e(TAG, "unhideSelectedFiles: ${currentFile.name} does not contain a current path")
return#forEach
}
val collection =
if(Build.VERSION.SDK_INT>=Build.VERSION_CODES.Q){
MediaStore.Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
}else{
MediaStore.Images.Media.EXTERNAL_CONTENT_URI
}
val contentValues: ContentValues =
ContentValues().apply {
put(MediaStore.Images.Media.DISPLAY_NAME, currentFile.name)
put(MediaStore.Images.Media.MIME_TYPE, currentFile.mimeType)
put(MediaStore.Images.Media.IS_PENDING, 1)
put(
MediaStore.Images.Media.RELATIVE_PATH,
address ?: currentFile.originalPathWithoutFileName
)
}
val uri = context.contentResolver.insert(collection, contentValues)
uri?.let {
try {
context.contentResolver.openOutputStream(it)?.use { outputStream ->
val buf = ByteArray(COPY_BYTE_SIZE)
val inputStream = FileInputStream(File(currentFile.currentPath))
var len: Int
while (inputStream.read(buf).also { len = it } > 0) {
outputStream.write(buf, 0, len)
}
outputStream.close()
inputStream.close()
contentValues.clear()
contentValues.put(MediaStore.MediaColumns.IS_PENDING, 0)
val isUpdated =
context.contentResolver.update(it, contentValues, null, null)
if (isUpdated > 0) {
if (File(currentFile.currentPath).delete()) {
emit(UIEvent.FileDone(currentFile))
Log.i(
TAG,
"unhideSelectedFiles: ${currentFile.name} unhidden successfully"
)
} else {
Log.e(
TAG,
"unhideSelectedFiles: Could not delete the image file from internal storage",
)
emit(UIEvent.Error("Could not delete ${currentFile.name} from hidden directory"))
}
} else {
Log.e(
TAG,
"unhideSelectedFiles: something went wrong with the file: ${currentFile.name}",
)
emit(UIEvent.Error("Something went wrong with the file: ${currentFile.name}"))
}
}
} catch (e: Exception) {
Log.e(
TAG,
"unhideSelectedFiles: file: ${currentFile.name}\n\nException: ${e.localizedMessage}",
)
emit(UIEvent.Error("Something went wrong: ${e.localizedMessage}"))
}
}
Log.i(TAG, "unhideSelectedFiles: file ${currentFile.name} task completed")
}
emit(UIEvent.Finished())
}
Here is the stack trace:
2022-03-31 06:47:25.806 13886-6547/com.androidbull.incognito.vaultreborn E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-2
Process: com.androidbull.incognito.vaultreborn, PID: 13886
java.util.ConcurrentModificationException
at java.util.LinkedHashMap$LinkedHashIterator.nextNode(LinkedHashMap.java:757)
at java.util.LinkedHashMap$LinkedKeyIterator.next(LinkedHashMap.java:780)
at com.androidbull.incognito.vaultreborn.utils.UtilsKt$unhideSelectedFiles$1.invokeSuspend(Utils.kt:302)
at com.androidbull.incognito.vaultreborn.utils.UtilsKt$unhideSelectedFiles$1.invoke(Unknown Source:8)
at com.androidbull.incognito.vaultreborn.utils.UtilsKt$unhideSelectedFiles$1.invoke(Unknown Source:4)
at kotlinx.coroutines.flow.SafeFlow.collectSafely(Builders.kt:61)
at kotlinx.coroutines.flow.AbstractFlow.collect(Flow.kt:212)
at com.androidbull.incognito.vaultreborn.viewModels.PhotosViewModel$unhideFiles$1.invokeSuspend(PhotosViewModel.kt:387)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
The function unhideSelectedFiles(..) is being called from a ViewModel just like this:
fun unhideFiles(selectedImages: Set<MyFile>, address: String? = null) {
viewModelScope.launch(IO) {
unhideSelectedFiles(selectedImages, address)
.collect {
if (it is UIEvent.FileDone) {
repository.deleteImage((it.file as ImageFile).toImageEntity())
} else {
/**
* you can show the progress of file by emitting the received flow and catching
* it in Fragment and showing dialog/bottomsheet accordingly
*/
}
}
}
}
So, my roommate and I are trying to develop an app to help students living on campus at our school keep track of their laundry. However, we are having trouble creating new laundry loads.
Our addLoad function is supposed to add a LaundryHolder object to Firebase (containing the machine number, whether it is a washer or dryer, who owns the load, and how many seconds are left for the load), whereas the LaundryLoad object contains a LaundryHolder, observer function (notifyDataSetChanged() for the LaundryLoadFragment), and timer (with time form LaundryHolder).
In Firebase, each clothingItem has a load ID with which to identify which load it is in on the user side. For our implementation to work, we need to fetch the ID which Firebase gives our LaundryHolder, which is why we are adding an onSuccessListener to a temporary query. The issue arises, however, when the query doesn't succeed or fail, and we can't figure out what is going on here.
This is the error we get:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: edu.rosehulman.roselaundrytracker, PID: 11847
kotlin.UninitializedPropertyAccessException: lateinit property load has not been initialized
at edu.rosehulman.roselaundrytracker.model.LaundryLoadViewModel.addLoad(LaundryLoadViewModel.kt:42)
at edu.rosehulman.roselaundrytracker.adapter.AddLoadAdapter.addLoad(AddLoadAdapter.kt:67)
at edu.rosehulman.roselaundrytracker.fragment.AddLoadFragment.onCreateView$lambda-1(AddLoadFragment.kt:32)
at edu.rosehulman.roselaundrytracker.fragment.AddLoadFragment.$r8$lambda$lIyFvxsLH_bCt-kHzadMjy2Ls_Y(Unknown Source:0)
at edu.rosehulman.roselaundrytracker.fragment.AddLoadFragment$$ExternalSyntheticLambda0.onClick(Unknown Source:2)
at android.view.View.performClick(View.java:7455)
at com.google.android.material.button.MaterialButton.performClick(MaterialButton.java:1119)
at android.view.View.performClickInternal(View.java:7432)
at android.view.View.access$3700(View.java:835)
at android.view.View$PerformClick.run(View.java:28810)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loopOnce(Looper.java:201)
at android.os.Looper.loop(Looper.java:288)
at android.app.ActivityThread.main(ActivityThread.java:7842)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
Does anyone have any idea?
class LaundryLoadViewModel: ViewModel() {
private var loads = ArrayList<LaundryLoad>()
private var curPos = 0
lateinit var ref: CollectionReference
lateinit var uid: String
private var onlyOwned = true
private val subscriptions = HashMap<String, ListenerRegistration>()
fun getPreference() = onlyOwned
fun addLoad(machineNumber: Int, machineType: String, contents: ArrayList<ClothingItem>, time: Long, observer: () -> Unit){
val holder = LaundryHolder(machineNumber, machineType.lowercase(Locale.getDefault()) == "dryer", time * LaundryLoadFragment.SEC_TO_MIN, uid)
// val load = LaundryLoad(holder, observer)
// loads.add(load)
ref.add(holder)
lateinit var load: LaundryLoad
val query = ref
.whereEqualTo("machineNumber",machineNumber)
.whereEqualTo("owner",uid)
query.get().addOnSuccessListener { snapshot ->
snapshot.documents.forEach {
Log.d(Constants.TAG,"Retrieving load from Firebase")
load = LaundryLoad.from(it, observer)
}
}
query.get().addOnFailureListener {
Log.d(Constants.TAG,"Retrieval failed due to $it")
}
// val query = ref.whereEqualTo("machineNumber",machineNumber).whereEqualTo("dryer",machineType.lowercase(Locale.getDefault())=="dryer")
load.addMany(contents)
loads.add(load)
}
fun addListener(fragmentName: String, observer: () -> Unit) {
lateinit var subscription: ListenerRegistration
loads.clear()
val auth = Firebase.auth
val user = auth.currentUser!!
val clothes = ArrayList<ClothingItem>()
uid = user.uid
ref = Firebase.firestore.collection(LaundryLoad.COLLECTION_PATH)
val ref2 = Firebase.firestore.collection(ClothingItem.COLLECTION_PATH)
val inLoadQuery = ref2.whereNotEqualTo("load","")
inLoadQuery.addSnapshotListener { snapshot: QuerySnapshot?, error: FirebaseFirestoreException? ->
error?.let {
Log.d(Constants.TAG, "Error: $it")
return#addSnapshotListener
}
snapshot?.documents?.forEach {
clothes.add(ClothingItem.from(it))
}
}
if(onlyOwned) {
val query = ref.whereEqualTo("owner",uid)
subscription = query
.addSnapshotListener { snapshot: QuerySnapshot?, error: FirebaseFirestoreException? ->
error?.let {
Log.d(Constants.TAG, "Error: $it")
return#addSnapshotListener
}
retrieveLoads(snapshot, clothes, observer)
}
} else {
subscription = ref
.addSnapshotListener { snapshot: QuerySnapshot?, error: FirebaseFirestoreException? ->
error?.let {
Log.d(Constants.TAG, "Error: $it")
return#addSnapshotListener
}
retrieveLoads(snapshot, clothes, observer)
}
}
subscriptions[fragmentName] = subscription
observer()
}
private fun retrieveLoads(snapshot: QuerySnapshot?, clothes: ArrayList<ClothingItem>, observer: () -> Unit) {
snapshot?.documents?.forEach {
loads.add(LaundryLoad.from(it, observer))
}
for (load in loads) {
for (item in clothes) {
if (item.load == load.getId()) {
load.addToLoad(item)
}
}
}
}
fun removeListener(fragmentName: String) {
for(load in loads) {
ref.document(load.getId()).set(load.laundryHolder)
}
subscriptions[fragmentName]?.remove()
subscriptions.remove(fragmentName)
}
fun togglePreference() {
onlyOwned = !onlyOwned
}
}
It looks like ref has not been initialized when you ref.add(holder) in addLoad. It's impossible for us to say why that is, as the code that calls addLoad seems to be missing, but the stack trace should point you pretty directly to where the problem is.
I'm trying to build a firebase login function, but the code crashes even though the exception thrown is caught.
When I output the exception as a log the code works, but when I pass it on as an exception it crashes.
I would like to pass the exception on to the MainActivity in order to process it correctly there, i.e. to have the exception.message output there as toast.
The login process works with the use of valid login data without problems only if firebase reports an exception it crashes.
internal var loginResult = MutableLiveData<LoginResult>()
inner class LoginResult {
var authResult: AuthResult? = null
var exception: Exception? = null
var isSuccessful = false
constructor(authResult: AuthResult?, isSuccessful: Boolean) {
this.authResult = authResult
this.isSuccessful = isSuccessful
}
constructor(authResult: AuthResult?, exception: Exception?, isSuccessful: Boolean) {
this.authResult = authResult
this.exception = exception
this.isSuccessful = isSuccessful
}
}
fun login(context: Context, email: String, password: String) {
FirebaseAuth.getInstance().signInWithEmailAndPassword(email, password)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
loginResult.value = LoginResult(task.result, task.isSuccessful)
startSession(context, LoginType.CREDENTIAL)
saveCredentialsEncrypted(context, email, password)
} else {
try {
loginResult.value = LoginResult(task.result, task.exception, task.isSuccessful)
} catch (e: FirebaseAuthInvalidCredentialsException) {
loginResult.value = LoginResult(task.result, e, task.isSuccessful)
} catch (e: Exception) {
loginResult.value = LoginResult(task.result, e, task.isSuccessful)
}
}
}
}
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.simplesaakotlin, PID: 23984
com.google.android.gms.tasks.RuntimeExecutionException: com.google.firebase.auth.FirebaseAuthInvalidCredentialsException: The password is invalid or the user does not have a password.
at com.google.android.gms.tasks.zzu.getResult(com.google.android.gms:play-services-tasks##17.1.0:15)
at com.example.simplesaakotlin.auth.AuthManager$login$1.onComplete(AuthManager.kt:84)
at com.google.android.gms.tasks.zzj.run(com.google.android.gms:play-services-tasks##17.1.0:4)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at com.google.android.gms.internal.tasks.zzb.dispatchMessage(com.google.android.gms:play-services-tasks##17.1.0:6)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7660)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
Caused by: com.google.firebase.auth.FirebaseAuthInvalidCredentialsException: The password is invalid or the user does not have a password.
at com.google.android.gms.internal.firebase-auth-api.zztt.zza(com.google.firebase:firebase-auth##20.0.2:28)
at com.google.android.gms.internal.firebase-auth-api.zzvb.zza(com.google.firebase:firebase-auth##20.0.2:9)
at com.google.android.gms.internal.firebase-auth-api.zzvc.zzk(com.google.firebase:firebase-auth##20.0.2:1)
at com.google.android.gms.internal.firebase-auth-api.zzuz.zzh(com.google.firebase:firebase-auth##20.0.2:25)
at com.google.android.gms.internal.firebase-auth-api.zztq.zzk(com.google.firebase:firebase-auth##20.0.2:1)
at com.google.android.gms.internal.firebase-auth-api.zzoi.zza(com.google.firebase:firebase-auth##20.0.2:2)
at com.google.android.gms.internal.firebase-auth-api.zzvg.zza(com.google.firebase:firebase-auth##20.0.2:25)
at com.google.android.gms.internal.firebase-auth-api.zzuq.zzf(com.google.firebase:firebase-auth##20.0.2:4)
at com.google.android.gms.internal.firebase-auth-api.zzpy.zzj(com.google.firebase:firebase-auth##20.0.2:5)
at com.google.android.gms.internal.firebase-auth-api.zztu.zzi(com.google.firebase:firebase-auth##20.0.2:8)
at com.google.android.gms.internal.firebase-auth-api.zzsk.zzd(Unknown Source:15)
at com.google.android.gms.internal.firebase-auth-api.zzsj.accept(Unknown Source:6)
at com.google.android.gms.common.api.internal.zach.doExecute(com.google.android.gms:play-services-base##17.3.0:2)
at com.google.android.gms.common.api.internal.zah.zaa(com.google.android.gms:play-services-base##17.3.0:9)
at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zac(com.google.android.gms:play-services-base##17.3.0:192)
at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zab(com.google.android.gms:play-services-base##17.3.0:157)
at com.google.android.gms.common.api.internal.GoogleApiManager$zaa.zaa(com.google.android.gms:play-services-base##17.3.0:125)
at com.google.android.gms.common.api.internal.GoogleApiManager.handleMessage(com.google.android.gms:play-services-base##17.3.0:144)
at android.os.Handler.dispatchMessage(Handler.java:102)
at com.google.android.gms.internal.base.zap.dispatchMessage(com.google.android.gms:play-services-base##17.3.0:8)
at android.os.Looper.loop(Looper.java:223)
at android.os.HandlerThread.run(HandlerThread.java:67)
UPDATE:
the error was thrown because i tried to access the task.result when there was no result, see the try catch block.
To fix this error i passed null as authResult
fun login(context: Context, email: String, password: String) {
FirebaseAuth.getInstance().signInWithEmailAndPassword(email, password)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
loginResult.value = LoginResult(task.result, task.isSuccessful)
startSession(context, LoginType.CREDENTIAL)
saveCredentialsEncrypted(context, email, password)
} else {
loginResult.value = LoginResult(null, task.exception, task.isSuccessful)
}
}
}
Because Firestore's .addSnapshotListener is async. How could I first step to get imgsGroupIds from firestore then second step to send imgsGroupIds into trackImageViewModel.getUserTrackedImgs(imgsGroupIds!!)?
In other words, how to let step 1 run finished then run step 2 after step 1 got imgsGroupIds?
runBlocking{
val imgsGroupIds: MutableList<String>? = mutableListOf()
val deferred = async {
Log.d(TAG, "CoroutineScope(Dispatchers.IO): Thread:${Thread.currentThread().name}")
Firebase.firestore
.collection("userData")
.document(uid!!)
.collection("trackGroupId")
.addSnapshotListener { querySnapshot: QuerySnapshot?, error: FirebaseFirestoreException? ->
Log.d(TAG, "addSnapshotListener: Thread:${Thread.currentThread().name}")
Log.d(TAG, "onViewCreated: FirebaseFirestoreException: $error")
querySnapshot?.forEach {
val imgsGroupId = it.id
Log.d(TAG, "onViewCreated: imgsGroupId = $imgsGroupId")
imgsGroupIds!!.add(imgsGroupId)
}
}
}
deferred.await()
Log.d(
TAG,
"trackImageViewModel.getUserTrackedImgs: Thread:${Thread.currentThread().name}"
)
Log.d(TAG, "onViewCreated: imgsGroupIds = $imgsGroupIds")
if (imgsGroupIds != null) {
trackImageViewModel.getUserTrackedImgs(imgsGroupIds)
.observe(viewLifecycleOwner, Observer {
tracked_imgs_recycler_view.apply {
setHasFixedSize(true)
layoutManager = LinearLayoutManager(requireContext())
val detailRecyclerAdapter =
DetailRecyclerAdapter(requireContext(), it).apply {
notifyDataSetChanged()
}
adapter = detailRecyclerAdapter
}
})
}
}
You can use coroutines Flow. It's in the core module.
This is the "contacts" documents in Firestore:
Firestore collection/documents
Module class Contact:
class Contact(var name: String) {
constructor(): this("John Doe")
}
by using callbackFlow {} pre-design your Firestore read:
fun getContacts() = callbackFlow<Contact> {
val ref = FirebaseFirestore.getInstance().collection("contacts")
val eventListener = ref.addSnapshotListener { value, error ->
if (error != null) {
Log.w(TAG, "getContacts: ", error )
} else {
for (doc in value!!) {
val contact = doc.toObject(Contact::class.java)
this#callbackFlow.sendBlocking(contact)
}
}
}
awaitClose {
eventListener.remove()
}
}
Here is the data that actually read and get:
CoroutineScope(Dispatchers.IO).launch {
getContacts().collect {
Log.d(TAG, "onCreate: ${it.name}")
}
}