TextView not able to setText with data from Firestore - java

I'm fetching data from Firestore. I want to set the string data to a TextView. I'm able to get the data successfully . i.e I'm able to log it in the logcat. But when I try to set the text,it shows null in place of the data
Here is my code :
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
yourSector=view.findViewById(R.id.Sector_tv);
yourPincode=view.findViewById(R.id.Pincode_tv);
DocumentReference docRef = db.collection("customerUsers").document(userID);
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
pincode = document.getString("pincode");
sector = document.getString("sector");
Log.d("pincodetest", "onComplete: "+pincode);
} else {
Log.d("docref", "No such document");
}
} else {
Log.d("docref", "get failed with ", task.getException());
}
}
});
String sectorText="Sector : " + sector;
String pincodeText="Pincode : "+pincode;
yourSector.setText(sectorText);
yourPincode.setText(pincodeText);
My logcat (shows the correct data):
2020-06-14 00:41:43.779 14633-14633/? D/pincodetest: onComplete: 110001
When I set the text, on my screen I get:
Sector : null
PS: Strings pincode,sector have already been declared outside onViewCreated

The OnCompleteListener completes asynchronously so you need to place your setTexts within the onComplete method of it. In other words, the sector and pincode local variables are not populated with data when they are accessed for the concatenation to form the sectorText and pincodeText Strings.
In simpler terms, the onComplete method runs after the string concatenation. Therefore, during the string concatenation the value of the variables sector and pincode are still null.
I've done a little fix in the code below:
if (document.exists()) {
pincode = document.getString("pincode");
sector = document.getString("sector");
Log.d("pincodetest", "onComplete: "+pincode);
String sectorText="Sector : " + sector;
String pincodeText="Pincode : "+pincode;
yourSector.setText(sectorText);
yourPincode.setText(pincodeText);
}

Related

How to Fetch Collections based on another collections field value inside the loop in Firestore

In our app we are trying to fetch all the products and by iterating each product trying to get specificationId which is equal to another documentId in seperate collection Specifications.
Basically the structure is like below:
ProductDetail
productId
productTitle
productDescription
specificationId
Specification
documentId — this is autogenerated id for collection which is being used as specificationId in ProductDetail collection.
MainActivity.java
Query productResponseQuery = FirebaseFirestore.getInstance()
.collection("productdetails")
.limit(50);
productResponseQuery.addSnapshotListener(new EventListener<QuerySnapshot>() {
#RequiresApi(api = Build.VERSION_CODES.N)
#Override
public void onEvent(#Nullable QuerySnapshot querySnapshot, #Nullable FirebaseFirestoreException error) {
if (error != null) {
Log.d(TAG, "Error getting documents: " + error.getMessage());
return;
}
querySnapshot.forEach(doc -> {
ProductDetailResponse productDetailResponse = new ProductDetailResponse();
productDetailResponse.setProductImage(doc.getData().get("productImageUrl").toString());
productDetailResponse.setProductTitle(doc.getData().get("productTitle").toString());
productDetailResponse.setProductShortDesc(doc.getData().get("productShortDesc").toString());
productDetailResponse.setProductDesc(doc.getData().get("productDescription").toString());
populateSpecification(doc.getData().get("specId").toString());
});
}
});
private void populateSpecification(String specId){
DocumentReference specDocumentRef = FirebaseFirestore.getInstance()
.collection("specifications")
.document(specId);
specDocumentRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if(task.getResult().exists()){
task.getResult();
}else{
task.getException();
}
}
});
}
I understand that the Firestore database calls are asynchronous in nature. Hence if I call the specificationQuery inside the for loop, without waiting for the result it is jumping to the next iteration.
Is there any way without changing the structure I can achieve the result, which will basically show all the product details along with their respective specification collection?

How to retrieve a value from a sub-field of a map field in a document in Firestore DB?

I am planning to use the value of a sub-field of a map field in my document for other purposes but it seems that I cannot retrieve it. I have found an answer here on this website but the solution code to get the value is too much for me. I can use the solution code to get the value but if there is the simplest way to get it, kindly drop the answer here.
This is the screenshot of Firestore DB where I need to get is the Boolean value of deleted inside a map field with the UID as field name:
To get the value of the "deleted" fields that exists inside that Map object, please use the following lines of code:
FirebaseFirestore db = FirebaseFirestore.getInstance();
CollectionReference candidatesRef = db.collection("Candidates");
DocumentReference emailRef = candidatesRef.document("ravalera...#umak.edu.ph");
// ^ add entire address ^
emailRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Map<String, Boolean> map = (Map<String, Boolean>) document.get("PADc...ayU2");
// ^ add entire ID
boolean deleted = map.get("deleted");
Log.d(TAG, "deleted: " + deleted);
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
The result in the logcat will be:
deleted: true

Retrieving firebase data in one go

I am trying to retrive a collection named jobs in which each job contains 3 different fields. I then want to display each job with all 3 field on an array list. My app runs but it returns one item which says: java.lang.field#2234456
This is my code:
ListView jobList;
ArrayList<String> jobInfo = new ArrayList<String>();
ArrayAdapter arrayAdapter;
Task hello;
String hi;
FieldPath ha;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_see_jobs2);
jobList = (ListView) findViewById(R.id.jobList);
setTitle("Hello");
arrayAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, jobInfo);
jobList.setAdapter(arrayAdapter);
jobList.setAdapter(arrayAdapter);
hello = FirebaseFirestore.getInstance().collectionGroup("jobs").get().addOnCompleteListener(new
OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
hi = QuerySnapshot.class.getFields().toString();
hi.toString();
}
});
jobInfo.add(QuerySnapshot.class.getDeclaredFields().toString());
}
This is not the way to get data from a Firestore document:
QuerySnapshot.class.getFields()
Instead this looks up the fields that are declared on the QuerySnapshot class, which is not at all what you're interested in.
If we check the Firebase documentation on getting data from multiple documents, you'll see that we can get the data with:
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d(TAG, document.getId() + " => " + document.getData());
}
} else {
Log.d(TAG, "Error getting documents: ", task.getException());
}
}
So this:
Loops over the documents that you get back from the database.
Then gets the data from each document with document.getData(), which returns a Map.
If you look at the reference documentation for the DocumentSnapshot class, you'll see that we can also get the value for a specific field with:
document.get("nameOfThefield")
You'll then want to add the field values to jobInfo.add(...) inside the onComplete method.

Getting Invalid collection reference error when accessing Firestore

I am unable to from a new collection in Firestore using the below code. It always gives me the error:
Invalid collection reference. Collection references must have an odd number of segments, but Stores/Stored_Product_Storage has 2.
ProductClass dummyProduct = new ProductClass("dummyitem", 0, "none", 0);
db.collection("Stores").document("Stored_Product_Storage").collection(String.valueOf(docLocation)).add(dummyProduct);
db.collection("Stores").document("Stored_Product_Storage").collection(String.valueOf(docLocation))
.whereGreaterThan("price", 0)
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
products.clear();
for (QueryDocumentSnapshot document : task.getResult()) {
Log.d(TAG, document.getId() + " => " + document.getData());
String name = document.get("name").toString();
int price = Integer.parseInt(document.get("price").toString());
float units = Float.parseFloat(document.get("units").toString());
String unitType = document.get("unitType").toString();
ProductClass product = new ProductClass( name, price, unitType, units);
products.add(product);
}
progressDialog.dismiss();
storeProductUpdateActivityAdapter.notifyDataSetChanged();
} else {
Log.w(TAG, "Error getting documents.", task.getException());
}
}
});
When I directly insert a string instead of String.valueOf(docLocation) then a new collection along with a document is created for inserting the dummy product data. But if I try to use a variable instead of directly giving the string in that field then it gives me the collection reference error. The same happens while reading the data. I am storing a custom string id in docLocation.Can anyone help me in fixing this? Thanks in advance.
It's almost certainly the case that String.valueOf(docLocation) is returning an empty string. You'll need to check that before passing it to document().

How do I handle my data model class to get Firebase Firestore data into recyclerView?

I have a Firestore database like this:
I want to access each of the different symptom data's i.e. "Anxiety_data" and its children which consists of timestamps, and then a dictionary, and place them into a RecyclerView using FirebaseUI FirebaseRecyclerViewAdapter
I have this model class:
public class EntryDataModel {
private String timestamp, symptom, severity, comment;
public EntryDataModel() {}
public EntryDataModel(String timestamp, String symptom, String severity, String comment) {
this.timestamp = timestamp;
this.symptom = symptom;
this.severity = severity;
this.comment = comment;
}
public String getTimestamp() {return timestamp;}
public String getSymptom() {return symptom;}
public String getSeverity() {return severity;}
public String getComment() {return comment;}
}
Here is my Query:
Query query = db.collection("users").document(user_id).collection("symptom_data");
Here is the Firebase RecyclerView Adapter:
void fireStoreRecyclerAdapterSetup() {
FirestoreRecyclerOptions<EntryDataModel> options = new FirestoreRecyclerOptions.Builder<EntryDataModel>()
.setQuery(query, EntryDataModel.class)
.build();
FirestoreRecyclerAdapter adapter = new FirestoreRecyclerAdapter<EntryDataModel, EntryDataHolder>(options) {
#Override
public void onBindViewHolder(EntryDataHolder holder, int position, EntryDataModel model) {
// Bind the Chat object to the ChatHolder
// ...
System.out.println("Query: " + query.toString());
}
#Override
public EntryDataHolder onCreateViewHolder(ViewGroup group, int i) {
// Create a new instance of the ViewHolder, in this case we are using a custom
// layout called R.layout.message for each item
View view = LayoutInflater.from(group.getContext())
.inflate(R.layout.entry_data_recyclerview_item, group, false);
return new EntryDataHolder(view);
}
};
recyclerView.setAdapter(adapter);
adapter.startListening();
}
}
I am not sure how I should set this up so that I take all the Timestamp arrays from each of the symptom data fields, and have them all together in a list that I can use for the recyclerView.
Maybe I cannot use FirebaseUI Recycler adapter, or I need to iterate through each different symptom field first and build + append a list? Hopefully I am clear in what I would like to do Thank you.
EDIT: I have done it on iOS, This is the result that I want:
EDIT: I have added this, getting the names of each individual document, and then once I have that in a for loop I am now trying to get the Array values and add that to an EntryDataModel list, then I can use my own adapter:
EDIT: This works, I get the data I need, from each Document. Now I just need to be able to Iterate over the fields and timestamps, and use my model to create a list. How can I do that? Log.d("example", "DocumentSnapshot data: " + document.getData()); this prints: D/example: DocumentSnapshot data: {1558879769={severity=Mild, symptom=Anxiety, comment=Mild anxiety at work., timestamp=1558879769}, 1558879745={severity=Mild, symptom=Anxiety, comment=Feeling relaxed watching TV., timestamp=1558879745}, 1558879710={severity=Moderate, symptom=Anxiety, comment=Walking the cat., timestamp=1558879710}, 1558879827={severity=Moderate, symptom=Anxiety, comment=Taking the cat for a walk., timestamp=1558879827}, 1558888729={severity=Mild, symptom=Anxiety, comment=The cat is fed up with walking., timestamp=1558888729}} Now I just need to get each timestamp array and add them to a seperate list, and i can then do this for each document and have a full list of all the timestamp arrays.
void getSymptomData() {
final CollectionReference colRef = db.collection("users").document(user_id).collection("symptom_data");
colRef.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());
DocumentReference docRef = colRef.document(document.getId());
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
Log.d("Iteration", "DocumentSnapshot data: " + document.getData());
} else {
Log.d("NoDoc", "No such document");
}
} else {
Log.d("Failed", "get failed with ", task.getException());
}
}
});
}
Log.d("listylist", list.toString());
} else {
Log.d("tag", "Error getting documents: ", task.getException());
}
}
});
}
The automatic mapping that the Firebase SDK can do from a Firestore document to a Java object, requires that each field name from the document matches a property name in the Java class. It has no mechanism to deal with dynamic fields, such as the timestamps in your example.
So you will have to do your own conversion. For that you can use the public T get (FieldPath fieldPath, Class<T> valueType) or public T get (String field, Class<T> valueType) method, which allow you to get an object from a specific field that you specify. So you will have to loop over the timestamp fields, but after that the Firebase SDK can map the severity, symptom, and timestamp properties to the object.

Categories