Socket Connection
To connect the user and the doctor in real-time, the SDK uses Pusher to reflect status updates of the consultation.
Initiating the Socket
To initiate the socket, you will need the pusherChannel and pusherAppKey from the Consultation object returned by the API.
import com.altibbi.telehealth.TBISocket
import com.altibbi.telehealth.TBISocketEventListener
import com.altibbi.telehealth.TBISubscribeEventListener
import org.json.JSONObject
// 1. Create a socket instance
val socket = TBISocket()
// 2. Initialize the socket with consultation data
socket.init(
channelName = response.socketChannel!!,
appKey = response.appKey!!,
connectionCallback = object : TBISocketEventListener {
override fun onConnectionStateChange(
previousState: String?,
currentState: String?
) {
if (currentState == "CONNECTED") {
println("Socket connected successfully")
}
}
override fun onError(message: String, code: String?, e: Exception?) {
println("Socket connection error: $message")
}
},
subscribeCallback = object : TBISubscribeEventListener {
override fun onEvent(event: JSONObject) {
// This callback receives ALL events on the channel
println("Received event: ${event.toString()}")
}
override fun onAuthenticationFailure(message: String?, e: Exception?) {
println("Channel authentication failed: $message")
}
override fun onSubscriptionSucceeded(channelName: String) {
println("Subscribed to channel: $channelName")
}
}
)
Listening to Specific Events
While the subscribeCallback in init receives all events, you can also bind to specific events like call-status to handle consultation status transitions.
socket.subscribe("call-status", object : TBISubscribeEventListener {
override fun onEvent(event: JSONObject) {
val status = event.getString("status")
when (status) {
"in_progress" -> {
// Move to live consultation screen
}
"closed" -> {
// Handle consultation completion
}
}
}
override fun onAuthenticationFailure(message: String?, e: Exception?) {
println("Event binding failed: $message")
}
override fun onSubscriptionSucceeded(channelName: String) {
println("Successfully bound to event on: $channelName")
}
})
Next Step (Move to Consultation)
On listening to the events, the event call-status reflects the status of the consultation,
So you need to check the consultation data on event listener and call getConsultationInfo to check the config of the consultation
and once the consultation status is in_progress and the config is ready you can move to the consultation screen
For Chat consultation the data will include the chatConfig object
For Voip consultations the data will include the voipConfig object
For Video consultations the data will include the videoConfig object
Config Classes
import com.google.gson.annotations.SerializedName
data class ChatConfig(
val id: Int?,
@SerializedName("consultation_id") val consultationId: Int?,
@SerializedName("group_id") val groupId: String?,
@SerializedName("app_id") val appId: String?,
@SerializedName("chat_user_id") val chatUserId: String?,
@SerializedName("chat_user_token") val chatUserToken: String?
) {
companion object {
fun fromJson(json: Map<String, Any?>): ChatConfig {
return ChatConfig(
id = json["id"] as? Int,
consultationId = json["consultation_id"] as? Int,
groupId = json["group_id"] as? String,
appId = json["app_id"] as? String,
chatUserId = json["chat_user_id"] as? String,
chatUserToken = json["chat_user_token"] as? String
)
}
}
}
For Both, voipConfig and videoConfig
import com.google.gson.annotations.SerializedName
data class VoipConfig(
val id: Int?,
@SerializedName("consultation_id") val consultationId: Int?,
@SerializedName("api_key") val apiKey: String?,
@SerializedName("call_id") val callId: String?,
@SerializedName("token") val token: String?,
) {
companion object {
fun fromJson(json: Map<String, Any>): VoipConfig {
return VoipConfig(
json["id"] as? Int,
json["consultation_id"] as? Int,
json["api_key"] as? String,
json["call_id"] as? String,
json["token"] as? String
)
}
}
}