I made an application that users can earn points by taking quizzes. I have a leader board too.
my primary database is cloud firestore. but I need the leaderboard to be more real-time like it needs to update every time when a user earns points without refreshing or closing the fragment.
So I need to connect firebase firestore to real-time databases, (if I change the firestore data(like coins for the specified user or any), it needs to change the real-time data too)
I made codes but it didn't work well. I have attached the code here.
private void LoadFirestore() {
firebaseFirestore.collection("Users")
.document(FirebaseAuth.getInstance().getUid())
.get().addOnSuccessListener(new OnSuccessListener<DocumentSnapshot>() {
#Override
public void onSuccess(DocumentSnapshot documentSnapshot) {
user = documentSnapshot.toObject(User.class);
totalCoins.setText(String.valueOf(user.getCoins()));
}
});
}
private void uploadToRealtime() {
HashMap<String, Object> map = new HashMap<>();
map.put("coins", totalCoins);
firebaseDatabase.getReference().child("Users").child(FirebaseAuth.getInstance().getUid())
.updateChildren(map);
}
}
You can use a onSnapshotListener to get the fata directly from firestore in realtime. Here is a basic example for that:
final DocumentReference docRef = db.collection("cities").document("SF");
docRef.addSnapshotListener(new EventListener<DocumentSnapshot>() {
#Override
public void onEvent(#Nullable DocumentSnapshot snapshot,
#Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "Listen failed.", e);
return;
}
if (snapshot != null && snapshot.exists()) {
Log.d(TAG, "Current data: " + snapshot.getData());
} else {
Log.d(TAG, "Current data: null");
}
}
});
You can check more about it here.
Creating a sync to the RealtimeDatabase just for the realtime feature wouldn't make any sense here and would give you more costs in your Firebase project.
Related
I am using Firebase Firestore (Not the realtime database) and programming an android app with it.
I want my app to work as offline first, which means that when the app loads FireStore will load the data from the Chace and show it to the user, and after that query changes from the server.
To accomplish that, I am doing a normal GET request (Not listener) with source of Chace.
After the get call has completed, I am attaching a listener to listen for all the update. (Locally or from the server), so I Can present the user the updated status.
The problem with this method is that basiclly it means I will download the same object twice First time on the initial get request, and than on the initial listener. Which means I will send duplicates to the UI.
Is there any practice available in the firestore API to avoid this? So the listener will not return records that the get request has already synced?
See below example code. Thx!
GET request: (runs first)
db.collection(sCollection)
.get(SOURCE.CHACE)
.addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() {
#Override
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());
}
}
});
Listener: (runs second)
m_registration = db.collection(sName).
orderBy("stamp", Query.Direction.ASCENDING)
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot value,
#Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "Listen failed.", e);
return;
}
for (DocumentChange dc : value.getDocumentChanges()) {
}
catch(JSONException ex) {
}
}
The Firebase Firestore handles offline so good that you can use it as oofline first database without doing anything on your own. You can read, listen and edit data as if you are doing it synchornously. You should deffinitely check this out.
That means you don't need to use get to make the offline better. It will work seamlesly with the listener to.
I Have a chat list(RecyclerView) which I retrieve from Firestore which I sort with respect to the timestamp. The problem which occurs is The list get updated only when the activity is created Else it sits idle.
I have tried Running fetching code in the Onresume as well.But it just creates another set of same items.
firebaseFirestore = FirebaseFirestore.getInstance();
Query secondquery = firebaseFirestore.collection("Users").document(currentUser).collection("Chat").orderBy("timestamp",Query.Direction.DESCENDING);
secondquery.addSnapshotListener((MainActivity.this), new EventListener<QuerySnapshot>() {
#Override
public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) {
for (DocumentChange doc : documentSnapshots.getDocumentChanges()) {
if (doc.getType() == DocumentChange.Type.ADDED) {
String UserId=doc.getDocument().getId();
ChatList chatList = doc.getDocument().toObject(ChatList.class).withId(UserId);
chatLists.add(chatList);
chatListAdapter.notifyDataSetChanged();
}
}
}
});
I expect it to get updated as soon as the new document is added. But it remains idle until I reopen the activity
Problem: My list items display in the wrong order. This happens when I close the fragment and re-open it. It then displays all the "sent messages" first, and then the received messages after. However, when I'm in writing the messages, they appear in the correct order. It's only when I close the fragment/activity and re-open it that the order has changed.
I call the getMessages method in my on-create method for opening the fragment containing the view.
What I've tried:
Using the Firestore orderby method (both with String and TimeStamp)
Using the simpler Firestore Snapshot Listener
Question:
How do I best use the Firestore Snapshot Listener with a RecyclerView and maintain the order of the items correctly?
Here is my main "getMessages" method:
public void getLiveChatMessages(final ArrayList<ChatConversationMessage> messageArrayList, final ChatConversationAdapter adapter, final String matchClicked) {
final String userID = onboardingFirebaseUser.returnCurrentUserId();
final CollectionReference messagesCollectionRef = db.collection("users")
.document(userID)
.collection("matches")
.document(matchClicked)
.collection("messages");
messagesCollectionRef
.orderBy("TimeStamp", Query.Direction.DESCENDING)
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot value,
#Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "listen:error", e);
return;
}
for (QueryDocumentSnapshot doc : value) {
if (doc.get("Message") != null) {
if (doc.get("Message") != null && doc.get("From user with ID").equals(userID)) {
String message = doc.getString("Message");
messageArrayList.add(new ChatConversationMessage(message));
adapter.notifyDataSetChanged(); //Ensures messages are visible immediately
} else if (doc.get("Message") != null) {
final String message = doc.getString("Message");
DocumentReference matchRef = db.collection("users")
.document(userID)
.collection("matches")
.document(matchClicked);
matchRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
#Override
public void onComplete(#NonNull Task<DocumentSnapshot> task) {
if (task.isSuccessful()) {
DocumentSnapshot document = task.getResult();
if (document.exists()) {
imageReference = storageReference.child(document.getString("profileImg"));
messageArrayList.add(new ChatConversationMessage(message, imageReference));
adapter.notifyDataSetChanged(); //Ensures messages are visible immediately
} else {
Log.d(TAG, "No such document");
}
} else {
Log.d(TAG, "get failed with ", task.getException());
}
}
});
}
}
}
}
});}}
After some time I've found the problem.
I was calling a the get-method on a new reference within the snapshot listener. When you do this, it impacts the order of the items in your ArrayList.
To solve it, ensure that all the items you need from Firestore to create your ArrayList is stored in the same location as fields on each document (and not in two different locations). That way, you don't need to use a separate get-method on a new reference within a snapshot listener. This also keeps client-side code cleaner. For future Googlers, here is how I restructured my method:
messagesCollectionRef
.orderBy("TimeStamp", Query.Direction.ASCENDING)
.addSnapshotListener(new EventListener<QuerySnapshot>() {
#Override
public void onEvent(#Nullable QuerySnapshot snapshots,
#Nullable FirebaseFirestoreException e) {
if (e != null) {
Log.w(TAG, "listen:error", e);
return;
}
for (DocumentChange dc : snapshots.getDocumentChanges()) {
switch (dc.getType()) {
case ADDED:
Log.d(TAG, "New message added" + dc.getDocument().getData());
if (dc.getDocument().get("Message") != null && dc.getDocument().get("From user with ID").equals(userID)) {
String message = dc.getDocument().getString("Message");
messageArrayList.add(new ChatConversationMessage(CURRENTUSER, message, null));
adapter.notifyDataSetChanged();
}
if (dc.getDocument().get("Message") != null && dc.getDocument().get("From user with ID").equals(matchClicked)) {
String message = dc.getDocument().getString("Message");
imageReference = storageReference.child(dc.getDocument().getString("profileImg"));
messageArrayList.add(new ChatConversationMessage(OTHERUSER, message, imageReference));
adapter.notifyDataSetChanged();
}
break;
case MODIFIED:
break;
case REMOVED:
break;
}
}
}
});
As you can see, I've now stored the imageReference String within each message doc in Firestore, and it can be retrieved in the same way I retrieve all the other data I need to make an addition to my ArrayList. The major code change you need to do is where you write your data to the cloud (ie. write/set Firestore docs). That's where you'll need to make sure that everything is added as field values to your doc, so you don't need to get it in separate locations. Good luck!
I'm implementing a search in my android app and I can't seem to make it work.
public void loadReleaseData(String name) {
mDatabase.child("releases")
.child("europe")
.child("data").orderByChild("game/name").startAt(name)
.endAt(name+"\uf8ff")
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if (dataSnapshot.hasChildren()) {
Log.d(TAG, "New datasnapshot");
for (DataSnapshot data : dataSnapshot.getChildren()) {
_Release release = data.getValue(_Release.class);
if (release != null) {
// No platform filter set add all releases!
list.add(release);
if (release.getGame() != null) {
Log.d(TAG, "NAME: " + release.getGame().getName());
}
}
mUpcomingGamesAdapter.notifyDataSetChanged();
}
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
}
When I remove startAt or endAt either of the two data is shown but not the correct data, but when they're both added in, nothing is show and my log "New datasnapshot" doesn't even get printed. I'm searching on the names of the games I have in my database.
My firebase database:
According to your comments, the reason your code was not working was because the value of the name variable that was passed to startAt() and endAt methods was incorrect.
The key for solving the problem is to pass as an argument to both method the exact same name that exist in the database, in this case you should search the name in lower case.
I need to create and use a database entry with all details (uid, name etc) for each user that logs in to the application
I am having trouble storing user data in order to use and retrieve user profile info using Firebase in Android. I found this documentation on the old version of Firebase but this does not seem to work any longer.
Does anyone know how to reproduce the above code so it works with the new version of Firebase? Many thanks in advance.
Edit - code below:
final Firebase ref = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com");
ref.authWithPassword("jenny#example.com", "correcthorsebatterystaple",
new Firebase.AuthResultHandler() {
#Override
public void onAuthenticated(AuthData authData) {
// Authentication just completed successfully :)
Map<String, String> map = new HashMap<String, String>();
map.put("provider", authData.getProvider());
if(authData.getProviderData().containsKey("displayName")) {
map.put("displayName", authData.getProviderData().get("displayName").toString());
}
ref.child("users").child(authData.getUid()).setValue(map);
}
#Override
public void onAuthenticationError(FirebaseError error) {
// Something went wrong :(
}
});
For creating user in firebase database (new Version) ,you need to do changes as followning..
private FirebaseAuth mAuth;
String mUserEmail = "jenny#example.com";
String mPassword = "correcthorsebatterystaple"
mAuth.createUserWithEmailAndPassword(mUserEmail, mPassword)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
#Override
public void onComplete(#NonNull Task<AuthResult> task) {
Log.d(LOG_TAG, getString(R.string.log_message_auth_successful) + " createUserWithEmail:onComplete:" + task.isSuccessful());
// if task is not successful show error
if (!task.isSuccessful()) {
try {
throw task.getException();
} catch (FirebaseAuthUserCollisionException e) {
// log error here
} catch (FirebaseNetworkException e) {
// log error here
} catch (Exception e) {
// log error here
}
} else {
// successfully user account created
// now the AuthStateListener runs the onAuthStateChanged callback
}
}
});
}
now Add following method in onCreate() .
final DatabaseReference ref = FirebaseDatabase.getInstance().
getReferenceFromUrl(https://<YOUR-FIREBASE-APP>.firebaseio.com");
mAuth = FirebaseAuth.getInstance();
mAuthListener = new FirebaseAuth.AuthStateListener() {
#Override
public void onAuthStateChanged(#NonNull FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// User is signed in
Map<String, String> map = new HashMap<String, String>();
map.put("provider", user.getProvider());
if(user.getProviderData().containsKey("displayName")) {
map.put("displayName",
user.getProviderData().get("displayName").toString());
}
ref.child("users").child(user.getUid()).setValue(map);
} else {
// User is signed out
Log.d(LOG_TAG, "onAuthStateChanged:signed_out");
}
}
};
#Override
public void onStart() {
super.onStart();
mAuth.addAuthStateListener(mAuthListener);
}
#Override
public void onStop() {
super.onStop();
if (mAuthListener != null) {
mAuth.removeAuthStateListener(mAuthListener);
}
}
To authenticate users with email and password (Google, Facebook) use Firebase Auth
To use Firebase database to store different data than use Firebase Realtime Database
If you want to Manage Auth users from your backend
User Management - Retrive user's full data and change a user's password or email address
Custom Authentication - You can integrate an external user system with Firebase.
Identity Verification - Use the service to identify these users on your own server.
Firebase Auth has a NODE JS sdk which you can use. If you want to access this features from Android, than you have to create a web service (eg.Restful API) and communicate with it through network.
If you want to access registered Auth users in Realtime Database i dont think you can because they are separated from each other. You can register users in Realtime Database but i dont understund why you want to do that since Auth provides you this feature in an easier way.
Firebase Realtime Database also has an Admin SDK (Java and Node JS)
Please check documentation before asksing.
Please use the latest Firebase SDK, found here: firebase.google.com/docs. Using the legacy SDK (as you are doing now) is just going to lead to a harder time than needed.
you can get user information as follows:
FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
for (UserInfo profile : user.getProviderData()) {
// Id of the provider (ex: google.com)
String providerId = profile.getProviderId();
// UID specific to the provider
String uid = profile.getUid();
// Name, email address, and profile photo Url
String name = profile.getDisplayName();
String email = profile.getEmail();
Uri photoUrl = profile.getPhotoUrl();
};
}