How to build Online User Presence System in Android using Realtime Database
Please Subscribe Youtube| Like Facebook | Follow Twitter
For Kotlin follow this article
Introduction
In this article we will learn how to build online user presence system using firebase real time database in our android app.
Our Example
In our example we have a list of user showing their online status that are connected in our android app through firebase realtime database. Before connecting to our database of online users, app user first needs to register using Firebase Authentication. Then that information will be saved our database. After registration user can see list of other online/offline users.
Online/Offline status is implemented using firebase db reference “.info/connected” which is updated every time the Firebase Realtime Database client’s connection state changes. So we will attach listener to it whenever user connection become active (online) or inactive (offline), we will then update online status value accordingly.
Requirements:
- Android Project/App in which Online User Presence to be implemented
- Firebase Project
Note: First you need to create Firebase Project and then connect your app to it. You can follow this article to set it up. Also you need to have knowledge of about Firebase Authentication and Firebase Realtime database, therefore it is recommended to follow this article for realtime db and this article for firebase authentication.
Steps
Follow below steps
1) Integrate Firebase Authentication and Realtime Database in your app
2) Code Online Presence System
3) Run and test your app
1) Integrate Firebase Authentication and Realtime Database in your app
First add firebase-auth , firebase-ui-auth and firebase realtime database library at app level build.gradle file
implementation 'com.google.firebase:firebase-database:19.3.0'
implementation 'com.google.firebase:firebase-auth:19.3.1'
implementation 'com.firebaseui:firebase-ui-auth:6.2.0'
Add FirebaseTheme inside res->values->styles.xml
<style name="FirebaseTheme" parent="Theme.AppCompat.Light.DarkActionBar"> </style>
2) Code Online Presence System
Our main activity screen contain button “ONLINE USERS”, on button click we will check user registration using firebase auth. If user is new then we will popup firebase auth registration and save user info in our db. Else if user is already registered then it will directly go to next activity which displays online status of registered users list.
public void onlineUser(View view) {
openOnlineUserList();
}
private void openOnlineUserList() {
if(checkSigInStatus()){
startActivity(new Intent(this,OnlineUserActivity.class));
}
else{
authenticate();
}
}
FirebaseUser authUser;
private static final int RC_SIGN_IN = 1;
private boolean checkSigInStatus() {
authUser = FirebaseAuth.getInstance().getCurrentUser();
return authUser != null;
}
public void authenticate() {
// Choose authentication providers
List<AuthUI.IdpConfig> providers = Arrays.asList(
new AuthUI.IdpConfig.EmailBuilder().build(),
new AuthUI.IdpConfig.GoogleBuilder().build()
);
// Create and launch sign-in intent
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.setTheme(R.style.FirebaseTheme)
.build(),
RC_SIGN_IN);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
if (resultCode == RESULT_OK && FirebaseAuth.getInstance().getCurrentUser()!=null ) {
openOnlineUserList();
} else {
Toast.makeText(this,"Sign in Failed. Please Sign in To Continue",Toast.LENGTH_SHORT).show();
}
}
}
Below is xml code for main activity and onlineuser activity respectively
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
>
<Button
android:id="@+id/change"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Online Users"
android:gravity="center"
android:onClick="onlineUser"
/>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".OnlineUserActivity">
<ListView
android:id="@+id/user_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
Now on OnlineUserActivity we will first add the current user to users node in our db if previously not added and then change its online status to online. Then we have attached listener to db ref “.info/connected” which will be called whenever user online status is changed. When user goes offline normally it will take 0-3 minutes to make changes effect. This is achieved using onDisconnect() method, where we have set online status value to offline on disconnect.
After adding user to users node and setting its online status we have called populateUserList() method, which will read all connected users online status on users node and display list on our listview. We have attached listener to users node so whenever if any user’s online status is changed then its updated value will be reflected in our users’ listview.
private void addToUserList(FirebaseUser user) {
usersListRef.child(user.getUid()).setValue(new User(user.getDisplayName(),"Online"));
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) {
}
});
}
private void populateUserList() {
userListValueEventListener = new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
userListItems.clear();
//first check datasnap shot exist
//then add all users except current/self user
if (dataSnapshot.exists()) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
if (ds.exists() && !ds.getKey().equals(user.getUid())){
String name = ds.child("name").getValue(String.class);
String onlineStatus = ds.child("onlineStatus").getValue(String.class);
userListItems.add(name+" status : "+onlineStatus);
}
}
}
adapter = new ArrayAdapter<String>(OnlineUserActivity.this,
android.R.layout.simple_list_item_1, android.R.id.text1, userListItems);
userListView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
};
usersListRef.addValueEventListener(userListValueEventListener);
}
Below is User java file code
package com.programtown.example;
public class User{
public String name,onlineStatus;
public User() {
// Default constructor required for calls to DataSnapshot.getValue(User.class)
}
public User(String name,String onlineStatus) {
this.name = name;
this.onlineStatus=onlineStatus;
}
}
Whole Code
app level build.gradle file
apply plugin: 'com.android.application'
// Add this line
apply plugin: 'com.google.gms.google-services'
android {
compileSdkVersion 28
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.programtown.example"
minSdkVersion 17
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
implementation 'com.google.firebase:firebase-database:19.3.0'
implementation 'com.google.firebase:firebase-auth:19.3.1'
implementation 'com.firebaseui:firebase-ui-auth:6.2.0'
}
Style.xml
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
</style>
<style name="FirebaseTheme" parent="Theme.AppCompat.Light.DarkActionBar">
</style>
</resources>
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:orientation="vertical"
>
<Button
android:id="@+id/change"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Online Users"
android:gravity="center"
android:onClick="onlineUser"
/>
</RelativeLayout>
activity_online_user.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".OnlineUserActivity">
<ListView
android:id="@+id/user_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
MainActivity.java
package com.programtown.example;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.firebase.ui.auth.AuthUI;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import java.util.Arrays;
import java.util.List;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
public void onlineUser(View view) {
openOnlineUserList();
}
private void openOnlineUserList() {
if(checkSigInStatus()){
startActivity(new Intent(this,OnlineUserActivity.class));
}
else{
authenticate();
}
}
FirebaseUser authUser;
private static final int RC_SIGN_IN = 1;
private boolean checkSigInStatus() {
authUser = FirebaseAuth.getInstance().getCurrentUser();
return authUser != null;
}
public void authenticate() {
// Choose authentication providers
List<AuthUI.IdpConfig> providers = Arrays.asList(
new AuthUI.IdpConfig.EmailBuilder().build(),
new AuthUI.IdpConfig.GoogleBuilder().build()
);
// Create and launch sign-in intent
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.setTheme(R.style.FirebaseTheme)
.build(),
RC_SIGN_IN);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == RC_SIGN_IN) {
if (resultCode == RESULT_OK && FirebaseAuth.getInstance().getCurrentUser()!=null ) {
openOnlineUserList();
} else {
Toast.makeText(this,"Sign in Failed. Please Sign in To Continue",Toast.LENGTH_SHORT).show();
}
}
}
}
OnlineUserActivity.java
package com.programtown.example;
import android.os.Bundle;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;
import java.util.ArrayList;
public class OnlineUserActivity extends AppCompatActivity {
FirebaseUser user;
FirebaseDatabase db;
DatabaseReference usersListRef,onlineStatus,connectedRef;
ValueEventListener userListValueEventListener;
ListView userListView;
ArrayList<String> userListItems;
ArrayAdapter<String> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_online_user);
userListView =(ListView) findViewById(R.id.user_list);
userListItems = new ArrayList<String>();
db = FirebaseDatabase.getInstance();
usersListRef = db.getReference("users");
user=FirebaseAuth.getInstance().getCurrentUser();
addToUserList(user);
populateUserList();
}
private void addToUserList(FirebaseUser user) {
usersListRef.child(user.getUid()).setValue(new User(user.getDisplayName(),"Online"));
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) {
}
});
}
private void populateUserList() {
userListValueEventListener = new ValueEventListener() {
@Override
public void onDataChange(@NonNull DataSnapshot dataSnapshot) {
userListItems.clear();
//first check datasnap shot exist
//then add all users except current/self user
if (dataSnapshot.exists()) {
for (DataSnapshot ds : dataSnapshot.getChildren()) {
if (ds.exists() && !ds.getKey().equals(user.getUid())){
String name = ds.child("name").getValue(String.class);
String onlineStatus = ds.child("onlineStatus").getValue(String.class);
userListItems.add(name+" status : "+onlineStatus);
}
}
}
adapter = new ArrayAdapter<String>(OnlineUserActivity.this,
android.R.layout.simple_list_item_1, android.R.id.text1, userListItems);
userListView.setAdapter(adapter);
adapter.notifyDataSetChanged();
}
@Override
public void onCancelled(@NonNull DatabaseError databaseError) {
}
};
usersListRef.addValueEventListener(userListValueEventListener);
}
@Override
protected void onDestroy() {
super.onDestroy();
usersListRef.removeEventListener(userListValueEventListener);
}
}
User.java
package com.programtown.example;
public class User{
public String name,onlineStatus;
public User() {
// Default constructor required for calls to DataSnapshot.getValue(User.class)
}
public User(String name,String onlineStatus) {
this.name = name;
this.onlineStatus=onlineStatus;
}
}
3) Run and test your app
Conclusion
So in this post we have learned how to display/show list of online/offline users in android using firebase realtime database.
Please Subscribe Youtube| Like Facebook | Follow Twitter