HOW TO BUILD ONLINE USER PRESENCE SYSTEM IN ANDROID/Kotlin USING REALTIME DATABASE
Please Subscribe Youtube| Like Facebook | Follow Twitter
For Java 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.
fun onlineUser(view: View?) {
openOnlineUserList()
}
private fun openOnlineUserList() {
if (checkSigInStatus()) {
startActivity(Intent(this, OnlineUserActivity::class.java))
} else {
authenticate()
}
}
var authUser: FirebaseUser? = null
private fun checkSigInStatus(): Boolean {
authUser = FirebaseAuth.getInstance().currentUser
return authUser != null
}
fun authenticate() {
// Choose authentication providers
val providers = Arrays.asList(
EmailBuilder().build(),
GoogleBuilder().build()
)
// Create and launch sign-in intent
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.setTheme(R.style.FirebaseTheme)
.build(),
RC_SIGN_IN)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_SIGN_IN) {
if (resultCode == Activity.RESULT_OK && FirebaseAuth.getInstance().currentUser != null) {
openOnlineUserList()
} else {
Toast.makeText(this, "Sign in Failed. Please Sign in To Continue", Toast.LENGTH_SHORT).show()
}
}
}
companion object {
private const val RC_SIGN_IN = 1
}
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: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/userListView"
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 fun addToUserList(user: FirebaseUser?) {
usersListRef!!.child(user!!.uid).setValue(User(user.displayName, "Online"))
onlineStatus = db!!.getReference("users/" + user.uid + "/onlineStatus")
connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected")
connectedRef!!.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val connected = snapshot.getValue(Boolean::class.java)!!
if (connected) {
onlineStatus!!.onDisconnect().setValue("offline")
onlineStatus!!.setValue("Online")
} else {
onlineStatus!!.setValue("offline")
}
}
override fun onCancelled(error: DatabaseError) {}
})
}
private fun populateUserList() {
userListValueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
userListItems!!.clear()
//first check datasnap shot exist
//then add all users except current/self user
if (dataSnapshot.exists()) {
for (ds in dataSnapshot.children) {
if (ds.exists() && ds.key != user!!.uid) {
val name = ds.child("name").getValue(String::class.java)!!
val onlineStatus = ds.child("onlineStatus").getValue(String::class.java)!!
userListItems!!.add("$name status : $onlineStatus")
}
}
}
adapter = ArrayAdapter(this@OnlineUserActivity,
android.R.layout.simple_list_item_1, android.R.id.text1, userListItems)
userListView.adapter = adapter
adapter!!.notifyDataSetChanged()
}
override fun onCancelled(databaseError: DatabaseError) {}
}
usersListRef!!.addValueEventListener(userListValueEventListener!!)
}
Below is User.kt file code
class User {
var name: String? = null
var onlineStatus: String? = null
constructor() {
// Default constructor required for calls to DataSnapshot.getValue(User.class)
}
constructor(name: String?, onlineStatus: String?) {
this.name = name
this.onlineStatus = onlineStatus
}
}
Whole Code
project level build.gradle file
buildscript {
ext.kotlin_version = '1.4.0-rc'
repositories {
jcenter()
google()
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.3'
// Add this line
classpath 'com.google.gms:google-services:4.3.3'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
app level build.gradle file
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android'
// 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'
implementation "androidx.core:core-ktx:1.3.1"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
}
repositories {
maven { url 'https://dl.bintray.com/kotlin/kotlin-eap' }
mavenCentral()
}
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: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/userListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</LinearLayout>
MainActivity.kt
package com.programtown.example
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import com.firebase.ui.auth.AuthUI
import com.firebase.ui.auth.AuthUI.IdpConfig.EmailBuilder
import com.firebase.ui.auth.AuthUI.IdpConfig.GoogleBuilder
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import java.util.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun onlineUser(view: View?) {
openOnlineUserList()
}
private fun openOnlineUserList() {
if (checkSigInStatus()) {
startActivity(Intent(this, OnlineUserActivity::class.java))
} else {
authenticate()
}
}
var authUser: FirebaseUser? = null
private fun checkSigInStatus(): Boolean {
authUser = FirebaseAuth.getInstance().currentUser
return authUser != null
}
fun authenticate() {
// Choose authentication providers
val providers = Arrays.asList(
EmailBuilder().build(),
GoogleBuilder().build()
)
// Create and launch sign-in intent
startActivityForResult(
AuthUI.getInstance()
.createSignInIntentBuilder()
.setAvailableProviders(providers)
.setTheme(R.style.FirebaseTheme)
.build(),
RC_SIGN_IN)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == RC_SIGN_IN) {
if (resultCode == Activity.RESULT_OK && FirebaseAuth.getInstance().currentUser != null) {
openOnlineUserList()
} else {
Toast.makeText(this, "Sign in Failed. Please Sign in To Continue", Toast.LENGTH_SHORT).show()
}
}
}
companion object {
private const val RC_SIGN_IN = 1
}
}
OnlineUserActivity.kt
package com.programtown.example
import android.os.Bundle
import android.view.View
import android.widget.ArrayAdapter
import android.widget.ListView
import androidx.appcompat.app.AppCompatActivity
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.database.*
import kotlinx.android.synthetic.main.activity_online_user.*
import java.util.*
class OnlineUserActivity : AppCompatActivity() {
var user: FirebaseUser? = null
var db: FirebaseDatabase? = null
var usersListRef: DatabaseReference? = null
var onlineStatus: DatabaseReference? = null
var connectedRef: DatabaseReference? = null
var userListValueEventListener: ValueEventListener? = null
var userListItems: ArrayList<String>? = null
var adapter: ArrayAdapter<String>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_online_user)
userListItems = ArrayList()
db = FirebaseDatabase.getInstance()
usersListRef = db!!.getReference("users")
user = FirebaseAuth.getInstance().currentUser
addToUserList(user)
populateUserList()
}
private fun addToUserList(user: FirebaseUser?) {
usersListRef!!.child(user!!.uid).setValue(User(user.displayName, "Online"))
onlineStatus = db!!.getReference("users/" + user.uid + "/onlineStatus")
connectedRef = FirebaseDatabase.getInstance().getReference(".info/connected")
connectedRef!!.addValueEventListener(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
val connected = snapshot.getValue(Boolean::class.java)!!
if (connected) {
onlineStatus!!.onDisconnect().setValue("offline")
onlineStatus!!.setValue("Online")
} else {
onlineStatus!!.setValue("offline")
}
}
override fun onCancelled(error: DatabaseError) {}
})
}
private fun populateUserList() {
userListValueEventListener = object : ValueEventListener {
override fun onDataChange(dataSnapshot: DataSnapshot) {
userListItems!!.clear()
//first check datasnap shot exist
//then add all users except current/self user
if (dataSnapshot.exists()) {
for (ds in dataSnapshot.children) {
if (ds.exists() && ds.key != user!!.uid) {
val name = ds.child("name").getValue(String::class.java)!!
val onlineStatus = ds.child("onlineStatus").getValue(String::class.java)!!
userListItems!!.add("$name status : $onlineStatus")
}
}
}
adapter = ArrayAdapter(this@OnlineUserActivity,
android.R.layout.simple_list_item_1, android.R.id.text1, userListItems)
userListView.adapter = adapter
adapter!!.notifyDataSetChanged()
}
override fun onCancelled(databaseError: DatabaseError) {}
}
usersListRef!!.addValueEventListener(userListValueEventListener!!)
}
override fun onDestroy() {
super.onDestroy()
usersListRef!!.removeEventListener(userListValueEventListener!!)
}
}
User.kt
package com.programtown.example
class User {
var name: String? = null
var onlineStatus: String? = null
constructor() {
// Default constructor required for calls to DataSnapshot.getValue(User.class)
}
constructor(name: String?, onlineStatus: String?) {
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