Skip to main content
Add real-time messaging to your call experience using CometChat UI Kit. This allows participants to send text messages, share files, and communicate via chat while on a call.

Overview

In-call chat creates a group conversation linked to the call session. When participants tap the chat button, they can:
  • Send and receive text messages
  • Share images, files, and media
  • See message history from the current call
  • Get unread message notifications via badge count

Prerequisites

  • CometChat Calls SDK integrated (Setup)
  • CometChat Chat SDK integrated (Chat SDK)
  • CometChat UI Kit integrated (UI Kit)
The Chat SDK and UI Kit are separate from the Calls SDK. You’ll need to add both dependencies to your project.

Step 1: Add UI Kit Dependency

Add the CometChat UI Kit to your build.gradle:
dependencies {
    implementation 'com.cometchat:chat-uikit-android:4.+'
}

Step 2: Enable Chat Button

Configure session settings to show the chat button:
val sessionSettings = CometChatCalls.SessionSettingsBuilder()
    .hideChatButton(false)  // Show the chat button
    .build()

Step 3: Create Chat Group

Create or join a CometChat group using the session ID as the group GUID. This links the chat to the specific call session.
private fun setupChatGroup(sessionId: String, meetingName: String) {
    // Try to get existing group first
    CometChat.getGroup(sessionId, object : CometChat.CallbackListener<Group>() {
        override fun onSuccess(group: Group) {
            if (!group.isJoined) {
                // Join the existing group
                joinGroup(sessionId, group.groupType)
            } else {
                Log.d(TAG, "Already joined group: ${group.name}")
            }
        }

        override fun onError(e: CometChatException) {
            if (e.code == "ERR_GUID_NOT_FOUND") {
                // Group doesn't exist, create it
                createGroup(sessionId, meetingName)
            } else {
                Log.e(TAG, "Error getting group: ${e.message}")
            }
        }
    })
}

private fun createGroup(guid: String, name: String) {
    val group = Group(guid, name, CometChatConstants.GROUP_TYPE_PUBLIC, null)
    
    CometChat.createGroup(group, object : CometChat.CallbackListener<Group>() {
        override fun onSuccess(createdGroup: Group) {
            Log.d(TAG, "Group created: ${createdGroup.name}")
        }

        override fun onError(e: CometChatException) {
            Log.e(TAG, "Group creation failed: ${e.message}")
        }
    })
}

private fun joinGroup(guid: String, groupType: String) {
    CometChat.joinGroup(guid, groupType, null, object : CometChat.CallbackListener<Group>() {
        override fun onSuccess(joinedGroup: Group) {
            Log.d(TAG, "Joined group: ${joinedGroup.name}")
        }

        override fun onError(e: CometChatException) {
            Log.e(TAG, "Join group failed: ${e.message}")
        }
    })
}

Step 4: Handle Chat Button Click

Listen for the chat button click and open your chat activity:
private var unreadMessageCount = 0

private fun setupChatButtonListener() {
    val callSession = CallSession.getInstance()
    
    callSession.addButtonClickListener(this, object : ButtonClickListener() {
        override fun onChatButtonClicked() {
            // Reset unread count when opening chat
            unreadMessageCount = 0
            callSession.setChatButtonUnreadCount(0)
            
            // Open chat activity
            val intent = Intent(this@CallActivity, ChatActivity::class.java).apply {
                putExtra("SESSION_ID", sessionId)
                putExtra("MEETING_NAME", meetingName)
            }
            startActivity(intent)
        }
    })
}

Step 5: Track Unread Messages

Listen for incoming messages and update the badge count on the chat button:
private fun setupMessageListener() {
    CometChat.addMessageListener(TAG, object : CometChat.MessageListener() {
        override fun onTextMessageReceived(textMessage: TextMessage) {
            // Check if message is for our call's group
            val receiver = textMessage.receiver
            if (receiver is Group && receiver.guid == sessionId) {
                unreadMessageCount++
                CallSession.getInstance().setChatButtonUnreadCount(unreadMessageCount)
            }
        }

        override fun onMediaMessageReceived(mediaMessage: MediaMessage) {
            val receiver = mediaMessage.receiver
            if (receiver is Group && receiver.guid == sessionId) {
                unreadMessageCount++
                CallSession.getInstance().setChatButtonUnreadCount(unreadMessageCount)
            }
        }
    })
}

override fun onDestroy() {
    super.onDestroy()
    CometChat.removeMessageListener(TAG)
}

Step 6: Create Chat Activity

Create a chat activity using UI Kit components:
<?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:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.cometchat.chatuikit.messageheader.CometChatMessageHeader
        android:id="@+id/message_header"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent" />

    <com.cometchat.chatuikit.messagelist.CometChatMessageList
        android:id="@+id/message_list"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toBottomOf="@id/message_header"
        app:layout_constraintBottom_toTopOf="@id/message_composer"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />

    <com.cometchat.chatuikit.messagecomposer.CometChatMessageComposer
        android:id="@+id/message_composer"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent" />

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

</androidx.constraintlayout.widget.ConstraintLayout>
class ChatActivity : AppCompatActivity() {

    private lateinit var messageList: CometChatMessageList
    private lateinit var messageComposer: CometChatMessageComposer
    private lateinit var messageHeader: CometChatMessageHeader
    private lateinit var progressBar: ProgressBar

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

        messageList = findViewById(R.id.message_list)
        messageComposer = findViewById(R.id.message_composer)
        messageHeader = findViewById(R.id.message_header)
        progressBar = findViewById(R.id.progress_bar)

        val sessionId = intent.getStringExtra("SESSION_ID") ?: return
        val meetingName = intent.getStringExtra("MEETING_NAME") ?: "Chat"

        loadGroup(sessionId, meetingName)
    }

    private fun loadGroup(guid: String, meetingName: String) {
        progressBar.visibility = View.VISIBLE

        CometChat.getGroup(guid, object : CometChat.CallbackListener<Group>() {
            override fun onSuccess(group: Group) {
                if (!group.isJoined) {
                    joinAndSetGroup(guid, group.groupType)
                } else {
                    setGroup(group)
                }
            }

            override fun onError(e: CometChatException) {
                if (e.code == "ERR_GUID_NOT_FOUND") {
                    createAndSetGroup(guid, meetingName)
                } else {
                    progressBar.visibility = View.GONE
                    Log.e(TAG, "Error: ${e.message}")
                }
            }
        })
    }

    private fun createAndSetGroup(guid: String, name: String) {
        val group = Group(guid, name, CometChatConstants.GROUP_TYPE_PUBLIC, null)
        CometChat.createGroup(group, object : CometChat.CallbackListener<Group>() {
            override fun onSuccess(createdGroup: Group) {
                setGroup(createdGroup)
            }

            override fun onError(e: CometChatException) {
                progressBar.visibility = View.GONE
            }
        })
    }

    private fun joinAndSetGroup(guid: String, groupType: String) {
        CometChat.joinGroup(guid, groupType, null, object : CometChat.CallbackListener<Group>() {
            override fun onSuccess(joinedGroup: Group) {
                setGroup(joinedGroup)
            }

            override fun onError(e: CometChatException) {
                progressBar.visibility = View.GONE
            }
        })
    }

    private fun setGroup(group: Group) {
        progressBar.visibility = View.GONE
        
        messageList.setGroup(group)
        messageComposer.setGroup(group)
        messageHeader.setGroup(group)

        // Hide auxiliary buttons (call, video) since we're already in a call
        messageHeader.setAuxiliaryButtonView { _, _, _ ->
            View(this).apply { visibility = View.GONE }
        }
    }

    companion object {
        private const val TAG = "ChatActivity"
    }
}

Complete Example

Here’s the complete CallActivity with in-call chat integration:
class CallActivity : AppCompatActivity() {

    private lateinit var callSession: CallSession
    private var sessionId: String = ""
    private var meetingName: String = ""
    private var unreadMessageCount = 0

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

        sessionId = intent.getStringExtra("SESSION_ID") ?: return
        meetingName = intent.getStringExtra("MEETING_NAME") ?: "Meeting"

        callSession = CallSession.getInstance()

        // Setup chat group for this call
        setupChatGroup(sessionId, meetingName)
        
        // Listen for chat button clicks
        setupChatButtonListener()
        
        // Track incoming messages for badge
        setupMessageListener()
        
        // Join the call
        joinCall()
    }

    private fun setupChatGroup(sessionId: String, meetingName: String) {
        CometChat.getGroup(sessionId, object : CometChat.CallbackListener<Group>() {
            override fun onSuccess(group: Group) {
                if (!group.isJoined) {
                    CometChat.joinGroup(sessionId, group.groupType, null, 
                        object : CometChat.CallbackListener<Group>() {
                            override fun onSuccess(g: Group) {}
                            override fun onError(e: CometChatException) {}
                        })
                }
            }

            override fun onError(e: CometChatException) {
                if (e.code == "ERR_GUID_NOT_FOUND") {
                    val group = Group(sessionId, meetingName, 
                        CometChatConstants.GROUP_TYPE_PUBLIC, null)
                    CometChat.createGroup(group, object : CometChat.CallbackListener<Group>() {
                        override fun onSuccess(g: Group) {}
                        override fun onError(e: CometChatException) {}
                    })
                }
            }
        })
    }

    private fun setupChatButtonListener() {
        callSession.addButtonClickListener(this, object : ButtonClickListener() {
            override fun onChatButtonClicked() {
                unreadMessageCount = 0
                callSession.setChatButtonUnreadCount(0)
                
                startActivity(Intent(this@CallActivity, ChatActivity::class.java).apply {
                    putExtra("SESSION_ID", sessionId)
                    putExtra("MEETING_NAME", meetingName)
                })
            }
        })
    }

    private fun setupMessageListener() {
        CometChat.addMessageListener(TAG, object : CometChat.MessageListener() {
            override fun onTextMessageReceived(textMessage: TextMessage) {
                val receiver = textMessage.receiver
                if (receiver is Group && receiver.guid == sessionId) {
                    unreadMessageCount++
                    callSession.setChatButtonUnreadCount(unreadMessageCount)
                }
            }
        })
    }

    private fun joinCall() {
        val container = findViewById<FrameLayout>(R.id.callContainer)
        
        val sessionSettings = CometChatCalls.SessionSettingsBuilder()
            .setTitle(meetingName)
            .hideChatButton(false)
            .build()

        CometChatCalls.joinSession(
            sessionId = sessionId,
            sessionSettings = sessionSettings,
            view = container,
            context = this,
            listener = object : CometChatCalls.CallbackListener<CallSession>() {
                override fun onSuccess(session: CallSession) {
                    Log.d(TAG, "Joined call")
                }

                override fun onError(e: CometChatException) {
                    Log.e(TAG, "Join failed: ${e.message}")
                }
            }
        )
    }

    override fun onDestroy() {
        super.onDestroy()
        CometChat.removeMessageListener(TAG)
    }

    companion object {
        private const val TAG = "CallActivity"
    }
}