Kotlin Firebase Realtime Database – Display List of Data with FirebaseRecyclerAdapter | Android

In previous post, we had known how to read/write list of data object. Today, we’re gonna look at way to display List of Data in an Android App with FirebaseUI FirebaseRecyclerAdapter.

Related Articles:
Kotlin Firebase Realtime Database – Read/Write Data example | Android
Kotlin Firebase Realtime Database – Get List of Data example | Android

I. FirebaseUI Database

To use the FirebaseUI to display list of data, we need:
– Java class for data object (Model)
– Java class for holding UI elements that match with Model’s fields (ViewHolder and layout)
– Custom RecyclerView adapter to map from a collection from Firebase to Android (FirebaseRecyclerAdapter)
RecyclerView object to set the adapter to provide child views on demand.

kotlin-firebase-db-recycleradapter-demo

1. Model and ViewHolder

Model class is a class that represents the data from Firebase:

class Message {

    var author: String? = ""
    var body: String? = ""
    var time: String? = ""

    // ...
}

ViewHolder layout (R.layout.item_message) with UI items that correspond to Model fields:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">

    <TextView android:id="@+id/tvAuthorItem" />

    <TextView android:id="@+id/tvTimeItem" />

    <TextView android:id="@+id/tvBodyItem" />
</LinearLayout>

ViewHolder class contains Android UI fields that point to layout items:

class MessageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

    fun bindMessage(message: Message?) {
        with(message!!) {
            itemView.tvAuthorItem.text = author
            itemView.tvTimeItem.text = time
            itemView.tvBodyItem.text = body
        }
    }
}

2. FirebaseRecyclerAdapter subclass

We need a subclass of the FirebaseRecyclerAdapter and implement its populateViewHolder() method:

private var mAdapter: FirebaseRecyclerAdapter? = null
// ...
// mMessageReference = FirebaseDatabase.getInstance().getReference("messages")

val query = mMessageReference!!.limitToLast(8)

mAdapter = object : FirebaseRecyclerAdapter(
        Message::class.java, R.layout.item_message, MessageViewHolder::class.java, query) {

    override fun populateViewHolder(viewHolder: MessageViewHolder?, model: Message?, position: Int) {
        viewHolder!!.bindMessage(model)
    }

    override fun onChildChanged(type: ChangeEventListener.EventType, snapshot: DataSnapshot?, index: Int, oldIndex: Int) {
        super.onChildChanged(type, snapshot, index, oldIndex)
        rcvListMessage.scrollToPosition(index)
    }
}

Now look at these lines of code:

val query = mMessageReference!!.limitToLast(8)

mAdapter = object : FirebaseRecyclerAdapter(
        Message::class.java, R.layout.item_message, MessageViewHolder::class.java, query)

– We tell FirebaseRecyclerAdapter object to use Message.class when reading from the database.
– Each Message will be displayed in a R.layout.item_message (that has 3 TextView elements: tvAuthorItem, tvTimeItem, tvBodyItem).
– We indicate class for ViewHolder
– We can just give reference to database node or sort/filter data by using Query:

val query = mMessageReference

val query = mMessageReference.orderByKey()
// orderByValue() or orderByChild("...")

val query = mMessageReference!!.limitToLast(8)
// limitToFirst(..), startAt(...), endAt(...), equalTo(...)

FirebaseRecyclerAdapter will call populateViewHolder() method for each Model it finds in database. It passes us the Model and a ViewHolder.
So what we should do is map the fields from model to the correct TextView items:

override fun populateViewHolder(viewHolder: MessageViewHolder?, model: Message?, position: Int) {
    viewHolder!!.bindMessage(model)
}

We can override onChildChanged() method to do something every time a Child in database has changed its value (add, update…).

3. RecyclerView

Now we set the adapter for RecyclerView object to provide child views on demand:

val layoutManager = LinearLayoutManager(this)
layoutManager.reverseLayout = false
rcvListMessage.setHasFixedSize(true)
rcvListMessage.layoutManager = layoutManager

// private var mAdapter: FirebaseRecyclerAdapter? = null
rcvListMessage.adapter = mAdapter

Remember to call adapter cleanup() method to stop listening for changes in the Firebase database:

override fun onDestroy() {
    super.onDestroy()
    mAdapter!!.cleanup()
}

4. Dependency

build.gradle file (App-level)

dependencies {
    // ...
    implementation 'com.android.support:appcompat-v7:26.0.1'
    implementation 'com.google.firebase:firebase-database:11.0.4'
    implementation 'com.firebaseui:firebase-ui-database:2.3.0'
}

apply plugin: 'com.google.gms.google-services'

II. Practice

1. Goal

We will build an Android App that can:
– create Account, sign in/sign out for Firebase Authentication.
– read/write user to Firebase Realtime Database.
(2 lines above come from this Post).
– write Message item to 2 nodes (/messages/$key and /user-messages/$userid/$key) at the same time, then read list of all Message items. (from this Post)
– display list of Messages using FirebaseUI FirebaseRecyclerAdapter.

kotlin-firebase-db-recycleradapter-demo

2. Technology

– Gradle 3.0.1
– Android Studio 3.x
– Firebase Android SDK 11.x
– Firebase UI Database 2.3.0

3. Project Structure

kotlin-firebase-db-recycleradapter-structure

LoginActivity is for Authentication, then user can enter MessageActivity to send Message to Firebase Realtime Database and show list of Message data.

4. Step by step

4.1 Create Android Project

– Generate new Android Project with package com.javasampleapproach.kotlin.firebase.realtimedb.
– Follow this instruction to add Firebase Auth and Realtime DB.

4.2 Model

package com.javasampleapproach.kotlin.firebase.realtimedb.model

import com.google.firebase.database.IgnoreExtraProperties

@IgnoreExtraProperties
class User {
    var name: String? = null
    var email: String? = null

    constructor() {
        // Default constructor required for calls to DataSnapshot.getValue(User.class)
    }

    constructor(username: String?, email: String?) {
        this.name = username
        this.email = email
    }
}
package com.javasampleapproach.kotlin.firebase.realtimedb.model

import com.google.firebase.database.IgnoreExtraProperties
import com.google.firebase.database.Exclude

@IgnoreExtraProperties
class Message {

    var author: String? = ""
    var body: String? = ""
    var time: String? = ""

    constructor() {
        // Default constructor required for calls to DataSnapshot.getValue(Message.class)
    }

    constructor(author: String, body: String, time: String) {
        this.author = author
        this.body = body
        this.time = time
    }

    @Exclude
    fun toMap(): Map {
        val result = HashMap()
        result.put("author", author!!)
        result.put("body", body!!)
        result.put("time", time!!)

        return result
    }
}

4.3 LoginActivity

In this tutorial, we don’t explain way to authenticate an user again. To know how to implement Firebase Authentication App Client, please visit:
Kotlin Firebase Authentication – How to Sign Up, Sign In, Sign Out, Verify Email | Android

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="3">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tvTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginBottom="16dp"
            android:gravity="center"
            android:text="ozenero.com"
            android:textSize="28sp" />

        <TextView
            android:id="@+id/tvStatus"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="4dp"
            android:text="Signed Out"
            android:textSize="14sp" />

        <TextView
            android:id="@+id/tvDetail"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:padding="4dp"
            android:textSize="14sp"
            tools:text="Firebase User ID: 123456789abc" />

    </LinearLayout>


    <RelativeLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#E0E0E0"
        android:gravity="center_vertical">

        <LinearLayout
            android:id="@+id/email_password_fields"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:paddingLeft="16dp"
            android:paddingRight="16dp">

            <EditText
                android:id="@+id/edtEmail"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:hint="Email"
                android:inputType="textEmailAddress" />

            <EditText
                android:id="@+id/edtPassword"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:hint="Password"
                android:inputType="textPassword" />
        </LinearLayout>

        <LinearLayout
            android:id="@+id/email_password_buttons"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_below="@+id/email_password_fields"
            android:orientation="horizontal"
            android:paddingLeft="16dp"
            android:paddingRight="16dp">

            <Button
                android:id="@+id/btn_email_sign_in"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:layout_weight="1"
                android:text="Sign In" />

            <Button
                android:id="@+id/btn_email_create_account"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:layout_weight="1"
                android:text="Create Account" />
        </LinearLayout>

        <LinearLayout
            android:id="@+id/layout_signed_in_buttons"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:orientation="horizontal"
            android:paddingLeft="16dp"
            android:paddingRight="16dp"
            android:visibility="gone"
            android:weightSum="2.0">

            <Button
                android:id="@+id/btn_sign_out"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:layout_weight="1.0"
                android:text="Sign Out" />

            <Button
                android:id="@+id/btn_test_message"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginLeft="4dp"
                android:layout_marginRight="4dp"
                android:layout_weight="1.0"
                android:text="Test Message" />

        </LinearLayout>

    </RelativeLayout>

</LinearLayout>
package com.javasampleapproach.kotlin.firebase.realtimedb

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import kotlinx.android.synthetic.main.activity_login.*
import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import android.widget.Toast
import android.util.Log
import android.text.TextUtils
import android.content.Intent
import com.google.firebase.database.FirebaseDatabase
import com.javasampleapproach.kotlin.firebase.realtimedb.model.User


class LoginActivity : AppCompatActivity(), View.OnClickListener {

    private val TAG = "LoginActivity"

    private var mAuth: FirebaseAuth? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_login)

        btn_email_sign_in.setOnClickListener(this)
        btn_email_create_account.setOnClickListener(this)
        btn_sign_out.setOnClickListener(this)
        btn_test_message.setOnClickListener(this)

        mAuth = FirebaseAuth.getInstance()
    }

    override fun onStart() {
        super.onStart()

        val currentUser = mAuth!!.currentUser
        updateUI(currentUser)
    }

    override fun onClick(view: View?) {
        val i = view!!.id

        when (i) {
            R.id.btn_email_create_account -> createAccount(edtEmail.text.toString(), edtPassword.text.toString())
            R.id.btn_email_sign_in -> signIn(edtEmail.text.toString(), edtPassword.text.toString())
            R.id.btn_sign_out -> signOut()
            R.id.btn_test_message -> testMessage()
        }
    }

    private fun createAccount(email: String, password: String) {
        Log.e(TAG, "createAccount:" + email)
        if (!validateForm(email, password)) {
            return
        }

        mAuth!!.createUserWithEmailAndPassword(email, password)
                .addOnCompleteListener(this) { task ->
                    if (task.isSuccessful) {
                        Log.e(TAG, "createAccount: Success!")

                        // update UI with the signed-in user's information
                        val user = mAuth!!.currentUser
                        updateUI(user)
                        writeNewUser(user!!.uid, getUsernameFromEmail(user.email), user.email)
                    } else {
                        Log.e(TAG, "createAccount: Fail!", task.exception)
                        Toast.makeText(applicationContext, "Authentication failed!", Toast.LENGTH_SHORT).show()
                        updateUI(null)
                    }
                }
    }

    private fun signIn(email: String, password: String) {
        Log.e(TAG, "signIn:" + email)
        if (!validateForm(email, password)) {
            return
        }

        mAuth!!.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener(this) { task ->
                    if (task.isSuccessful) {
                        Log.e(TAG, "signIn: Success!")

                        // update UI with the signed-in user's information
                        val user = mAuth!!.currentUser
                        updateUI(user)
                    } else {
                        Log.e(TAG, "signIn: Fail!", task.exception)
                        Toast.makeText(applicationContext, "Authentication failed!", Toast.LENGTH_SHORT).show()
                        updateUI(null)
                    }

                    if (!task.isSuccessful) {
                        tvStatus.text = "Authentication failed!"
                    }
                }
    }

    private fun signOut() {
        mAuth!!.signOut()
        updateUI(null)
    }

    private fun validateForm(email: String, password: String): Boolean {

        if (TextUtils.isEmpty(email)) {
            Toast.makeText(applicationContext, "Enter email address!", Toast.LENGTH_SHORT).show()
            return false
        }

        if (TextUtils.isEmpty(password)) {
            Toast.makeText(applicationContext, "Enter password!", Toast.LENGTH_SHORT).show()
            return false
        }

        if (password.length < 6) {
            Toast.makeText(applicationContext, "Password too short, enter minimum 6 characters!", Toast.LENGTH_SHORT).show()
            return false
        }

        return true
    }

    private fun updateUI(user: FirebaseUser?) {

        if (user != null) {
            tvStatus.text = "User Email: " + user.email
            tvDetail.text = "Firebase User ID: " + user.uid

            email_password_buttons.visibility = View.GONE
            email_password_fields.visibility = View.GONE
            layout_signed_in_buttons.visibility = View.VISIBLE
        } else {
            tvStatus.text = "Signed Out"
            tvDetail.text = null

            email_password_buttons.visibility = View.VISIBLE
            email_password_fields.visibility = View.VISIBLE
            layout_signed_in_buttons.visibility = View.GONE
        }
    }

    private fun writeNewUser(userId: String, username: String?, email: String?) {
        val user = User(username, email)

        FirebaseDatabase.getInstance().reference.child("users").child(userId).setValue(user)
    }

    private fun getUsernameFromEmail(email: String?): String {
        return if (email!!.contains("@")) {
            email.split("@".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
        } else {
            email
        }
    }

    private fun testMessage() {
        startActivity(Intent(this, MessageActivity::class.java))
    }
}

4.4 ViewHolder

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    android:paddingBottom="7dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:weightSum="2">

        <TextView
            android:id="@+id/tvAuthorItem"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="author"
            android:textStyle="bold" />

        <TextView
            android:id="@+id/tvTimeItem"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="time" />

    </LinearLayout>

    <TextView
        android:id="@+id/tvBodyItem"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="body"
        android:textColor="@android:color/holo_blue_dark"
        android:textSize="20sp" />
</LinearLayout>
package com.javasampleapproach.kotlin.firebase.realtimedb.viewholder

import android.support.v7.widget.RecyclerView
import android.view.View

import com.javasampleapproach.kotlin.firebase.realtimedb.model.Message
import kotlinx.android.synthetic.main.item_message.view.*

class MessageViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

    fun bindMessage(message: Message?) {
        with(message!!) {
            itemView.tvAuthorItem.text = author
            itemView.tvTimeItem.text = time
            itemView.tvBodyItem.text = body
        }
    }
}

4.5 MessageActivity

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:weightSum="4">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.5"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center_horizontal"
            android:text="ozenero.com"
            android:textSize="28sp" />

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:text="Messages"
            android:textSize="18sp" />
    </LinearLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rcvListMessage"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="2.5"
        android:padding="16dp" />

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#E0E0E0"
        android:gravity="center"
        android:orientation="vertical"
        android:paddingLeft="16dp"
        android:paddingRight="16dp">

        <EditText
            android:id="@+id/edtSentText"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:inputType="text" />

        <LinearLayout
            android:id="@+id/layout_signed_in_buttons"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:gravity="right"
            android:orientation="horizontal"
            android:weightSum="2">

            <Button
                android:id="@+id/btnBack"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.5"
                android:text="Back" />

            <Button
                android:id="@+id/btnSend"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="0.5"
                android:text="Send" />
        </LinearLayout>

    </LinearLayout>

</LinearLayout>
package com.javasampleapproach.kotlin.firebase.realtimedb

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.text.TextUtils
import android.util.Log
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_message.*

import com.google.firebase.auth.FirebaseAuth
import com.google.firebase.auth.FirebaseUser
import com.google.firebase.database.*
import com.javasampleapproach.kotlin.firebase.realtimedb.model.Message

import java.text.SimpleDateFormat
import java.util.Calendar
import com.javasampleapproach.kotlin.firebase.realtimedb.model.User
import com.google.firebase.database.DatabaseError
import com.google.firebase.database.DataSnapshot
import com.google.firebase.database.ChildEventListener
import com.firebase.ui.database.FirebaseRecyclerAdapter
import android.support.v7.widget.LinearLayoutManager
import com.firebase.ui.database.ChangeEventListener
import com.javasampleapproach.kotlin.firebase.realtimedb.viewholder.MessageViewHolder

class MessageActivity : AppCompatActivity() {

    private val TAG = "MessageActivity"
    private val REQUIRED = "Required"

    private var user: FirebaseUser? = null

    private var mDatabase: DatabaseReference? = null
    private var mMessageReference: DatabaseReference? = null
    private var mMessageListener: ChildEventListener? = null

    private var mAdapter: FirebaseRecyclerAdapter? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_message)

        mDatabase = FirebaseDatabase.getInstance().reference
        mMessageReference = FirebaseDatabase.getInstance().getReference("messages")
        user = FirebaseAuth.getInstance().currentUser

        firebaseListenerInit()

        btnSend.setOnClickListener {
            submitMessage()
            edtSentText.setText("")
        }

        btnBack.setOnClickListener {
            finish()
        }

        val layoutManager = LinearLayoutManager(this)
        layoutManager.reverseLayout = false
        rcvListMessage.setHasFixedSize(true)
        rcvListMessage.layoutManager = layoutManager

        val query = mMessageReference!!.limitToLast(8)

        mAdapter = object : FirebaseRecyclerAdapter(
                Message::class.java, R.layout.item_message, MessageViewHolder::class.java, query) {

            override fun populateViewHolder(viewHolder: MessageViewHolder?, model: Message?, position: Int) {
                viewHolder!!.bindMessage(model)
            }

            override fun onChildChanged(type: ChangeEventListener.EventType, snapshot: DataSnapshot?, index: Int, oldIndex: Int) {
                super.onChildChanged(type, snapshot, index, oldIndex)

                rcvListMessage.scrollToPosition(index)
            }
        }

        rcvListMessage.adapter = mAdapter
    }

    private fun firebaseListenerInit() {

        val childEventListener = object : ChildEventListener {

            override fun onChildAdded(dataSnapshot: DataSnapshot?, previousChildName: String?) {
                // A new message has been added
                // onChildAdded() will be called for each node at the first time
                val message = dataSnapshot!!.getValue(Message::class.java)

                Log.e(TAG, "onChildAdded:" + message!!.body)
            }

            override fun onChildChanged(dataSnapshot: DataSnapshot?, previousChildName: String?) {
                Log.e(TAG, "onChildChanged:" + dataSnapshot!!.key)

                // A message has changed
                val message = dataSnapshot.getValue(Message::class.java)
                Toast.makeText(this@MessageActivity, "onChildChanged: " + message!!.body, Toast.LENGTH_SHORT).show()
            }

            override fun onChildRemoved(dataSnapshot: DataSnapshot?) {
                Log.e(TAG, "onChildRemoved:" + dataSnapshot!!.key)

                // A message has been removed
                val message = dataSnapshot.getValue(Message::class.java)
                Toast.makeText(this@MessageActivity, "onChildRemoved: " + message!!.body, Toast.LENGTH_SHORT).show()
            }

            override fun onChildMoved(dataSnapshot: DataSnapshot?, previousChildName: String?) {
                Log.e(TAG, "onChildMoved:" + dataSnapshot!!.key)

                // A message has changed position
                val message = dataSnapshot.getValue(Message::class.java)
                Toast.makeText(this@MessageActivity, "onChildMoved: " + message!!.body, Toast.LENGTH_SHORT).show()
            }

            override fun onCancelled(databaseError: DatabaseError?) {
                Log.e(TAG, "postMessages:onCancelled", databaseError!!.toException())
                Toast.makeText(this@MessageActivity, "Failed to load Message.", Toast.LENGTH_SHORT).show()
            }
        }

        mMessageReference!!.addChildEventListener(childEventListener)

        // copy for removing at onStop()
        mMessageListener = childEventListener
    }

    override fun onStop() {
        super.onStop()

        if (mMessageListener != null) {
            mMessageReference!!.removeEventListener(mMessageListener)
        }
    }

    override fun onDestroy() {
        super.onDestroy()

        mAdapter!!.cleanup()
    }

    private fun submitMessage() {
        val body = edtSentText.text.toString()

        if (TextUtils.isEmpty(body)) {
            edtSentText.error = REQUIRED
            return
        }

        // User data change listener
        mDatabase!!.child("users").child(user!!.uid).addListenerForSingleValueEvent(object : ValueEventListener {
            override fun onDataChange(dataSnapshot: DataSnapshot) {
                val user = dataSnapshot.getValue(User::class.java)

                if (user == null) {
                    Log.e(TAG, "onDataChange: User data is null!")
                    Toast.makeText(this@MessageActivity, "onDataChange: User data is null!", Toast.LENGTH_SHORT).show()
                    return
                }

                writeNewMessage(body)
            }

            override fun onCancelled(error: DatabaseError) {
                // Failed to read value
                Log.e(TAG, "onCancelled: Failed to read user!")
            }
        })
    }

    private fun writeNewMessage(body: String) {
        val time = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(Calendar.getInstance().time)
        val message = Message(getUsernameFromEmail(user!!.email), body, time)

        val messageValues = message.toMap()
        val childUpdates = HashMap()

        val key = mDatabase!!.child("messages").push().key

        childUpdates.put("/messages/" + key, messageValues)
        childUpdates.put("/user-messages/" + user!!.uid + "/" + key, messageValues)

        mDatabase!!.updateChildren(childUpdates)
    }

    private fun getUsernameFromEmail(email: String?): String {
        return if (email!!.contains("@")) {
            email.split("@".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
        } else {
            email
        }
    }
}

4.6 Run & Check result

- Use Android Studio, build and Run your Android App:

kotlin-firebase-db-recycleradapter-demo

- Firebase Console:
kotlin-firebase-db-recycleradapter-console-result

III. Source code

Kotlin-FirebaseRealtimeDB-FirebaseRecyclerAdapter

0 0 votes
Article Rating
Subscribe
Notify of
guest
2.2K Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments