CometChat Calls SDK v5 — Custom UI
Overview
Build fully custom call interfaces by hiding the default SDK controls and implementing your own using CallSession actions and event listeners.
Key Imports
import com.cometchat.calls.core.CallSession
import com.cometchat.calls.core.CometChatCalls
import com.cometchat.calls.listeners.MediaEventsListener
import com.cometchat.calls.listeners.SessionStatusListener
import com.cometchat.calls.listeners.ParticipantEventListener
import com.cometchat.calls.model.*
Implementation
1. Hide Default Controls
val sessionSettings = CometChatCalls.SessionSettingsBuilder()
.hideControlPanel(true) // hide entire bottom bar
.hideHeaderPanel(true) // hide top header
// Or hide individual buttons:
// .hideToggleAudioButton(true)
// .hideToggleVideoButton(true)
// .hideParticipantListButton(true)
.build()
2. Custom Control Panel
Create your own buttons and wire them to CallSession actions:
val callSession = CallSession.getInstance()
var isAudioMuted = false
var isVideoPaused = false
btnMute.setOnClickListener {
if (isAudioMuted) callSession.unmuteAudio() else callSession.muteAudio()
}
btnVideo.setOnClickListener {
if (isVideoPaused) callSession.resumeVideo() else callSession.pauseVideo()
}
btnSwitchCamera.setOnClickListener { callSession.switchCamera() }
btnEndCall.setOnClickListener {
callSession.leaveSession()
finish()
}
3. Sync UI with Media Events
callSession.addMediaEventsListener(this, object : MediaEventsListener() {
override fun onAudioMuted() {
runOnUiThread { isAudioMuted = true; btnMute.setImageResource(R.drawable.ic_mic_off) }
}
override fun onAudioUnMuted() {
runOnUiThread { isAudioMuted = false; btnMute.setImageResource(R.drawable.ic_mic_on) }
}
override fun onVideoPaused() {
runOnUiThread { isVideoPaused = true; btnVideo.setImageResource(R.drawable.ic_video_off) }
}
override fun onVideoResumed() {
runOnUiThread { isVideoPaused = false; btnVideo.setImageResource(R.drawable.ic_video_on) }
}
// ... other overrides
})
4. Custom Participant List
Hide default and build with RecyclerView:
// Hide default
.hideParticipantListButton(true)
// Listen for participant updates
callSession.addParticipantEventListener(this, object : ParticipantEventListener() {
override fun onParticipantListChanged(participants: List<Participant>) {
runOnUiThread { adapter.updateParticipants(participants) }
}
// ... other overrides
})
// Participant actions from custom UI
adapter.onMuteClick = { participant -> callSession.muteParticipant(participant.uid) }
adapter.onPauseVideoClick = { participant -> callSession.pauseParticipantVideo(participant.uid) }
adapter.onPinClick = { participant ->
if (participant.isPinned) callSession.unpinParticipant()
else callSession.pinParticipant(participant.uid, "pin")
}
5. Layout Control
// Change layout programmatically
callSession.setLayout(LayoutType.TILE)
callSession.setLayout(LayoutType.SPOTLIGHT)
// Listen for layout changes
callSession.addLayoutListener(this, object : LayoutListener() {
override fun onCallLayoutChanged(layoutType: LayoutType) { /* update UI */ }
override fun onParticipantListVisible() {}
override fun onParticipantListHidden() {}
override fun onPictureInPictureLayoutEnabled() {}
override fun onPictureInPictureLayoutDisabled() {}
})
Available CallSession Actions
| Action | Method |
|---|---|
| Mute/unmute audio | muteAudio(), unmuteAudio() |
| Pause/resume video | pauseVideo(), resumeVideo() |
| Switch camera | switchCamera() |
| Change audio output | setAudioMode(AudioMode) |
| Change layout | setLayout(LayoutType) |
| Start/stop recording | startRecording(), stopRecording() |
| Pin/unpin participant | pinParticipant(uid, type), unpinParticipant() |
| Mute participant | muteParticipant(uid) |
| Pause participant video | pauseParticipantVideo(uid) |
| Leave session | leaveSession() |
| Raise/lower hand | raiseHand(), lowerHand() |
| Enable/disable PiP | enablePictureInPictureLayout(), disablePictureInPictureLayout() |
| Set chat badge count | setChatButtonUnreadCount(count) |
Gotchas
- Always use
MediaEventsListenerto sync your custom UI with actual state runOnUiThread {}is required for UI updates from listener callbackshideControlPanel(true)hides the entire bottom bar — individual hide methods are ignored- The call view container still renders video tiles even with controls hidden
- Use
SessionStatusListenerto handle session end and navigate away
Sample App Reference
CallActivity.kt— Default UI withSessionStatusListenerandButtonClickListener