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.
Related
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.
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}")
}
}
I want to update the last message, try multiple methods but little bit confused what is the exact code to update the last message. Thanks in advance. Below is the code that I have tried:
databaseReference = FirebaseDatabase.getInstance().reference.child("user").child(apiKey)
databaseReference.orderByChild("api_key").equalTo(loginModel.api_key).addValueEventListener(object : ValueEventListener{
override fun onCancelled(error: DatabaseError) {
}
override fun onDataChange(snapshot: DataSnapshot) {
if(snapshot.exists()){
val map = mutableMapOf<String, String>()
map["last_message"] = message
map["last_message_key"] = apiKey
//databaseReference.child("api_key").child(loginModel.api_key).updateChildren(map.toMap())
//databaseReference.updateChildren(map.toMap())
databaseReference.child(snapshot.key!!).setValue(chatUser)
Utils.showToast(requireContext(), "Exists")
}else{
Utils.showToast(requireContext(), "Not Exists")
userReference.setValue(chatUser)
}
}
})
It would be useful if you posted the code and also explained the outcome of your code for us to understand better.
Example from the docs: mDatabase.child("users").child(userId).child("username").setValue(name); You can call .child all the way to your preferred child and do a setValue().
From your code, the issue might be with the snapshot.key!! value which seems to be pointing back to your apiKey value.
What you need is mDatabase.child("user").child("2c3e...").child("-MXH..").updateChildren(). setValue() will overwrite everything in that node. You should debug to see what values you are seeing in each step.
To be able to update properties in Firebase Realtime Database when using a Query, then you should call getRef() directly on the DataSnapshot object, as explained in the following lines of code:
val rootRef = FirebaseDatabase.getInstance().reference
val apiKeyRef = rootRef.child("user").child("apiKey")
val query = apiKeyRef.orderByChild("api_key").equalTo(loginModel.api_key)
val valueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
if(dataSnapshot.exists()) {
for (ds in dataSnapshot.children) {
val map = mutableMapOf<String, String>()
map["last_message"] = message
map["last_message_key"] = apiKey
ds.getRef().updateChildren(map)
}
}
}
override fun onCancelled(databaseError: DatabaseError) {
Log.d("TAG", databaseError.getMessage()) //Don't ignore potential errors!
}
}
query.addListenerForSingleValueEvent(valueEventListener)
I also encourage you to attach a complete listener to all update operations, to always know if something goes wrong.
Instead of use set() method i think you should use update() method. In that method you pass the object and firebase detect the properties that has been updated. I'm using it for my project and works fine.
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
I have an android application which uses Firebase Authentication via Facebook. A user can delete their account in the application using the following function:
override fun deleteUserAcc() {
val user = FirebaseAuth.getInstance().currentUser
val userToken = FacebookAuthProvider.getCredential(authTokenProvider.provideToken())
user?.reauthenticate(userToken)?.addOnCompleteListener { task ->
user.delete()
}
}
After this a user really gets deleted on the Firebase servers. However when they try to access the application again and log in one more time, they are not able to do this (their account with uid had been deleted and somehow they are not assigned a new one uid).
The login function and the onSuccess callback are both implemented and called.
override fun login(): Completable {
LoginManager.getInstance().logInWithReadPermissions(
activityReference.get(),
listOf("public_profile", "user_birthday", "user_location")
)
return CompletableSubject.create().apply {
loginSubject = this
}
}
override fun onSuccess(result: LoginResult) {
val credential = FacebookAuthProvider.getCredential(result.accessToken.token)
authTokenProvider.saveToken(result.accessToken.token)
firebaseAuth.signInWithCredential(credential)
.addOnCompleteListener { task ->
if (task.isSuccessful) {
getInfoOnFirestore(loginSubject)
} else {
loginSubject.onError(task.exception!!)
}
}
}
What can possibly be the cause of the following issue?
A little late to the party but I know what the issue is. Firebase deletes only the authentication, which means that the real-time database is still there with the same uid. In order to delete the database entry as well, you need to upgrade to the blaze program and add the extension.