Database reference valueEventListener not called - android

I have an application where i register the user using phone number authentication , everything works fine but as the user is done verifying the phone number , i would like to push some credentials of the user to
realtime datbase , but the issue is that i'm not able to do so ... stuck basically , if any one could i'll appreciate it
This is my code
private fun signInWithAuthPhoneCredentials(credentials: PhoneAuthCredential) {
FirebaseAuth.getInstance().signInWithCredential(credentials)
.addOnCompleteListener {
if(it.isSuccessful){
// so basically if authentication is successful , i want to create a path to save user
// credentials which dosn't exist in the first place , upon checking if snapshot dosn't
// exist , i want to get user details and push them into database
// even with debugging , it gets to the last line of ( valueEventlister) and it stops
// there , it dson't even get to check wether snapshot exists or not
val currentUser = FirebaseAuth.getInstance().currentUser
if(currentUser != null){
val databaseReference = FirebaseDatabase.getInstance().reference.child("users").child(currentUser.uid)
databaseReference.addListenerForSingleValueEvent(object : ValueEventListener{
override fun onDataChange(snapshot: DataSnapshot) {
if(!snapshot.exists()){
val contactInfoMap = hashMapOf<String,Any>()
contactInfoMap["name"] = currentUser.displayName!!
contactInfoMap["phone"] = currentUser.phoneNumber!!
databaseReference.updateChildren(contactInfoMap)
}
userIsLoggedIn()
}
override fun onCancelled(error: DatabaseError) {
}
})
}
}
}
.addOnFailureListener {
Log.d("ERROR TAG","Error ${it.message}")
}
}

Related

problem with kotlin update real database permanent loop

First of all. Thanks to everybody this place is awesome and full of people willing to help ;)
My question: I've created a function using Realtime Database to update the same time three values from three different children in the same table. And it works perfectly well if I update just one of them.
To launch the function the user can update from none to all three values together but the problem is that when the user modify more than one of the value firebase keep in a death loop updating the values continuously
My DB
My function is here:
private fun guardarTokens () {
referenciaBD2 = FirebaseDatabase.getInstance().getReference("TipoUsuario")
val tipoUsuarioDatos = HashMap<String, Any>()
referenciaBD2.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
for (snapshot in snapshot.children) {
val tipoUsuarioInfo = snapshot.getValue(TipoUsuario::class.java)
if (tipoUsuarioInfo!!.descripcionUsuario == "usuario")
tipoUsuarioDatos["tuid"] = binding.etTokenAlumno.text.toString()
if (tipoUsuarioInfo!!.descripcionUsuario == "profesor")
tipoUsuarioDatos["tuid"] = binding.etTokenProfesor.text.toString()
if (tipoUsuarioInfo!!.descripcionUsuario == "administrador")
tipoUsuarioDatos["tuid"] = binding.etTokenAdmin.text.toString()
snapshot.ref.updateChildren(tipoUsuarioDatos)
}
}
}
override fun onCancelled(error: DatabaseError) {
}
})
}
That's not how you should update a specific element in the Realtime Database. What you're actually doing, you're downloading the entire TipoUsuario node on the client in order to perform a verification. That is considered a waste of resources and bandwidth. What you should do instead, is to perform a query and get only the data you are interested in:
referenciaBD2 = FirebaseDatabase.getInstance().getReference("TipoUsuario")
val queryByUsuario = referenciaBD2.orderByChild("descripcionUsuario").equalTo("usuario")
val tipoUsuarioDatos = HashMap<String, Any>()
queryByUsuario.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
if (snapshot.exists()) {
for (snapshot in snapshot.children) {
val tipoUsuarioInfo = snapshot.getValue(TipoUsuario::class.java)
tipoUsuarioDatos["tuid"] = binding.etTokenAlumno.text.toString()
snapshot.ref.updateChildren(tipoUsuarioDatos)
}
}
}
override fun onCancelled(error: DatabaseError) {
Log.d("TAG", error.getMessage()) //Never ignore potential errors!
}
})
In this way, the query will only return the children where the descripcionUsuario field holds the value of usuario.

The Firebase value keeps regenerating even after i delete them

I've been struggling with this problem for more than a week help me out if you know the fix.
enter image description here
I am trying to delete the data from a child node in the Realtime Firebase but it keeps regenerating even when I delete the token data.
This is my code:
when a user logs in an FCM token is generated automatically (onCreate).
when I try to log him out of his account I want the token to be deleted from the token list but it keeps regenerating even when I logout
this is the User fragment is redirected to after login:
val currentUser : String = firebaseAuth.currentUser?.uid.toString()
val databaseReference = FirebaseDatabase.getInstance("https://trial-38785-default-rtdb.firebaseio.com/")
.getReference("AppUsers").child("Doctor").child(currentUser)
FirebaseMessaging.getInstance().token.addOnCompleteListener {
if (it.isComplete) {
val firebaseToken = it.result.toString()
val myRef =
FirebaseDatabase.getInstance("https://trial-38785-default-rtdb.firebaseio.com/")
.getReference("AppUsers").child("Doctor").child(currentUser)
myRef.child("Token").orderByChild("token").equalTo(firebaseToken)
.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val token : Token = Token(firebaseToken)
if (snapshot.exists()) {
return
} else {
databaseReference.child("Token")
.child(currentdate.toString()).setValue(token)
}
}
override fun onCancelled(error: DatabaseError) {
Toast.makeText(context, error.message, Toast.LENGTH_LONG).show()
}
})
}
}.addOnFailureListener {
Toast.makeText(context, it.message, Toast.LENGTH_LONG).show()
}
This is another fragment where the doctor logout.
pressing logout button this is the code i ran
val delRef = FirebaseDatabase.getInstance().getReference("AppUsers")
.child("Doctor").child(currentId.toString())
.child("Token").child(key.toString()).removeValue()
delRef.addOnSuccessListener {
println("Removed.")
}.addOnFailureListener {
println("Not Removed.")
}
When using the Query#addValueEventListener() method, it means that you are trying to listen for real-time updates. That being said, every change that takes place in the database and corresponds to your query, always triggers your onDataChange() method. Since when you are logging out, you remove a value from the query you are listening to, your token gets written again, hence that behavior. This is happening over and over again.
To solve this, simply change the above method call to Query#addListenerForSingleValueEvent(). This means that it listen for changes only once.
I finally figured out what I was doing wrong, I just had to move my code (myRef) outside of FirebaseMessaging.

How can i retrieve values of Firebase Nodes with random auto generated IDs in Android Using Kotlin

I don't know how to go about retrieving data from the Firebase Realtime Database where the values have randomly generated IDs as seen below.
To be able to get all the keys and values that exist under the 21-6-21 node, you need to loop through the DatasnaShot object, as in the following lines of code:
val rootRef = FirebaseDatabase.getInstance().reference
val dateRef = rootRef.child("SigninData").child("CSC101").child("21-6-21")
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val key = ds.getkey()
val value = ds.getValue(String::class.java)
Log.d("TAG", "$key/$value")
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("TAG", databaseError.getMessage()) //Don't ignore potential errors!
}
}
dateRef.addListenerForSingleValueEvent(valueEventListener)
The result in the logcat will be:
-Mf8...ESM/oma#gmail.com...
-Mf8...7nb/oma#gmail.com...
-Mf8...XJv/oma#gmail.com...
Edit:
private fun getDataFrom(date: String) {
val rootRef = FirebaseDatabase.getInstance().reference
val dateRef = rootRef.child("SigninData").child("CSC101").child(date)
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
for (ds in dataSnapshot.children) {
val key = ds.getkey()
val value = ds.getValue(String::class.java)
Log.d("TAG", "$key/$value")
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("TAG", databaseError.getMessage()) //Don't ignore potential errors!
}
}
dateRef.addListenerForSingleValueEvent(valueEventListener)
}
Now you can call the above method either with:
getDataFrom("21-6-21")
Or:
getDataFrom("22-6-21")
If you need to get them both, then you should use a get() call, and pass both Task objects to the whenAllSuccess(Task...<?> tasks).
I believe this will work out for you.
fun getDataFromFirebaseDatabase() {
// Your path
database.getReference("SigninData/CSC101/21-6-21")
.get()
.addOnCompleteListener {
//this checks if there were any exceptions or Errors generated and will Log the error in your Logcat.
if (it.exception != null) Log.e("FirebaseDatabase", "Error", it.exception)
// this checks if your result was successful or not and also,
//if it has children or not, if both conditions are satisfied,
//it will enter the for loop and print all the
// values along with their keys in logcat.
if (it.isSuccessful && it.result.hasChildren()) {
for (i in it.result.children) {
//i.key will give you required key
Log.d("FirebaseDatabase", "key is ${i.key}")
// i.value will give respective value
// import the values as given in database, for your case it would be String so.
val data = i.value as String
Log.d("FirebaseDatabase", "value is $data")
}
}
}
}
Note - This will read all the values inside 21-6-21 child
Your Logcat should look something like this.
//if there is an error
E/FirebaseDatabase: Error <Some Error Code Here>
//if Run is Successful
//since there are 3 entries in CSC101 node all three will be printed one by one
//like the example given below
D/FirebaseDatabase: key is Mf80wSd9neOAqlPhESM
D/FirebaseDatabase: value is oma#gmail.com You signed in for CSC101
The above code uses get() function which means your app will only read the database only once.
Instead of get() you can also use:
addChildEventListener()
addValueEventListener()
addListenerForSingleValueEvent()
All are for different purposes.
Here are the Firebase Docs regarding these: https://firebase.google.com/docs/database/android/read-and-write#read_data
the 3rd one is similar to the get function but 3rd one will read from the cache memory and should be used when you are not expecting your data to change frequently.
1st and 2nd will get the results from the database as soon as there are changes made in the database nodes.

Is there a code to know if a firebase-database data has loaded?

I am been developing a kotlin app with firebase as database and I am trying to have a loading screen to pass to another activity, and I am trying to keep everything organized so I am trying to have a code that when the value above is loaded pass to another activity
var myRef = database?.
getReference(tags?.USERS_DATABASE_VALUE.toString()).
child(mAuth?.currentUser!!.uid)
myRef?.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
snapshot?.children?.forEach {
val map = it.value as HashMap<String, Any>?
if (map!!.containsKey(tags?.COURSE_TAG_NAME)) {
if (map[tags?.COURSE_TAG_NAME].toString().equals(sharedPreferences?.getString(tags?.CURRENT_COURSE_ACTIVITY, null).toString())) {
var courseStats = map[tags?.COURSE_ADVANCEMENT]
// if (loaded)
// {
// GoToAnotherActivity
// }
}
}
}
}
override fun onCancelled(p0: DatabaseError) {
Toast.makeText(applicationContext, p0.message, Toast.LENGTH_SHORT).show()
}
})
When you call something from firebase database it has its own 'loading time' as Frank van Puffelen said in a comment below my question

Firebase Database EventListener outside of an activity

I am new to Android and Kotlin and I want to implement a FirebaseRetriever class in my project that returns a specific database snapshot.
Now I am having issues that the EventListener is never triggered and so, of course, I get a null object back.
Here is my data class:
public class FirebaseRetriever() {
private val TAG = "FirebaseRetriever"
private lateinit var fbData: FirebaseDatabase
private lateinit var fbAuth: FirebaseAuth
private lateinit var userRef: DatabaseReference
//Snapshots
private lateinit var userSnap: DataSnapshot
init {
fbData = FirebaseDatabase.getInstance()
userRef = fbData.reference.child("users")
fbAuth = FirebaseAuth.getInstance()
userRef.addValueEventListener(object : ValueEventListener {
override fun onCancelled(e: DatabaseError) {
Log.e(TAG, "Data could not be retrieved from Database." + e.message)
}
override fun onDataChange(snap: DataSnapshot) { // is never executed
userSnap = snap
}
})
}
public fun getUserSnap(): DataSnapshot {
return userSnap // returns null
}
}
This is how I call the getUser():
firebaseRetriever = FirebaseRetriever()
...
val uniSnap = firebaseRetriever.getUniSnap()
Is it possible that a Firebase EventListener can only work in a regular AndroidActivity or am I doing something else wrong?
Thanks in advance.
Most likely the data simply hasn't been loaded yet when you call getUserSnap. To verify this, add a few more log statements:
Log.i(TAG, "Starting to read from database.")
userRef.addValueEventListener(object : ValueEventListener {
override fun onCancelled(e: DatabaseError) {
Log.e(TAG, "Data could not be retrieved from Database." + e.message)
}
override fun onDataChange(snap: DataSnapshot) { // is never executed
Log.i(TAG, "User read from database.")
userSnap = snap
}
})
public fun getUserSnap(): DataSnapshot {
Log.i(TAG, "Getting user.")
return userSnap // returns null
}
When you run your app with this the output will likely be:
Starting to read from database.
Getting user.
User read from database.
That is probably not the order you expected. Firebase loads data from the database asynchronously, since it may take some time for the results to be available. Instead of making your app wait (which would trigger an Application Not Responding dialog), your code is allowed to continue. Bu the time you call getUserSnap, that user hasn't been loaded yet.
The simplest solution is to move all the code that needs access to the user into the onDataChange() method. Also see my answer to this question for a more flexible solution, that requires more code: getContactsFromFirebase() method return an empty list

Categories

Resources