I would like to load RecyclerView from my firestone db. My firestore structure is like this:
users(collection) -> user_id(document) -> books(collection)
books(collection) -> book_id(document)
Added: Database structure images
enter image description here
enter image description here
enter image description here
What I want to do is getting the book_ids of current_user into an ArrayList<String> then with this arraylist and for each loop, I want to load books into another ArrayList<Book>. I am getting the “ArrayList book_ids” of the current user. (I want to show the books which were added by current user)
The problem is with this array list I can not manage to get ArrayList<Book>books. I will send this array list to recycler view adapter.
public void getBookIdsFromDb(){
final ArrayList<String> myBookIds = new ArrayList<String>();
CollectionReference bookIdRef = db.collection(getString(R.string.collection_users)).document(userId)
.collection(getString(R.string.colleciton_books));
bookIdRef.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull com.google.android.gms.tasks.Task<QuerySnapshot> task) {
if (task.isSuccessful()){
for (DocumentSnapshot ds : task.getResult()){
myBookIds.add(ds.getString("book_id"));
myBookIds.size());
}
Log.d(TAG, "onComplete: myBookIds.size" + myBookIds.size());
if (myBookIds.size()>0){
getMyBooksFromDb(myBookIds);
//this works
}
}
}
});
}
public void getMyBooksFromDb(ArrayList<String> bookIds){
final ArrayList<String> myBooksIds =bookIds;
final ArrayList<Book> myBooks = new ArrayList<Book>();
CollectionReference dbRef = db.collection(getString(R.string.colleciton_books));
for (int i =0; i<myBooksIds.size();i++){
Log.d(TAG, "getMyBooksFromDb: myBookIds in for " + myBooksIds.size());
dbRef.document(myBooksIds.get(i)).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot != null){
Book myBook = documentSnapshot.toObject(Book.class);
myBooks.add(myBook);
//Can not get out of this array from for loop
}
}
});
}
}
//I tried to make a nested query below but no result :(
public void getBookListFromDb(){
final ArrayList<String> myBookIds = new ArrayList<String>();
CollectionReference bookIdRef = db.collection(getString(R.string.collection_users)).document(userId)
.collection(getString(R.string.colleciton_books));
bookIdRef.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull com.google.android.gms.tasks.Task<QuerySnapshot> task) {
final ArrayList<Book> myBooks = new ArrayList<Book>();
if (task.isSuccessful()){
for (DocumentSnapshot ds : task.getResult()){
myBookIds.add(ds.getString("book_id"));
myBookIds.size();
}
Log.d(TAG, "onComplete: myBookIds.size" + myBookIds.size());
if (myBookIds.size()>0){
CollectionReference colRef = db.collection(getString(R.string.colleciton_books));
for (int i=0; i< myBookIds.size(); i++){
colRef.document(myBookIds.get(i)).get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull com.google.android.gms.tasks.Task<DocumentSnapshot> task) {
if (task.isSuccessful()){
DocumentSnapshot ds = task.getResult();
Book myBook = ds.toObject(Book.class);
myBooks.add(myBook);
}
Log.d(TAG, "onComplete: myBooks.size(i) "+ myBooks.size());
}
});
Log.d(TAG, "onComplete: myBooks.size() in for "+ myBooks.size());
}
Log.d(TAG, "onComplete: myBooks.size() "+ myBooks.size());
}
}
}
});
}'
You cannot achieve what you want in this way because both methods, onComplete() and onSuccess() have an asynchronous behaviour. This means that by the time you are calling getMyBooksFromDb() method, you haven't finished getting the data from the database. So to solve this, you need to move all your logic from your getMyBooksFromDb() method inside onComplete() method. This is the flow:
add a complete listener on your bookIdRef
get all the book_id you have in your database by iteration using the foor loop
inside onComplete() method use the book_id to create your CollectionReference for finding the books.
Move the declaration of your ArrayList<Book> inside onSuccess() method.
Do what you want to do with your list or with your Book objects.
So the solution is to use nested queries as explained above. So please use the following code:
CollectionReference bookIdRef = db.collection(getString(R.string.collection_users))
.document(userId)
.collection(getString(R.string.colleciton_books));
bookIdRef.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull com.google.android.gms.tasks.Task<QuerySnapshot> task) {
if (task.isSuccessful()){
for (DocumentSnapshot ds : task.getResult()) {
String bookId = myBookIds.add(ds.getString("book_id"));
dbRef.document(bookId).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot != null){
Book myBook = documentSnapshot.toObject(Book.class);
Log.d("TAG", myBoog.getBookName());
}
}
});
}
}
}
});
Step -1:
Create a Model Class of Userbook.java and Book.java
Userbook.Java
String book_id;
Book book;
//create getter and setter for that.
As per you code set value:
UserBook userbook = new UserBook();
final ArrayList<UserBook> books = new ArrayList<String>();
public void getBookIdsFromDb(){
CollectionReference bookIdRef = db.collection(getString(R.string.collection_users)).document(userId)
.collection(getString(R.string.colleciton_books));
bookIdRef.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull com.google.android.gms.tasks.Task<QuerySnapshot> task) {
if (task.isSuccessful()){
for (DocumentSnapshot ds : task.getResult()){
userbook.setBook_id(ds.getString("book_id"));
}
Log.d(TAG, "onComplete: myBookIds.size" + myBookIds.size());
if (myBookIds.size()>0){
getMyBooksFromDb(myBookIds);
//this works
}
}
}
});
}
public void getMyBooksFromDb(ArrayList<String> bookIds){
CollectionReference dbRef = db.collection(getString(R.string.colleciton_books));
for (int i =0; i<myBooksIds.size();i++){
Log.d(TAG, "getMyBooksFromDb: myBookIds in for " + myBooksIds.size());
dbRef.document(myBooksIds.get(i)).get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
if (documentSnapshot != null){
Book myBook = documentSnapshot.toObject(Book.class);
userbook.setBook_id(myBook);
//Can not get out of this array from for loop
}
}
});
}
}
//after all this add obj to list
books.add(userbook);
I hope you will get the result. or else ping here.
Related
I have this problem sometimes, when after I request Firebase Firestore Documents, it RETRIEVES ALL THE DATA, shows it correctly in the RecyclerView, but then the Data just disappears after one second.
adapter.getItemCount(); shows, there are Items. I just can´t figure it out...
Here is the retrieval code for the first Cardview:
void getData(){
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
final FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference doc =db.collection("data").document("codes");
doc.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
userWorkList.clear();
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
final ArrayList codes = (ArrayList) document.get("code");
System.out.println("Codes: "+codes);
for(int i=0;i<codes.size();i++) {
DocumentReference doc =db.collection("codes").document(codes.get(i).toString());
final int finalI = i;
doc.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
if(document.get("code")!=null) {
HashMap codeMap = (HashMap) document.get("code");
if (codeMap.toString().toLowerCase().contains(user.getDisplayName().toLowerCase())){
userWorkList.add(new User_Work_View_Row(codes.get(finalI).toString(), user.getEmail(), "", finalI));
System.out.println(codes.get(finalI) + " FOUND");
//WorkAdapter.notifyDataSetChanged();
}
if(finalI==codes.size()-1){
Handler handler = new Handler();
Runnable runnable = new Runnable() {
#Override
public void run() {
System.out.println("Execute Handler");
if(WorkAdapter.getItemCount()==0&&userWorkList.size()==0){
System.out.println("NO ITEMS");
getData();
//handler.postDelayed(this, 2500);
}
}
};
handler.postDelayed(runnable, 1000);
swipeLayout.setRefreshing(false);
WorkAdapter.notifyDataSetChanged();
}
}
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
}
//WorkAdapter.notifyDataSetChanged();
//userWorkProgress.setVisibility(View.GONE);
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
}
});
}
The adapter Init:
RecyclerView list = root.findViewById(R.id.currentUserWork);
LinearLayoutManager LayoutManager = new LinearLayoutManager(getActivity());
LayoutManager.setReverseLayout(false);
LayoutManager.setStackFromEnd(false);
list.setLayoutManager(LayoutManager);
WorkAdapter.setHasStableIds(true);
WorkAdapter.setGlobalContext(getContext());
list.setAdapter(WorkAdapter);
The Adapter Code really isn´t important because it only shows the Title of the next RecyclerView.
I would love to share a video of it happening, but exactly when I want it to disappear, it shows correctly.
Has someone experienced this sort of thing before or is it a bug?
Oh ok, I THINK I figured it out, I had 0dp for my RecyclerView instead of wrap_content.
But I need to run it a few times before I am certain..
possible trouble in code:
you makes loop
for (int i=0;i<codes.size();i++) {
and in the loop you creates many OnCompleteListeners
the problem here is, that they could be finished in different order.
so your finalI could come before all results are done
UPDATE
To solve this problem
create global variable int requestsLeft
after if (document.exists()) { set requestsLeft = codes.size()
after public void onComplete(#NonNull Task<DocumentSnapshot> task) { make requestsLeft--
instead of if(finalI==codes.size()-1){ you can now check if (requestsLeft == 0) {
so you are sure, that all requests are done
i have some problem understanding whats wrong with android code about calling function
as show below, I have 2 private void
cekSaved(place.getName());
addUserInfo(place.getName(),"");
i expect the android run ceksaved method first then addUserInfo but android running adduser first then run ceksaved function
i need help understanding this
code is :
private void cekSaved(String param1){
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference newUserInfo = db.collection("trip").document();
String userID = FirebaseAuth.getInstance().getCurrentUser().getUid();
db.collection("trip")
.whereEqualTo("user_id", userID )
.whereEqualTo("city", param1)
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful())
{
for (QueryDocumentSnapshot document : task.getResult()) {
status = "ada";
Log.i(TAG, "onComplete: "+status);
}
}
else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
}
private void addUserInfo(String city, String tittle){
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference newUserInfo = db.collection("trip").document();
place_id = newUserInfo.getId();
Log.i(TAG, "addUserInfo: "+status);
if(!status.equals("ada")) {
String userID = FirebaseAuth.getInstance().getCurrentUser().getUid();
userTrip usertrip = new userTrip();
usertrip.setCity(city);
usertrip.setTittle("My Trip To " + city);
usertrip.setTrip_id(newUserInfo.getId());
usertrip.setUser_id(userID);
Log.i(TAG, "addUserInfo: trip baru telah di buat");
newUserInfo.set(usertrip).addOnCompleteListener(new OnCompleteListener<Void>() {
#Override
public void onComplete(#NonNull Task<Void> task) {
if (task.isSuccessful()) {
ToastMessage("OK");
} else {
ToastMessage("fail to register");
}
}
});
}else{ToastMessage("sudah ada");}
}
You're getting values from Firebase, it's an async call, so you don't really know which one will be completed first. You can see that either calls have an onCompleteListener, if you want to run addUserInfo after cekSaved you have to call addUserInfo inside onCompleteListener of cekSaved method.
I want to retrieve images frm storage to recyclerview but it is not working
mFirestore.collection("Users").whereEqualTo("special", s).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
final SpecializedDoctorNameFragment S = new SpecializedDoctorNameFragment();
final List<DoctorNameStructure> eventList = new ArrayList<>();
for (final DocumentSnapshot doc : task.getResult()) {
String id = doc.getString("id");
final DoctorNameStructure e = new DoctorNameStructure();
e.setName(doc.getString("name"));
e.setAddress(doc.getString("address"));
e.setConsultancy(doc.getString("consultancyFees"));
e.setSpecialization(doc.getString("special"));
storageReference.child("images/" + id).getDownloadUrl().addOnSuccessListener(new OnSuccessListener<Uri>() {
#Override
public void onSuccess(Uri uri) {
uploadImages();
e.setImage(uri.toString());
eventList.add(e);
}
});
}
NameAdapter recyclerViewAdapter = new NameAdapter(getContext(), eventList, mFirestore);
recyclerName.setAdapter(recyclerViewAdapter);
}
}
});
Listener is executing after your setAdapter. Change the logic of filling the list.
i created two methods to get data from firestore. The first method is iterating over a list of attributes which are selected by the user. The other method is querying data depending on the selections of the user. After iterating over each item the new method needs to start. The problem is that my list is always empty, even though there is data available. Here are both methods:
private void getStores(ArrayList<String> checkedItems) {
newSeller = new ArrayList<>();
sellers = new ArrayList<>();
a = 0;
for (int i =0; i<checkedItems.size(); i++){
String checkedItem = checkedItems.get(i);
int finalA = i;
getStore(checkedItem);
}
}
private void getStore(String checkedItem) {
db.collection("Seller")
.whereEqualTo("typ", checkedItem)
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
SellerObject so = document.toObject(SellerObject.class);
so.setId(document.getId());
sellers.add(so);
Log.i(TAG, "onComplete: toa"+sellers.size());
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});
}
You are declaring the "sellers" array inside the first function but then trying to add to it from the second function. Move sellers out of the function so it can be reached.
I'd like update document in cloud firestore according to value in document. Something like this. I have few documents with random names and values and one document with values id = 1 , name = patryk . And now I'd to update document with name=patryk. And I don't know document name because I make them like this and they have a random name.
b.collection("Users")
.add(postMapa)
.addOnCompleteListener
How to do this? here I need have the name of the document but I don't have.
Try this and make sure there will be only single document for name = patryk
db.collection("Users").whereEqualTo("name", "patryk").addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot queryDocumentSnapshots, #Nullable FirebaseFirestoreException e) {
if (e == null) {
String documentId = queryDocumentSnapshots.getDocuments().get(0).getId();
// here you have id but make sure you have only one document for name=patryk
}
}
});
Assuming that the id property is of type number and the name property is of type String, please use the following code in order to update all users with the id = 1 and name = patryk:
FirebaseFirestore rootRef = FirebaseFirestore.getInstance();
Query query = rootRef.collection("Users").whereEqualTo("id", 1).whereEqualTo("name", "patryk");
query.get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
List<String> list = new ArrayList<>();
for (DocumentSnapshot document : task.getResult()) {
list.add(document.getId());
}
for (String id : list) {
rootRef.collection("Users").document(id).update("name", "New Name").addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "Name Updated!");
}
});
}
}
}
});
If you are using a model class for your user, please see my answer from this post.
you can retrieve documents by their name (which does not really apply here):
DocumentReference docRef = db.collection("Users").document("patryk");
or you can query for values contained in a document (which might answer your question):
/* create a reference to the Users collection */
CollectionReference users = db.collection("Users");
/* create a query against the collection */
Query query = users.whereEqualTo("name", "patryk");
the rest is the same procedure, in both cases:
/* asynchronously retrieve the document */
ApiFuture<DocumentSnapshot> future = docRef.get();
/* future.get() blocks on response */
DocumentSnapshot document = future.get();
if (document.exists()) {
System.out.println("Document data: " + document.getData());
} else {
System.out.println("No such document!");
}
Simple way to get all document id under the collections:
firebase.collection("Users").get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
List<String> list = new ArrayList<>();
for (QueryDocumentSnapshot document : task.getResult()) {
list.add(document.getId());
}
Log.d(TAG, list.toString());
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
});