I am developing a chat app in which i have to maintain the user online status and the technology i am using is firebase so how can i do that any kind of help is appreciated. Thanks in advance...
Mabz has the right idea conceptually, but I want to highlight a feature of Firebase that specifically addresses your use case.
The trouble that I had run into was updating the RealtimeDatabase with an 'Offline' status. That is, If the client (e.g. your Android app) is offline, how is it supposed to tell the database?
The Solution: Use DatabaseRef.onDisconnect() to set offline status automatically
DatabaseRef presenceRef =
FirebaseDatabase.getInstance().getReference("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().setValue("I disconnected!");
From the documentation:
When you establish an onDisconnect() operation, the operation lives on
the Firebase Realtime Database server. The server checks security to
make sure the user can perform the write event requested, and informs
the your app if it is invalid. The server then monitors the
connection. If at any point the connection times out, or is actively
closed by the Realtime Database client, the server checks security a
second time (to make sure the operation is still valid) and then
invokes the event.
A (slightly) more practical example might do something like this when your app first connects:
DatabaseRef userStatus =
FirebaseDatabase.getInstance().getReference("users/<user_id>/status");
userStatus.onDisconnect.setValue("offline");
userStatus.setValue("online");
NOTE: Please note the order of the last two "online" and "offline" status lines. Do not swap it, or else you might have "ghost users" in case the onDisconnect handler fails to register due to a disconnect itself. (which is actually a race condition problem)
Based upon the information you gave and not considering the performance or bandwidth constraints, here is a way on how I would solve this:
Using a service and a thread in the Real-Time Database:
Within the Real-Time Database, I would have an OnlineStatus thread, and then I would save child Key-Value pairs of User Ids and set to their Value to "Green" or "Yellow" or "Red"; this would give me the status as to whether the user is On-line or Away. So that would look like:
OnlineStatus
User1: Green
User2: Yellow
User3: Red
Create a Service that will check if:
The user is authenticated
The app is in the background
If the user is authenticated and the app is open, then write to the OnlineStatus thread Current User as a key and "Green" as a Value.
If the app is in the background and the user is authenticated, then do the same but "Yellow" is the Value.
Anything else should result in the color "Red". Also note that if the user signs-off, you may want to write "Red" during that operation.
So this allows every device to update Firebase Real-time Database. What remains is adding a reference to your OnlineStatus location to listen to changes through a ValueEventListener.
Hope this helps.
You can use database reference listener “.info/connected” plus disconnect method dbRef.onDisconnect() to solve the presence problem of online user.
Here is some sample code:
onlineStatus = db.getReference("users/"+user.getUid()+"/onlineStatus");
connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
boolean connected = snapshot.getValue(Boolean.class);
if (connected) {
onlineStatus.onDisconnect().setValue("offline");
onlineStatus.setValue("Online");
} else {
onlineStatus.setValue("offline");
}
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
You can read more in this article on my website.
Related
Changing the email address of the currently logged in user works just fine using the updateEmail method of the user object, however it appears as if that change is not replicated to other logins, which still keep the previous email address.
Is there any notification I need to subscribe to / catch in order to be notified by such a change or is there another way to accomplish that?
Me, as a long time Firebaser, I read the docs a lot, like a lot. And I do have a workaround for this since there aren’t any built in.
First, if you provide multiple sessions per user, you’d want to use the reauthenticate method.
Re-authenticate a user
Some security-sensitive actions—such as deleting an account, setting a
primary email address, and changing a password—require that the user
has recently signed in. If you perform one of these actions, and the
user signed in too long ago, the action fails with the
FIRAuthErrorCodeCredentialTooOld error. When this happens,
re-authenticate the user by getting new sign-in credentials from the
user and passing the credentials to reauthenticate. For example:
let user = Auth.auth().currentUser
var credential: AuthCredential
// Prompt the user to re-provide their sign-in credentials
user?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
Using this, you’d get the error 90% percent of the time because of old credentials.
Or:
(1) On the change process. Set a specific key for the user on your real-time database or Firestore,
(2) Check for that specific key on your app, if existed. That means the user needs to be re authenticated. Firebase will update the user credential on every new login.
I hope it helps.
Here’s the docs:
Manage users on Firebase
How can I retrieve all data values from Firebase's real-time database when I load onStart event from my Android app without run any listener of Firebase?
Because I don't want to run any listener such as ("ondataChange, childAdded, childRemoved and etc ...") I don't need it.
From the Firebase documentation, the onDataChange method of the listener fires once when being attached and then every time there is a change. So, yes there is a way to fetch the data without an event. Though, this fetch would happen using listener only.
https://firebase.google.com/docs/database/android/read-and-write
Listen for value events To read data at a path and listen for changes,
use the addValueEventListener() oraddListenerForSingleValueEvent()
method to add a ValueEventListener to a DatabaseReference.
You can use the onDataChange() method to read a static snapshot of the
contents at a given path, as they existed at the time of the event.
This method is triggered once when the listener is attached and again
every time the data, including children, changes. The event callback
is passed a snapshot containing all data at that location, including
child data. If there is no data, the snapshot will return false when
you call exists() and null when you call getValue() on it.
There is no way to get the data from a Firebase real-time database or from Cloud Firestore without using a listener. Everything in Firebase is about listeners, even if you are getting data in real-time or if you are getting only once, you need to attach a listener on a particular location. Without it, there is no way you can get it.
Edit: From the official documentation regarding reading data in Android, here is a code snippet that demonstrates a social blogging application retrieving the details of a post from the database:
ValueEventListener postListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
// Get Post object and use the values to update the UI
Post post = dataSnapshot.getValue(Post.class);
// ...
}
#Override
public void onCancelled(DatabaseError databaseError) {
// Getting Post failed, log a message
Log.w(TAG, "loadPost:onCancelled", databaseError.toException());
// ...
}
};
mPostReference.addValueEventListener(postListener);
Though, it is late to reply here, but I am choosing to post here only rather than creating another thread on this.
IMO, the basic expectation from any database is to be able to read a selected set of data which is available in DB. It does not matter whether I have to attach a listener or write a custom code.
While I understand that the added benefit of real-time DB is that it is event driven and UI does not need to wait for user to initiate an action for getting the data, but how come there is no way to retrieve data if there is a need !! I have spent hours looking for this one way to fetch data. OOh :(
For the past few days i've been trying to show the online/offline status of a user.. For this i have a register activity where they register and their info gets saved in firebase and if they exit an activity i have overriden its onstop method and made the value to set to offline... but if the user suddenly loses internet connection it still shows online.. i cant change it to offline because internet is needed to make a change in the database and the use doesn't have internet... SO how do i set the database value to offline... i googled quite some stuff about this but didnt find anything... Can anyone please help me out please
My code
#Override
protected void onStart() {
super.onStart();
fetchData();
// mDatabaseReference.child("UserData").child(UID).child("Online").setValue("True");
}
#Override
protected void onStop() {
super.onStop();
fetchData();
// mDatabaseReference.child("UserData").child(UID).child("Online").setValue(false);
}
What you're trying to do is known as a presence system. The Firebase Database has a special API to allow this: onDisconnect(). When you attach a handler to onDisconnect(), the write operation you specify will be executed on the server when that server detects that the client has disconnected.
From the documentation on managing presence:
Here is a simple example of writing data upon disconnection by using the onDisconnect primitive:
DatabaseRef presenceRef = FirebaseDatabase.getInstance().getReference("disconnectmessage");
// Write a string when this client loses connection
presenceRef.onDisconnect().setValue("I disconnected!");
In your case this could be as simple as:
protected void onStart() {
super.onStart();
fetchData();
DatabaseReference onlineRef = mDatabaseReference.child("UserData").child(UID).child("Online");
onlineRef.setValue("True");
onlineRef.onDisconnect().setValue("False");
}
Note that this will work in simple cases, but will start to have problems for example when your connection toggles rapidly. In that case it may take the server longer to detect that the client disappears (since this may depends on the socket timing out) than it takes the client to reconnect, resulting in an invalid False.
To handle these situations better, check out the sample presence system in the documentation, which has more elaborate handling of edge cases.
If user turn off both wi-fi, 3g, 4g, and so on and reverse (no internet connection). Firebase database name child connections:(true/false)
So, when internet connections, wi-fi, 3g, 4g, and so on are off or missing, the user is offline so he can't be found.
Remember the two scenarios: Before and After. If user is offline before an other user search him, then he will not displayed in the list result, if user is off-line after an other user search him, then it will display NO MORE AVAILABLE icon on the user
Kindly some one help me for this problem.
To solve this, you can create a new node in your Firebase Realtime Database to hold all online users, so when the user opens the application, you'll immediately add his id to this newly created node. Then, if you want to check if the user is online, just check if his id exists in the list.
You can also add a new property named isOnline for each user in your database and then update it accordingly.
For that, I recommend you using Firebase's built-in onDisconnect() method. It enables you to predefine an operation that will happen as soon as the client becomes disconnected.
See Firebase documentation.
You can also detect the connection state of the user. For many presence-related features, it is useful for your app to know when it is online or offline. Firebase Realtime Database provides a special location at /.info/connected which is updated every time the Firebase Realtime Database client's connection state changes. Here is an example also from the official documentation:
DatabaseReference connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
boolean connected = snapshot.getValue(Boolean.class);
if (connected) {
System.out.println("connected");
} else {
System.out.println("not connected");
}
}
#Override
public void onCancelled(DatabaseError error) {
System.err.println("Listener was cancelled");
}
});
Though this is more than a year late, to clear up the confusion. Alex's question would like to implement a live chat scenario in which each user can view everyone's online status at their ends or on their devices.
A simple solution be to create a node where all users would inject their online status each. e.g.
//say your realtime database has the child `online_statuses`
DatabaseReference online_status_all_users = FirebaseDatabase.getInstance().getReference().child("online_statuses");
//on each user's device when connected they should indicate e.g. `linker` should tell everyone he's snooping around
online_status_all_users.child("#linker").setValue("online");
//also when he's not doing any snooping or if snooping goes bad he should also tell
online_status_all_users.child("#linker").onDisconnect().setValue("offline")
So if another user, say mario checks for linker from his end he can be sure some snooping around is still ongoing if linker is online i.e.
DatabaseReference online_status_all_users = FirebaseDatabase.getInstance().getReference().child("online_statuses");
online_status_all_users.child("#linker").addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
String snooping_status = dataSnapshot.getValue(String.class);
//mario should decide what to do with linker's snooping status here e.g.
if(snooping_status.contentEquals("online")){
//tell linker to stop doing sh*t
}else{
//tell linker to do a lot of sh****t
}
}
#Override
public void onCancelled(DatabaseError databaseError) {
}
});
I want to maintain user presence in Firestore or Firebase. Actually the single user account handled by multiple mobiles devices and one tablet and that tablet maintain or do actions or provide functionality against my firestore data change. so i need to maintain presence of tablet in Firestore or Firebase so other mobile device understand the tablet working or not.
So please give me some solution on that.
I use Firebase node to check connection status. code below but i not give the proper response e.g when i remove the lan cable from router then also onDataChange not fire.for disconnection.
final FirebaseDatabase database = FirebaseDatabase.getInstance();
final DatabaseReference myConnectionsRef = database.getReference(FirebaseAuth.getInstance().getCurrentUser().getUid()+"/tab/connections");
// stores the timestamp of my last disconnect (the last time I was seen online)
final DatabaseReference lastOnlineRef = database.getReference(FirebaseAuth.getInstance().getCurrentUser().getUid()+"/tab/lastOnline");
final DatabaseReference connectedRef = database.getReference(".info/connected");
connectedRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot snapshot) {
boolean connected = snapshot.getValue(Boolean.class);
if (connected) {
DatabaseReference con = myConnectionsRef.push();
// when this device disconnects, remove it
con.onDisconnect().removeValue();
// when I disconnect, update the last time I was seen online
lastOnlineRef.onDisconnect().setValue(ServerValue.TIMESTAMP);
long timestamp = Calendar.getInstance().getTimeInMillis();
// add this device to my connections list
// this value could contain info about the device or a timestamp too
con.setValue(Boolean.TRUE);
}else {
long timestamp2 = Calendar.getInstance().getTimeInMillis();
}
}
#Override
public void onCancelled(DatabaseError error) {
System.err.println("Listener was cancelled at .info/connected");
}
});
Even if you are using the Firebase Realtime database or the new Cloud Firestore, what you are doing in your code is called a 1:1 relation between a user and a connection. You are looking for a user that may be connected from multiple devices. If you are using a flag that can hold a value of true or false depending on the connection status, you'll have a problem, which is if the user will disconnect from one of those devices, the onDisconnect() method from that particular device is called and will set online to false while the users may still be connected on another devices.
In my opinion, you should not rely on having a 1:1 relation between a user and their connection(s). The example in the official documentation treat connections as a collection and assume that the user is connected as long as there is any "connect Id" (generated by push()) method left for that user. I recommend you do the same thing, to prevent hard to debug race conditions and connection problems
You can update connected flag with device id on firebase realtime database, After login .
So all other devices will know about other devices.
While logout / on application Close, Make that flag to false.