android firestore: retrieve custom object from HashMap - java

I would like to store userdata in a document in Firestore. To do that I’m using a HashMap:
String userID = mAuth.getCurrentUser().getUid();
User user = new User(stringMail, stringUsername, stringGender, stringBirthday,
stringCountry, registeredOn);
HashMap<String, User> userHashMap = new HashMap<>();
userHashMap.put("userdata", user);
users.document(userID).set(userHashMap, SetOptions.merge())
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(RegisterActivity.this, "Upload completed to Firestore!", Toast.LENGTH_LONG).show();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(RegisterActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
});
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(RegisterActivity.this, e.getMessage(), Toast.LENGTH_LONG).show();
}
});
That works pretty good. I do have a problem though with retrieving the data. As the data is stored in a HashMap I can’t use following code:
String userID = mAuth.getUid();
users.document(userID).get()
.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
Map<String, Object> userHashMap = new HashMap<String, Object>();
userHashMap = documentSnapshot.getData();
User user = (User) userHashMap.get("userdata");
I also can't use:
User user = documentSnapshot.toObject(User.class);
So I wondered whether someone can suggest a way how to retrieve a custom object from a HashMap? I should also mention that the document will contain a second HashMap.

Related

How to resolve this onSuccesListener error?

So in the following on Success Listener code:
database.collection("users")
.add(users)
.addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
}
})
I am getting the error:
'addOnSuccessListener(com.google.android.gms.tasks.OnSuccessListener<? super com.google.firebase.firestore.DocumentReference>)' in 'com.google.android.gms.tasks.Task' cannot be applied to '(anonymous com.google.android.gms.tasks.OnSuccessListener<com.google.firebase.firestore.DocumentSnapshot>)'
How to resolve this?
Assuming that you have a database structure that looks like this:
Firestore-root
|
--- users (collection)
|
--- $uid (document)
|
--- // user fields.
To check if a particular users already exists, please use the following lines of cde:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
FirebaseFirestore db = FirebaseFirestore.getInstance();
DocumentReference uidRef = db.collection("users").document(uid);
uidRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document != null) {
Log.d("TAG", "User already exists.");
} else {
Log.d("TAG", "User doesn't exist.");
}
} else {
Log.d("TAG", task.getException().getMessage()); //Never ignore potential errors!
}
}
});
It's as the stacktrace implies, you're using the wrong parameter in the listener. You need to use OnSuccessListener<DocumentReference> or any superclass of DocumentReference.
EDIT:
Reference from Firebase : Add a document
You need to use DocumentReference:
Map<String, Object> data = new HashMap<>();
data.put("name", "Tokyo");
data.put("country", "Japan");
db.collection("cities")
.add(data)
.addOnSuccessListener(new OnSuccessListener<DocumentReference>() {
#Override
public void onSuccess(DocumentReference documentReference) {
Log.d(TAG, "DocumentSnapshot written with ID: " + documentReference.getId());
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.w(TAG, "Error adding document", e);
}
});
Use the resulting id to get the document again.

Store subcollection data in Firestore

I have a problem on to add a subcollection "Diary" under collection "Users". How to make it so that one user can have several diary?
Here's Java codes:
check.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FirebaseUser user = fAuth.getCurrentUser();
String uid = user.getUid();
DocumentReference df = fStore.collection("Diary").document(user.getUid());
Map<String, Object> diaryInfo = new HashMap<>();
diaryInfo.put("Symptom", symptom.getEditText().getText().toString());
diaryInfo.put("UID", uid);
diaryInfo.put("Note", note.getEditText().getText().toString());
diaryInfo.put("Date", dateButton.getText().toString());
SimpleDateFormat tf = new SimpleDateFormat("hh:mm a");
String currentTime = tf.format(Calendar.getInstance().getTime());
time.setText(currentTime);
diaryInfo.put("Time",time.getText().toString());
feeling = spinner.getSelectedItem().toString();
diaryInfo.put("Feeling", feeling);
df.set(diaryInfo).addOnSuccessListener(new OnSuccessListener() {
#Override
public void onSuccess(Object o) {
Toast.makeText(add_diary_user.this, "Data successfully stored", Toast.LENGTH_SHORT).show();
startActivity(new Intent(add_diary_user.this, diary_user.class));
finish();
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(add_diary_user.this, e.toString(), Toast.LENGTH_SHORT).show();
}
});
}
});
Database structure:
You should place your subcollection inside the corresponding document of your parent collection:
// Create a reference to a new 'Diary' document at Users/user_id/Diary/doc_id
// with an auto-generated document id
DocumentReference df = fStore.collection("Users").document(user.getUid()).collection("Diary").document();
When you need to retrieve the diaries, query the individual user's Diary collection:
fStore.collection("Users").document(user.getUid()).collection("Diary")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
// Do something with your retrieved documents
}
}
}

FireStore Data insert (Android Studio 3.6.1)

I want to create new document in my DB in FireStore and insert a comment from my app, When I tried to debug My code in case of comment insert, I found
that onSuccess method is skipped, in every case when toAdd(String, this is the comment) isn't empty The
code runs onFailure Method, and I can't understand why. Here is my code:
public void insertComment(View v) {
toAdd = comment.getText().toString();
if (!toAdd.isEmpty()) {
//TODO : CHECK THIS SOLUTION (onSuccess not working)
String currentUser = FirebaseAuth.getInstance().getCurrentUser().getUid();
Map<String, Object> comment = new HashMap<>();
comment.put(COMMENT, toAdd);
db.collection("Users").document(currentUser+" Comments").set(comment)
.addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
counter++;
ListItem listItem = new ListItem(counter, toAdd);
listItems.add(listItem);
listView.setAdapter(adapter);
adapter.notifyDataSetChanged();
Toast.makeText(ListActivity.this, "Comment saved", Toast.LENGTH_SHORT).show();
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(ListActivity.this, "Error!", Toast.LENGTH_SHORT).show();
}
});
}
else {
Toast.makeText(ListActivity.this, "Empty Comment!", Toast.LENGTH_SHORT);
}
}
Solve it via Database -> Rules ->
Change allow read, write: if false; to true;

Get and update Data from Firestore - Problems

For my app, I have a lot of occasions, where I need to retrieve and write data to Firestore.
I am able to write new data and to get and display data. I can also overwrite data in one class, but having troubles right now in another one despite using the same method.
FirebaseFirestore db = FirebaseFirestore.getInstance();
FirebaseUser currUser = FirebaseAuth.getInstance().getCurrentUser();
DocumentReference userDocRef = db.collection("Users").document(currUser.getEmail());
//access current values saved under this user
userDocRef.get().addOnSuccessListener(new
OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
User currentUser = documentSnapshot.toObject(User.class);
warmups_skipped = currentUser.getWarmupsSkipped()+1;
Log.d(TAG, "DocumentSnapshot successfully retrieved! " + warmups_skipped);
}
});
I can see in logCat that the local variable is being changed. All good so far.
Then I call this method immediately after:
FirebaseFirestore db = FirebaseFirestore.getInstance();
FirebaseUser currUser = FirebaseAuth.getInstance().getCurrentUser();
DocumentReference userDocRef = db.collection("Users").document(currUser.getEmail());
Map<String, Object> update = new HashMap<>();
update.put(WARMUPSSKIPPED, getWarmups_skipped());
userDocRef
.set(update, SetOptions.merge()).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "Document has been saved");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "Document could not be saved");
}
});
}
Also here I get the TAG message displayed, but nothing gets changed in FireStore. I use the exact same second method in another class, where I do not check for, what is already online, but simply overwrite it.
Really appreciate the help!
Okay I figured it out eventually. Apparently I cannot have these two actions separate, so I put the second method inside the first one and now it seems to work. Not entirely sure why, though, cause the variables are class variables and should be accessible from both methods. If anyone else has that problem, here is how it works for me now:
FirebaseFirestore db = FirebaseFirestore.getInstance();
FirebaseUser currUser = FirebaseAuth.getInstance().getCurrentUser();
final DocumentReference userDocRef = db.collection("Users").document(currUser.getEmail());
//access current values saved under this user
userDocRef.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
User currentUser = documentSnapshot.toObject(User.class);
warmups_skipped = currentUser.getWarmupsSkipped()+1;
Map<String, Object> update = new HashMap<>();
update.put(WARMUPSSKIPPED, warmups_skipped);
userDocRef
.set(update, SetOptions.merge()).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Log.d(TAG, "Document has been saved");
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Log.d(TAG, "Document could not be saved" +e.toString());
}
});
Log.d(TAG, "DocumentSnapshot successfully retrieved! " + warmups_skipped);
}
});

Firebase Firestore get data from collection

I want to get data from my Firebase Firestore database. I have a collection called user and every user has collection of some objects of the same type (My Java custom object). I want to fill my ArrayList with these objects when my Activity is created.
private static ArrayList<Type> mArrayList = new ArrayList<>();;
In onCreate():
getListItems();
Log.d(TAG, "onCreate: LIST IN ONCREATE = " + mArrayList);
*// it logs empty list here
Method called to get items to list:
private void getListItems() {
mFirebaseFirestore.collection("some collection").get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
if (documentSnapshots.isEmpty()) {
Log.d(TAG, "onSuccess: LIST EMPTY");
return;
} else {
for (DocumentSnapshot documentSnapshot : documentSnapshots) {
if (documentSnapshot.exists()) {
Log.d(TAG, "onSuccess: DOCUMENT" + documentSnapshot.getId() + " ; " + documentSnapshot.getData());
DocumentReference documentReference1 = FirebaseFirestore.getInstance().document("some path");
documentReference1.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
Type type= documentSnapshot.toObject(Type.class);
Log.d(TAG, "onSuccess: " + type.toString());
mArrayList.add(type);
Log.d(TAG, "onSuccess: " + mArrayList);
/* these logs here display correct data but when
I log it in onCreate() method it's empty*/
}
});
}
}
}
}
}).addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(getApplicationContext(), "Error getting data!!!", Toast.LENGTH_LONG).show();
}
});
}
The get() operation returns a Task<> which means it is an asynchronous operation. Calling getListItems() only starts the operation, it does not wait for it to complete, that's why you have to add success and failure listeners.
Although there's not much you can do about the async nature of the operation, you can simplify your code as follows:
private void getListItems() {
mFirebaseFirestore.collection("some collection").get()
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
if (documentSnapshots.isEmpty()) {
Log.d(TAG, "onSuccess: LIST EMPTY");
return;
} else {
// Convert the whole Query Snapshot to a list
// of objects directly! No need to fetch each
// document.
List<Type> types = documentSnapshots.toObjects(Type.class);
// Add all to your list
mArrayList.addAll(types);
Log.d(TAG, "onSuccess: " + mArrayList);
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
Toast.makeText(getApplicationContext(), "Error getting data!!!", Toast.LENGTH_LONG).show();
}
});
}
Try this..Working fine.Below function will get Realtime Updates from firebse as well..
db = FirebaseFirestore.getInstance();
db.collection("dynamic_menu").addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
if (e !=null)
{
}
for (DocumentChange documentChange : documentSnapshots.getDocumentChanges())
{
String isAttendance = documentChange.getDocument().getData().get("Attendance").toString();
String isCalender = documentChange.getDocument().getData().get("Calender").toString();
String isEnablelocation = documentChange.getDocument().getData().get("Enable Location").toString();
}
}
});
More reference
:https://firebase.google.com/docs/firestore/query-data/listen
If You do not want realtime updates refer Below Document
https://firebase.google.com/docs/firestore/query-data/get-data
Here is a simplified example:
Create a collection "DownloadInfo" in Firebase.
And add a few documents with these fields inside it:
file_name (string),
id (string),
size (number)
Create your class:
public class DownloadInfo {
public String file_name;
public String id;
public Integer size;
}
Code to get list of objects:
FirebaseFirestore db = FirebaseFirestore.getInstance();
db.collection("DownloadInfo")
.get()
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
public void onComplete(#NonNull Task<QuerySnapshot> task) {
if (task.isSuccessful()) {
if (task.getResult() != null) {
List<DownloadInfo> downloadInfoList = task.getResult().toObjects(DownloadInfo.class);
for (DownloadInfo downloadInfo : downloadInfoList) {
doSomething(downloadInfo.file_name, downloadInfo.id, downloadInfo.size);
}
}
}
} else {
Log.w(TAG, "Error getting documents.", task.getException());
}
}
});
db.collection("users").get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
console.log(`${doc.id} => ${doc.data()}`);
});
source:-
https://firebase.google.com/docs/firestore/quickstart
This is the code to get the list.
Since this is an async task, it takes time that's why the list size shows empty at first.
But including the source for the cache data will enable the previous list(and also its size) to be in memory until next task is performed.
Source source = Source.CACHE;
firebaseFirestore
.collection("collectionname")
.get(source)
.addOnSuccessListener(new OnSuccessListener<QuerySnapshot>() {
#Override
public void onSuccess(QuerySnapshot documentSnapshots) {
if (documentSnapshots.isEmpty()) {
return;
} else {
// Convert the whole Query Snapshot to a list
// of objects directly! No need to fetch each
// document.
List<ModelClass> types = documentSnapshots.toObjects(ModelClass.class);
// Add all to your list
mArrayList.addAll(types);
}
}
})
.addOnFailureListener(new OnFailureListener() {
#Override
public void onFailure(#NonNull Exception e) {
}
});

Categories