Chat using PubSub - Android
room.pubSub lets participants send messages to each other on named topics. Anyone subscribed to a topic receives what is published on it. Chat is one use; you can also use it to signal actions, such as asking a participant to unmute.
Now let's see how to use PubSub to implement chat. If you are not familiar with the PubSub mechanism and pubSub class, you can follow this guide.
publish(), subscribe() and unsubscribe() require a joined room. They are suspend functions, so call them from a coroutine. Each returns once the server has answered. It throws a VideoSDKException when the room is not joined, is RECONNECTING, or the server call fails. The examples below open the chat screen while the user is in the room, so the room is already joined. If you subscribe as part of your join flow, do it from onRoomJoined().
Implementing Chat
Group Chat
- Choose the topic every participant publishes and subscribes to for chat messages. This guide uses
CHAT. - On the send button, publish the message that the sender typed in the
EditTextfield.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.widget.Toolbar
import live.videosdk.rtc.android.Room
import live.videosdk.rtc.android.lib.VideoSDKException
import live.videosdk.rtc.android.listeners.PubSubMessageListener
import live.videosdk.rtc.android.model.PubSubPublishOptions
import android.text.TextUtils
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
class ChatActivity : AppCompatActivity() {
private lateinit var etmessage: EditText
// Room
var room: Room? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat)
etmessage = findViewById(R.id.etMessage)
/**
* Here, we have created 'MainApplication' class, which extends android.app.Application class.
* It has a Room property and getter and setter methods of the Room property.
* In your android manifest, you must declare the class implementing android.app.Application
* (add the android:name=".MainApplication" attribute to the existing application tag):
* In MainActivity.kt, we have set the Room property.
*
* For Example: (MainActivity.kt)
* val room = VideoSDK.createRoom(roomId = roomId, token = token, participantName = participantName, micEnabled = micEnabled, camEnabled = camEnabled)
* (this.application as MainApplication).room = room
*/
// Get Room
room = (this.application as MainApplication).room
findViewById<View>(R.id.btnSend).setOnClickListener { sendMessage() }
}
private fun sendMessage() {
// get message from EditText
val message: String = etmessage.getText().toString()
if (!TextUtils.isEmpty(message)) {
val publishOptions = PubSubPublishOptions(persist = true)
lifecycleScope.launch {
try {
// Sending the Message using the publish method
room!!.pubSub.publish("CHAT", message, publishOptions)
// Clearing the message input
etmessage.setText("")
} catch (e: VideoSDKException) {
// Room is not joined yet, is reconnecting, or the message could not be sent
Toast.makeText(
this@ChatActivity, "Can't send right now, please try again",
Toast.LENGTH_SHORT
).show()
}
}
} else {
Toast.makeText(
this@ChatActivity, "Please Enter Message",
Toast.LENGTH_SHORT
).show()
}
}
}
- Next step would be to display the messages others send. For this we have to
subscribeto that topic i.eCHATand display all the messages.
class ChatActivity : AppCompatActivity() {
// PubSubMessageListener
val pubSubMessageListener = object : PubSubMessageListener {
override fun onMessageReceived(message: PubSubMessage) {
Log.d("VideoSDK", "onMessageReceived: ${message.message}")
}
override fun onOldMessagesReceived(messages: List<PubSubMessage>, info: PubSubHistoryInfo) {
// Persisted message list, delivered in batches
Log.d("VideoSDK", "onOldMessagesReceived: $messages")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat)
//...
// Subscribe for 'CHAT' topic
lifecycleScope.launch {
try {
room!!.pubSub.subscribe("CHAT", pubSubMessageListener)
} catch (e: VideoSDKException) {
// Room is not joined yet, is reconnecting, or the subscription failed
Log.d("VideoSDK", "subscribe failed: ${e.code} ${e.message}")
}
}
}
}
- The final step is to unsubscribe from the topic once you no longer need it. Here we unsubscribe from the
CHATtopic when the activity is destroyed.
If the activity is destroyed after the room has been left, or while it is RECONNECTING, unsubscribe() throws a VideoSDKException. onDestroy() often runs after the room is left, so guard the call as shown below. lifecycleScope is already cancelled when onDestroy() runs, so the example calls unsubscribe() on a scope that outlives the activity.
class ChatActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_chat)
//...
}
override fun onDestroy() {
super.onDestroy()
val room = room ?: return
// Unsubscribe for 'CHAT' topic
MainScope().launch {
try {
room.pubSub.unsubscribe("CHAT", pubSubMessageListener)
} catch (e: VideoSDKException) {
// Room already left, is reconnecting, or the unsubscribe failed
Log.d("VideoSDK", "unsubscribe failed: ${e.code} ${e.message}")
}
}
}
}
Private Chat
To turn the example above into a private chat between two participants, pass the sendOnly parameter in PubSubPublishOptions.
class ChatActivity : AppCompatActivity() {
//..
private fun sendMessage() {
// get message from EditText
val message: String = etmessage.getText().toString()
if (!TextUtils.isEmpty(message)) {
// Pass the participantId of the participant to whom you want to send the message.
val sendOnly: Array<String> = arrayOf("xyz")
val publishOptions = PubSubPublishOptions(persist = true, sendOnly = sendOnly)
lifecycleScope.launch {
try {
// Sending the Message using the publish method
room!!.pubSub.publish("CHAT", message, publishOptions)
// Clearing the message input
etmessage.setText("")
} catch (e: VideoSDKException) {
// Room is not joined yet, is reconnecting, or the message could not be sent
Toast.makeText(
this@ChatActivity, "Can't send right now, please try again",
Toast.LENGTH_SHORT
).show()
}
}
} else {
Toast.makeText(
this@ChatActivity, "Please Enter Message",
Toast.LENGTH_SHORT
).show()
}
}
}
Handling failures
A failed chat call throws a VideoSDKException. Every code below except 3065 is also reported through onError on the RoomEventListener.
3035(ERROR_ACTION_PERFORMED_BEFORE_ROOM_JOINED): the room is not joined yet.3065(ERROR_ROOM_RECONNECTING): the room is reconnecting. The message is not queued; send it again once the room isCONNECTED.4087(PUBSUB_PUBLISH_FAILED):publish()could not send the message, for example because the connection dropped before the server acknowledged it.4088(PUBSUB_SUBSCRIBE_FAILED):subscribe()failed. Nothing is registered, so subscribe again once the room is connected.4089(PUBSUB_UNSUBSCRIBE_FAILED):unsubscribe()failed. The listener stays subscribed.
subscribe() also throws 3073 (ERROR_INVALID_PARAMETER) when its options are invalid, or when this listener is already subscribed to the topic. 3073 is only thrown, never reported on onError. A failed or cancelled subscribe() registers nothing, and a failed unsubscribe() keeps the listener.
Downloading Chat Messages
All the messages from the PubSub that were published with persist : true can be downloaded as a .csv file. This file will be available in the VideoSDK dashboard as well as through the Sessions API.
API Reference
The API references for all the methods and events utilised in this guide are provided below.
Got a Question? Ask us on discord

