Advertisement

Responsive Advertisement

Chat Integretion using firebase realtime database

  • First of all your are confident that you have implemented the dependency & the sdk

implementation("com.google.firebase:firebase-database:20.3.1")
  •  First of all you have to create data model class like this
data class FirebaseChatUserModel(
var name: String? = "",
var id: Int? = 0,
var image: String? = "",
var token:String?=""
):Serializable
data class FirebaseChatModel(
var message: String? = "",
var createdAt: Long? = 0,
var media: String? = "",
var from: FirebaseChatUserModel? = null,
var to: FirebaseChatUserModel? = null,
)

  • In your fragment or activity initialize this

    private lateinit var database: FirebaseDatabase
    private var receiverDetails: FirebaseChatUserModel? = null
    private var chatList: ArrayList<FirebaseChatModel> = ArrayList()
    private var senderChatReference: DatabaseReference? = null
    private var messaageAdapter: MessageAdapter? = null
    private var receiverChatReference: DatabaseReference? = null


  • This will come when you come from another activity or fragment to current fragment , which consists of that user to want chat.

    requireArguments().getSerializable(CommonUtils.USER_MODEL)?.let {
    receiverDetails = it as FirebaseChatUserModel
    }
  • In onCreate , first of all , call this function.
    private fun doFirebaseSetup() {
    database = FirebaseDatabase.getInstance()
    senderChatReference =
    database.reference.child(CommonUtils.MESSAGES).child(model.user!!.id.toString())
    .child(receiverDetails?.id.toString())
    receiverChatReference =
    database.reference.child(CommonUtils.MESSAGES).child(receiverDetails?.id.toString())
    .child(model.user!!.id.toString())
    firebaseDataListeners()
    }
  • then paste the code of firebasedatalisteners
    private fun firebaseDataListeners() {
    senderChatReference?.addChildEventListener(chatAddedListener)
    receiverChatReference?.addChildEventListener(chatReadListener)
    }
  • Then paste the variable chataddedlistner & chatreadlistener
    private val chatReadListener = object : ChildEventListener {
    @SuppressLint("SuspiciousIndentation")
    override fun onChildAdded(
    snapshot: DataSnapshot, previousChildName: String?,
    ) {
    val chatOtherUser = snapshot.getValue(FirebaseChatModel::class.java)
    if (chatOtherUser?.to?.id.toString() == model.user?.id.toString()) receiverChatReference?.child(
    snapshot.key ?: ""
    )
    }

    override fun onChildChanged(
    snapshot: DataSnapshot, previousChildName: String?,
    ) {
    }

    override fun onChildRemoved(snapshot: DataSnapshot) {
    }

    override fun onChildMoved(
    snapshot: DataSnapshot, previousChildName: String?,
    ) {
    }

    override fun onCancelled(error: DatabaseError) {
    }

    }

    private val chatAddedListener = object : ChildEventListener {
    override fun onChildAdded(snapshot: DataSnapshot, previousChildName: String?) {
    try {
    val chat = snapshot.getValue(FirebaseChatModel::class.java)
    chat?.let {
    val position = chatList.size
    chatList.add(chat)
    messaageAdapter?.notifyItemInserted(position)
    }
    binding.rvChatView.smoothScrollToPosition(chatList.size - 1)
    binding.progressBar.visibility=View.GONE
    } catch (e: Exception) {
    showToast(e.localizedMessage ?: "")
    }
    }

    override fun onChildChanged(snapshot: DataSnapshot, previousChildName: String?) {
    TODO("Not yet implemented")
    }

    override fun onChildRemoved(snapshot: DataSnapshot) {
    TODO("Not yet implemented")
    }

    override fun onChildMoved(snapshot: DataSnapshot, previousChildName: String?) {
    TODO("Not yet implemented")
    }

    override fun onCancelled(error: DatabaseError) {
    TODO("Not yet implemented")
    }
    }
  • add this method in onDestroy lifecycle
    override fun onDestroy() {
    senderChatReference?.removeEventListener(chatAddedListener)
    receiverChatReference?.removeEventListener(chatReadListener)
    super.onDestroy()
    }
  • set adapter if you have and call in onCreate method
    private fun setAdapter() {
    messaageAdapter = MessageAdapter(
    requireContext(), model.user?.id!!.toInt(), chatList
    )
    binding.rvChatView.adapter = messaageAdapter
    }
  • on send button paste this method
    private fun createAndSendChatMessage() {
    binding.progressBar.visibility=View.VISIBLE
    senderChatReference?.push()?.setValue(getChatModel())?.addOnCompleteListener {
    receiverChatReference?.push()?.setValue(getChatModel())?.addOnCompleteListener {

    val firebaseMessageReq = FirebaseMessageReq().apply {
    to = receiverDetails?.token
    data = Data(
    title = receiverDetails?.name,
    body = binding.edtMessage.text.toString(),
    type = "Chat",
    receiver = FirebaseChatUserModel(
    name = model.user?.name,
    id=model.user?.id,
    image = model.user?.image,
    token = model.sharedPreference.getString(CommonUtils.FCM_TOKEN)
    )
    )
    }
    model.sendNotification(firebaseMessageReq){}
    binding.progressBar.visibility=View.GONE
    binding.edtMessage.text = null
    binding.edtMessage.isEnabled = true
    }
    }
    }
  • here i have taken model.sendnotification for api call and request for push notification. Comment down if you dont want. 
  • Here is the adapter class 


    import android.content.Context
    import android.view.LayoutInflater
    import android.view.ViewGroup
    import androidx.recyclerview.widget.RecyclerView
    import com.infoicon.epcproject.databinding.MessageIncomingBinding
    import com.infoicon.epcproject.databinding.MessageOutgoingBinding
    import com.infoicon.epcproject.models.firebasedatabase.FirebaseChatModel
    import com.infoicon.epcproject.utils.CommonUtils
    import com.infoicon.epcproject.utils.TimeAgo2

    class MessageAdapter(
    private val context: Context,
    private var currentUserId: Int,
    private val chatlist: List<FirebaseChatModel> // Changed to List for better performance
    ) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
    private lateinit var timeConverted: String
    private val itemSend = 1
    private val itemReceive = 2
    private lateinit var myFinalValue: String


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
    return when (viewType) {
    itemSend -> {
    val binding = MessageOutgoingBinding.inflate(
    LayoutInflater.from(parent.context),
    parent,
    false
    )
    OutgoingMessageViewHolder(binding)
    }

    else -> {
    val binding = MessageIncomingBinding.inflate(
    LayoutInflater.from(parent.context),
    parent,
    false
    )
    IncomingMessageViewHolder(binding)
    }
    }
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
    val item = chatlist[position]
    when (holder) {
    is OutgoingMessageViewHolder -> holder.bind(item)
    is IncomingMessageViewHolder -> holder.bind(item)
    }
    }

    override fun getItemCount(): Int {
    return chatlist.size
    }

    override fun getItemViewType(position: Int): Int {
    val item = chatlist[position]
    return if (item.from?.id == currentUserId) itemSend else itemReceive
    }

    inner class OutgoingMessageViewHolder(private val binding: MessageOutgoingBinding) :
    RecyclerView.ViewHolder(binding.root) {
    fun bind(item: FirebaseChatModel) {
    binding.txtMessage.text = item.message
    timeConverted = CommonUtils.convertLongToTime(item.createdAt!!)
    myFinalValue = TimeAgo2.convertTimeToText(context,timeConverted).toString()
    binding.txtTimeStamp.text = myFinalValue
    }
    }

    inner class IncomingMessageViewHolder(private val binding: MessageIncomingBinding) :
    RecyclerView.ViewHolder(binding.root) {
    fun bind(item: FirebaseChatModel) {
    val context=binding.root.context
    binding.txtMessage.text = item.message
    binding.txtTimeStamp.text =
    TimeAgo2.convertTimeToText(context,CommonUtils.convertLongToTime(item.createdAt!!))
    .toString()
    }
    }
    }



  • here is the message_outgoing layout
    <?xml version="1.0" encoding="utf-8"?>

    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:paddingEnd="@dimen/_12sdp"
    android:paddingStart="@dimen/_35sdp"
    android:layout_height="wrap_content">

    <TextView
    android:id="@+id/txtMessage"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="@dimen/_5sdp"
    android:layout_marginBottom="@dimen/_5sdp"
    android:background="@drawable/background_outgoing_message"
    android:fontFamily="@font/segoeui"
    android:padding="@dimen/_12sdp"
    android:textAlignment="textEnd"
    app:layout_constraintHorizontal_bias="1"
    android:text="Hello, this is an outgoing message. afasasfasf"
    android:textColor="@color/white"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

    <TextView
    android:id="@+id/txtTimeStamp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="timestamp"
    android:fontFamily="@font/seguisb"

    android:layout_marginEnd="@dimen/_3sdp"
    android:textSize="@dimen/_10sdp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="@+id/txtMessage"
    app:layout_constraintTop_toBottomOf="@+id/txtMessage" />
    </androidx.constraintlayout.widget.ConstraintLayout>

  • here is the message_incoming layout 
    <?xml version="1.0" encoding="utf-8"?>

    <androidx.constraintlayout.widget.ConstraintLayout 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:paddingStart="@dimen/_12sdp"
    android:paddingEnd="@dimen/_35sdp"
    android:layout_height="wrap_content">

    <TextView
    android:id="@+id/txtMessage"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintHorizontal_bias="0"
    android:background="@drawable/background_incoming_message"
    android:fontFamily="@font/segoeui"
    android:padding="@dimen/_12sdp"
    android:text="Hello, this is an incoming message."
    android:textColor="#000000"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />

    <TextView
    android:id="@+id/txtTimeStamp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:fontFamily="@font/seguisb"
    android:text="timestamp"
    android:textSize="@dimen/_10sdp"
    android:layout_marginStart="@dimen/_3sdp"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintStart_toStartOf="@+id/txtMessage"
    app:layout_constraintTop_toBottomOf="@+id/txtMessage" />
    </androidx.constraintlayout.widget.ConstraintLayout>


  • here is the mainlayout
    <?xml version="1.0" encoding="utf-8"?>
    <layout 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">

    <androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:animateLayoutChanges="true"
    tools:context=".fragments.FacilityMessageFragment">

    <!-- RecyclerView for displaying chat messages -->
    <androidx.recyclerview.widget.RecyclerView
    android:id="@+id/rvChatView"
    android:layout_width="0dp"
    android:layout_height="0dp"
    android:layout_marginBottom="@dimen/_4sdp"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
    app:layout_constraintBottom_toTopOf="@+id/btnSend"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent" />


    <ProgressBar
    android:id="@+id/progress_bar"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHorizontal_bias="0.5"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintVertical_bias="0.5" />

    <ImageView
    android:id="@+id/btnSend"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@drawable/custom_trans_background"
    android:src="@drawable/sendbtn"
    app:layout_constraintBottom_toBottomOf="@+id/edtMessage"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toEndOf="@+id/edtMessage"
    app:layout_constraintTop_toTopOf="@+id/edtMessage" />

    <EditText
    android:id="@+id/edtMessage"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="@dimen/_7sdp"
    android:layout_marginBottom="@dimen/_7sdp"
    android:background="@drawable/background_green_corner"
    android:hint="@string/enter_your_message"
    android:paddingHorizontal="@dimen/_12sdp"
    android:paddingVertical="@dimen/_14sdp"
    android:singleLine="true"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintEnd_toStartOf="@+id/btnSend"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/rvChatView" />

    </androidx.constraintlayout.widget.ConstraintLayout>
    </layout>
  • here is the api call if you want push notification
    fun sendNotification(
    firebaseMessageReq: FirebaseMessageReq,
    result: (FirebaseResponse) -> Unit
    ) {

    viewModelScope.launch {
    executeApiRequest({ getFirebaseClient().sendNotification(firebaseMessageReq) },
    { result(it) },
    { throwable ->
    val errorMessage = throwable.localizedMessage ?: "Unknown error occurred"
    CommonUtils.showToast(errorMessage)
    retrofitError.postValue(errorRetrofit(throwable))
    })
    }
    } data class FirebaseResponse( val failure: Int?, val success: Int? )
    data class FirebaseMessageReq(
    var `data`: Data?=null,
    var to: String?=null
    )

    data class Data(
    var body: String?=null,
    var title: String?=null,
    var type: String?=null,
    var receiver: FirebaseChatUserModel?=null
    )
  • in your api calling interface add this
    @POST(ApiEndPoints.FIREBASE_END_POINTS)
    suspend fun sendNotification(
    @Body firebaseMessageReq: FirebaseMessageReq
    ): Response<FirebaseResponse>
  • your retrofit function will be
    
    
    const val FIREBASE_URL = "https://fcm.googleapis.com/"
    fun
    getFirebaseClient(): RetrofitUrl {
    val client = getOkHttpClient(false)
    return Retrofit.Builder().baseUrl(FIREBASE_URL).client(client)
    .addConverterFactory(GsonConverterFactory.create()).build()
    .create(RetrofitUrl::class.java)
    }


  • in your header, in interceptor
    add this,
    header(CommonUtils.AUTHORIZATION, BEARER + ApiEndPoints.SERVER_KEY)
    //here server key will be from firebase and bearer is "Bearer"
  • your messagingservice file will be


    import android.app.Notification
    import android.app.NotificationChannel
    import android.app.NotificationManager
    import android.app.PendingIntent
    import android.content.Context
    import android.content.Intent
    import android.graphics.BitmapFactory
    import android.graphics.Color
    import android.media.RingtoneManager
    import android.os.Build
    import android.os.PowerManager
    import android.util.Log
    import androidx.annotation.RequiresApi
    import androidx.core.app.NotificationCompat
    import com.google.firebase.messaging.FirebaseMessagingService
    import com.google.firebase.messaging.RemoteMessage
    import com.infoicon.epcproject.R
    import com.infoicon.epcproject.activity.HomeActivity
    import com.infoicon.epcproject.models.NotificationData
    import com.infoicon.epcproject.models.firebasedatabase.FirebaseChatUserModel
    import com.infoicon.epcproject.utils.CommonUtils
    import org.json.JSONObject
    import java.util.Locale

    class MyFirebaseMessagingService : FirebaseMessagingService() {

    private var wakeLock: PowerManager.WakeLock? = null

    override fun onNewToken(token: String) {
    super.onNewToken(token)
    }

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onMessageReceived(message: RemoteMessage) {
    super.onMessageReceived(message)
    val notificationData = message.data.let { data ->
    val receiverData = data["receiver"]
    Log.e(CommonUtils.TAG, "onMessageReceived: $receiverData")

    val receiverModel = if (receiverData != null) {
    // Parse receiver data here and create FirebaseChatUserModel object
    // For example:
    val receiverJson = JSONObject(receiverData)
    FirebaseChatUserModel(
    id = receiverJson.getInt("id"),
    name = receiverJson.getString("name"),
    token = receiverJson.getString("token")
    // Add other fields as needed
    )
    } else {
    null
    }

    NotificationData(
    type = data["type"] ?: "default",
    title = data["title"] ?: "No Title",
    body = data["body"] ?: "No Body",
    receiver = receiverModel
    )
    }
    Log.e(CommonUtils.TAG, "onMessageReceived: $notificationData")

    if (notificationData.type.isNotEmpty()) {
    createNotificationChannel(notificationData.type)
    createNotification(notificationData, applicationContext)
    }
    }

    private fun createNotificationChannel(channelId: String) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    val name = "Channel ${
    channelId.replaceFirstChar {
    if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString()
    }
    }"
    val descriptionText = "Description for channel $channelId"
    val importance = NotificationManager.IMPORTANCE_HIGH
    val channel = NotificationChannel(channelId, name, importance).apply {
    description = descriptionText
    enableVibration(true)
    enableLights(true)
    lightColor = Color.GREEN
    lockscreenVisibility = Notification.VISIBILITY_PUBLIC
    }
    val notificationManager =
    getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    notificationManager.createNotificationChannel(channel)
    }
    }

    @RequiresApi(Build.VERSION_CODES.O)
    private fun createNotification(notificationData: NotificationData, context: Context) {
    val notificationIntent = Intent(context, HomeActivity::class.java).apply {
    flags = Intent.FLAG_ACTIVITY_FORWARD_RESULT or Intent.FLAG_ACTIVITY_CLEAR_TOP
    putExtra("type", notificationData.type)
    putExtra("receiver",notificationData.receiver)
    }
    val pendingIntent = PendingIntent.getActivity(
    context,
    0,
    notificationIntent,
    PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
    )
    //val vibrationPattern = longArrayOf(0, 1000, 500, 1000) // Define your custom vibration pattern here
    val bigTextStyle = NotificationCompat.BigTextStyle()
    .bigText(notificationData.body)
    // Acquire wake lock
    acquireWakeLock(context)
    val alarmSound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
    val notificationBuilder = NotificationCompat.Builder(context, notificationData.type)
    .setSmallIcon(R.drawable.logo1)
    .setContentTitle(notificationData.title)
    .setContentText(notificationData.body)
    .setLights(Color.GREEN, 1000, 300)
    .setPriority(NotificationCompat.PRIORITY_HIGH)
    .setLargeIcon(BitmapFactory.decodeResource(context.resources, R.drawable.logo1))
    .setContentIntent(pendingIntent)
    .setAutoCancel(true)
    .setSound(alarmSound)
    .setStyle(bigTextStyle)

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    notificationBuilder.setChannelId(notificationData.type)
    }

    val notification = notificationBuilder.build()

    val notificationManager =
    context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    notificationManager.notify(notificationData.type.hashCode(), notification)
    }

    private fun acquireWakeLock(context: Context) {
    val powerManager = context.getSystemService(Context.POWER_SERVICE) as PowerManager
    wakeLock = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
    powerManager.newWakeLock(
    PowerManager.FULL_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP or PowerManager.ON_AFTER_RELEASE,
    "MyApp::MyWakelockTag"
    )
    } else {
    powerManager.newWakeLock(
    PowerManager.PARTIAL_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP or PowerManager.ON_AFTER_RELEASE,
    "MyApp::MyWakelockTag"
    )
    }
    wakeLock?.acquire(10 * 1000L /*10 seconds*/)
    }

    override fun onDestroy() {
    super.onDestroy()
    // Release wake lock
    wakeLock?.release()
    }
    }

  • add permission in this mainfest
    <uses-permission android:name="android.permission.WAKE_LOCK"/>
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    <uses-permission android:name="android.permission.INTERNET" />
  • also add your service file in application body in manifest.xml
    <service
    android:name=".receiver.MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
    <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
    </service>
  • now handle the received notification and redirect to that activity and pass data through intent or bundle 
    fun checkNotificationTap() {
    lifecycleScope.launch {
    val dataFromNotification = intent.getStringExtra("type")
    // Log.e(TAG, "checkNotificationTap: $dataFromNotification", )
    val receiverDetails = intent?.getSerializableExtra("receiver") as? FirebaseChatUserModel
    if (dataFromNotification != null) {
    when (dataFromNotification) {
    "Chat" -> {
    updateFragmentLabel(
    R.id.facilityMessageFragment,
    receiverDetails?.name ?: ""
    )
    navController.navigate(
    R.id.facilityMessageFragment,
    bundleOf(CommonUtils.USER_MODEL to receiverDetails)
    )
    }

    "review" -> {
    navController.navigate(R.id.ratingReviewsFragment)
    }

    "default" -> {
    navController.navigate(R.id.notficationFragment)
    }
    }
    intent.removeExtra("type")
    }
    }
    }
  • want to get firebase token use this
    private fun initFirebaseToken() {
    lifecycleScope.launch {
    FirebaseMessaging.getInstance().token
    .addOnSuccessListener { token ->
    // Log.e(TAG, "FCM Token: $token")
    homeViewModel.sharedPreference.putString(FCM_TOKEN,token)
    // Handle the token here
    }
    }

    }

Post a Comment

0 Comments