I have multiple buttons in my android application and when clicked it has to fetch data from Firebase database and the fetched data is added into a list which has to be passed as an argument to another class constructor . But the fetching of data happens in another thread by default since firebase queries are asynchronous . How do I make sure that list has been added with data from other thread before passing it to constructor ?
It's simple use oncompletelistener you can refer this example:
db=FirebaseFirestore.getInstance();
db.collection("Order").document(TableListFragment.tableno)
.update(
"Items", FieldValue.arrayUnion("ABCD"),
"Quantity", FieldValue.arrayUnion("34")
).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Toast.makeText(getContext(),"Item Added",Toast.LENGTH_LONG).show();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.i("onFailure:",e.toString());
}
});
Related
This question already has answers here:
How to return a DocumentSnapShot as a result of a method?
(2 answers)
Closed 2 years ago.
I'm building an APP and using Firestore to create a collections of Users. Before adding the new user to the collection i need to check within the collection if the email is already in use and for this i've built two methods: one for reading the collection looking for an user with that email and the other one to adding the new user IF everything is ok. But no matter what I do, the add method always executes first leading to the validation being useless. I guess it's has something to do with the methods priority withing Firebase but i really couldn't pull out with a solution
Here's the two methods
The first one it's validation and the second one it's the add
private boolean createFirestoreUser(final String identificador) {
final boolean[] isValid = {true};
FirebaseFirestore.getInstance().collection("Usuarios")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
for (QueryDocumentSnapshot document : task.getResult()) {
if(identificador.equals(document.getString("identificador")))
isValid[0] = false;
}
}
});
return isValid[0];
}
private void createUser(Usuario novoUsuario) {
FirebaseFirestore.getInstance().collection("Usuarios")
.add(novoUsuario)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(final DocumentReference documentReference) {
documentReference
.update("id", documentReference.getId())
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
loginSessionManager.createLoginSession(documentReference.getId());
loginSessionManager.checkLogin(this.getClass());
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
}
});
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
snackbar.showMensagemLonga(v,e.getMessage());
}
});
}
The second one is ALWAYS being called first no matter the order i use on the button listener. I've debbuged and it really enters in the isValid[0] = false after the user is added
Where are you calling the methods?
You could just call the createUser inside of the .addOnSuccessListener this way it will not be called until the valdiation is returned.
Something like:
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
for (QueryDocumentSnapshot document : task.getResult()) {
if(identificador.equals(document.getString("identificador")))
isValid[0] = false;
else
createUser(novoUsuario)
}
}
The reason why I'm asking where you are calling the methods is because your variable might be by default true which would trigger the second function before the async listener is returned therefore calling the second method before the validation is made.
So the alternative would be to call the register method inside the same mehrod where you are validating or if what I'm assuming that you have a boolean declared first to see if you call the create method, just have it false as default and make sure to be calling it after the async .OnCompleteLister is finished.
I am trying to delete a document from Firestore. I am trying to do this based on the task ID that was randomly generated by Firestone. when a particular task is selected on android, I want to be able to delete that task. However, when I tried debugging the code, it shows a random ID that doesn't exist on the database and tries to delete that, sending me a success message in the console. I am not sure where I am going wrong. Please advice.
public void deleteTasks(View v) {
userId = mFirebaseAuth.getCurrentUser().getUid();
String tskid= fStore.collection("usersData").document(userId).collection("tasks").document().getId();
DocumentReference taskref = fStore.collection("usersData").document(userId).collection("tasks").document(tskid);
taskref.delete().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Log.d("tag", "Task Deleted Successfully");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d("tag", "Task Deletion Unsuccessful");
}
});
}
the above delete method is called on the button using android:OnClick
The Task I'd like to delete is Circled
When you are using the following line of code:
String tskid= fStore.collection("usersData").document(userId)
.collection("tasks").document()
.getId();
You are generating a new random ID. Actully, you are reserving a key for a document that you'll be writing in the future. When using this line:
DocumentReference taskref = fStore.collection("usersData").document(userId)
.collection("tasks").document(tskid);
You are creating a reference to that location. However, when using this line:
taskref.delete().addOnCompleteListener(/* ... */);
You are trying to delete a document that does not exist and this is because you didn't create it in the first place. If you need to delete a specific document, you need to know the ID. So the following lines of code will do the trick:
public void deleteTasks(View v) {
userId = mFirebaseAuth.getCurrentUser().getUid();
String tskid = "CQ45RKh8Ohd6DXjSQ8RO";
DocumentReference taskref = fStore.collection("usersData").document(userId)
.collection("tasks").document(tskid);
taskref.delete().addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Log.d("tag", "Task Deleted Successfully");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d("tag", "Task Deletion Unsuccessful");
}
});
}
In order to delete that document, I have used the exact same ID that exists in the database.
I am trying to add the HashMap "attending" to the .collection("Users").document(user_id) and it seems that .add() does not work with this, so I used .set(), however it erases all the previously stored data on the document(user_id).
How could I just add the fields to the document rather then rewriting the whole document in this case?
attending.put(ID,a);
firebaseFirestore.collection("Events").document(ID).collection("Users").add(userMap).addOnCompleteListener(new OnCompleteListener<DocumentReference>() {
#Override
public void onComplete(#NonNull Task<DocumentReference> task) {
//Toast.makeText(eventRegistration.this, "User Stores", Toast.LENGTH_SHORT).show();
}
});
firebaseFirestore.collection("Users").document(user_id).set(attending).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
Toast.makeText(eventRegistration.this, "Registered Successfully", Toast.LENGTH_SHORT).show();
}
});
Using add() on a CollectionReference. always creates a new document with a random ID. Calling set() on a DocumentReference will overwrite the document, but you can change that by passing a second SetOptions type argument that says to merge the new data with the existing data - just call SetOptions.merge() to get that object to pass. update() will always just try to update an existing document.
The fact that you're trying to update with a Map doesn't change the way any of these methods work.
All of these operations are also discussed in the documentation.
I'm trying to retrieve document references from Firestore in a for loop (without iterating through the subset). I need to wait for the loop to finish, wait for data to be received and on success, submit this data to the Firestore. Currently, my method does not wait for data to be received what so ever since it is async.
It would probably be a good idea to create a method that returns a Task and then wait for a result. Suggestions?
ArrayList<String> documentPath = new ArrayList<>();
private void getDocumentRef() {
try {
for (String path : documentPath) {
db.document(path).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful() && task.getResult() != null) {
if (task.getResult().exists()) {
references.add(task.getResult().getReference());
}
}
}
});
}
} catch (Exception e) {
}
}
If I understood your issue correctly, following method should work:
private void getDocumentRef(List<String> documentPaths) {
List<Task<DocumentSnapshot>> tasks = new ArrayList<>();
for (String path : documentPaths) {
tasks.add(db.document(path).get());
}
Task<List<DocumentSnapshot>> finalTask = Tasks.whenAllSuccess(tasks);
finalTask.addOnSuccessListener(new OnSuccessListener<List<DocumentSnapshot>>() {
#Override
public void onSuccess(List<DocumentSnapshot> documentSnapshots) {
/* This list contains all the retrieved document snapshots. Now iterate
through this list to get the document references.*/
for (DocumentSnapshot snapshot : documentSnapshots) {
references.add(snapshot.getReference());
}
/* Here u can do whatever u want with the list named references
because now it has references of all required documents. */
}
});
}
Here, we iterate through the supplied list of paths, create a separate Task for retrieving the document at that path and add this task to a list of Task<DocumentSnapshot>. Then, we supply this list to Tasks.whenAllSuccess() and create a new Task named finalTask. We then attach an OnSuccessListener to finalTask whose onSuccess() method is called when all the supplied tasks get completed. What we get in onSuccess() is a list of DocumentSnapshot of each document. We can now go through this list and get the DocumentReference.
Hope it helps!
For continuing after completing your requests for the selected subset, you would need to keep track of how many queries you're making and how many have completed.
Create a member variable to track the queries and within your OnCompleteListener:
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
mCompleted++; // Update your member variable
if(task.isSuccessful(){
// Do something with your returned data
}else{
// The task failed
}
// Check if the last query has completed
if(mCompleted == numOfQueries){
mCompleted = 0; // Reset the completed queries if you might run this process again
// All of your queries have returned and you can now do something with the complete data set
}
}
I have add data on firestore Firebase but I don't have specific document because I use addSnapShotListener to retrieve the data. How can I delete the document when I don't know the name of it. Here is the code where I add the data:
mondayCollectionReference.document().set(userMap, SetOptions.merge()).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(TimeForm.this, "Submitted", Toast.LENGTH_SHORT).show();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d("ERROR", e.getMessage());
}
});
document() returns a DocumentReference object. That object gives you everything you need to know to delete it, especially its own delete() method. It also has a getId() method to help you remember its id.
So, you should store the DocumentReference object first before calling methods on it:
DocumentReference ref = mondayCollectionReference.document()
String id = ref.getId();
ref.set(...);
// use ref or id later if you want to delete it