Skip to main content
{
  "component": "CometChatConversations",
  "package": "com.cometchat.chatuikit.conversations",
  "xmlElement": "<com.cometchat.chatuikit.conversations.CometChatConversations />",
  "description": "Scrollable list of recent one-on-one and group conversations for the logged-in user.",
  "primaryOutput": {
    "method": "setOnItemClick",
    "type": "OnItemClick<Conversation>"
  },
  "methods": {
    "data": {
      "setConversationsRequestBuilder": {
        "type": "ConversationsRequest.ConversationsRequestBuilder",
        "default": "SDK default (30 per page)",
        "note": "Pass the builder, not the result of .build()"
      },
      "setDateTimeFormatter": {
        "type": "DateTimeFormatterCallback",
        "default": "Component default (hh:mm a today, Yesterday, dd MMM yyyy)"
      },
      "setDateFormat": {
        "type": "SimpleDateFormat",
        "default": "Component default"
      }
    },
    "callbacks": {
      "setOnItemClick": "OnItemClick<Conversation>",
      "setOnItemLongClick": "OnItemLongClick<Conversation>",
      "setOnBackPressListener": "OnBackPress",
      "setOnSelect": "OnSelection<Conversation>",
      "setOnError": "OnError",
      "setOnLoad": "OnLoad<Conversation>",
      "setOnEmpty": "OnEmpty",
      "setOnSearchClickListener": "OnSearchClick"
    },
    "visibility": {
      "setBackIconVisibility": { "type": "int (View.VISIBLE | View.GONE)", "default": "View.GONE" },
      "setToolbarVisibility": { "type": "int", "default": "View.VISIBLE" },
      "setLoadingStateVisibility": { "type": "int", "default": "View.VISIBLE" },
      "setDeleteConversationOptionVisibility": { "type": "int", "default": "View.VISIBLE" },
      "setErrorStateVisibility": { "type": "int", "default": "View.VISIBLE" },
      "setEmptyStateVisibility": { "type": "int", "default": "View.VISIBLE" },
      "setSeparatorVisibility": { "type": "int", "default": "View.VISIBLE" },
      "setUserStatusVisibility": { "type": "int", "default": "View.VISIBLE" },
      "setGroupTypeVisibility": { "type": "int", "default": "View.VISIBLE" },
      "setReceiptsVisibility": { "type": "int", "default": "View.VISIBLE" },
      "setSearchBoxVisibility": { "type": "int", "default": "View.VISIBLE" }
    },
    "sound": {
      "disableSoundForMessages": { "type": "boolean", "default": false },
      "setCustomSoundForMessages": { "type": "@RawRes int", "default": "built-in" }
    },
    "selection": {
      "setSelectionMode": {
        "type": "UIKitConstants.SelectionMode",
        "values": ["NONE", "SINGLE", "MULTIPLE"],
        "default": "NONE"
      }
    },
    "viewSlots": {
      "setItemView": "ConversationsViewHolderListener — entire list item row",
      "setLeadingView": "ConversationsViewHolderListener — avatar / left section",
      "setTitleView": "ConversationsViewHolderListener — name / title text",
      "setSubtitleView": "ConversationsViewHolderListener — last message preview",
      "setTrailingView": "ConversationsViewHolderListener — timestamp / badge / right section",
      "setLoadingView": "@LayoutRes int — loading spinner",
      "setEmptyView": "@LayoutRes int — empty state",
      "setErrorView": "@LayoutRes int — error state",
      "setOverflowMenu": "View — toolbar menu",
      "setOptions": "Function2<Context, Conversation, List<MenuItem>> — long-press context menu (replaces defaults)",
      "addOptions": "Function2<Context, Conversation, List<MenuItem>> — long-press context menu (appends to defaults)"
    },
    "formatting": {
      "setTextFormatters": {
        "type": "List<CometChatTextFormatter>",
        "default": "default formatters from data source"
      }
    },
    "advanced": {
      "selectConversation": "Conversation, SelectionMode — programmatic selection",
      "clearSelection": "void — clears all selected conversations",
      "getSelectedConversations": "List<Conversation> — returns selected items",
      "getRecyclerView": "RecyclerView — internal RecyclerView access",
      "getViewModel": "ConversationsViewModel — internal ViewModel access",
      "getConversationsAdapter": "ConversationsAdapter — internal adapter access",
      "setAdapter": "ConversationsAdapter — replace the default adapter",
      "getBinding": "CometchatConversationsListViewBinding — root ViewBinding",
      "hideReceipts": "boolean — hide/show read receipts at adapter level",
      "setMentionAllLabelId": "String id, String label — mention-all label"
    },
    "style": {
      "setStyle": {
        "type": "@StyleRes int",
        "parent": "CometChatConversationsStyle"
      }
    }
  },
  "events": [
    {
      "name": "CometChatConversationEvents.ccConversationDeleted",
      "payload": "Conversation",
      "description": "Conversation deleted from list"
    }
  ],
  "sdkListeners": [
    "onTextMessageReceived",
    "onMediaMessageReceived",
    "onCustomMessageReceived",
    "onTypingStarted",
    "onTypingEnded",
    "onMessagesDelivered",
    "onMessagesRead",
    "onMessagesDeliveredToAll",
    "onMessagesReadByAll",
    "onUserOnline",
    "onUserOffline",
    "onGroupMemberJoined",
    "onGroupMemberLeft",
    "onGroupMemberKicked",
    "onGroupMemberBanned",
    "onMemberAddedToGroup"
  ],
  "compositionExample": {
    "description": "Sidebar conversations wired to message view",
    "components": [
      "CometChatConversations",
      "CometChatMessageHeader",
      "CometChatMessageList",
      "CometChatMessageComposer"
    ],
    "flow": "setOnItemClick emits Conversation -> extract User/Group via getConversationWith() -> pass to MessageHeader, MessageList, MessageComposer"
  }
}

Where It Fits

CometChatConversations is a list component. It renders recent conversations and emits the selected Conversation via setOnItemClick. Wire it to CometChatMessageHeader, CometChatMessageList, and CometChatMessageComposer to build a standard chat layout.
ChatActivity.kt
class ChatActivity : AppCompatActivity() {

    private lateinit var conversations: CometChatConversations
    private lateinit var messageHeader: CometChatMessageHeader
    private lateinit var messageList: CometChatMessageList
    private lateinit var messageComposer: CometChatMessageComposer

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

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

        conversations.setOnItemClick { view, position, conversation ->
            if (conversation.conversationType == CometChatConstants.CONVERSATION_TYPE_USER) {
                val user = conversation.conversationWith as User
                messageHeader.setUser(user)
                messageList.setUser(user)
                messageComposer.setUser(user)
            } else {
                val group = conversation.conversationWith as Group
                messageHeader.setGroup(group)
                messageList.setGroup(group)
                messageComposer.setGroup(group)
            }
        }
    }
}
On phones, you’d typically use separate Activities instead of a side-by-side layout — see the Conversation List + Message View getting started guide.

Quick Start

Add the component to your layout XML:
layout_activity.xml
<com.cometchat.chatuikit.conversations.CometChatConversations
        android:id="@+id/conversation"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
Prerequisites: CometChat SDK initialized with CometChatUIKit.init(), a user logged in, and the cometchat-chat-uikit-android dependency added. To add programmatically in an Activity:
YourActivity.kt
 override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(CometChatConversations(this))
}
Or in a Fragment:
YourFragment.kt
 override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View {
    return CometChatConversations(requireContext())
}

Filtering Conversations

Pass a ConversationsRequest.ConversationsRequestBuilder to setConversationsRequestBuilder. Pass the builder instance — not the result of .build().
val builder = ConversationsRequest.ConversationsRequestBuilder()
    builder.setConversationType(CometChatConstants.CONVERSATION_TYPE_USER)
    builder.setLimit(50)

cometChatConversations.setConversationsRequestBuilder(builder)

Filter Recipes

RecipeCode
Only user conversationsbuilder.setConversationType(CometChatConstants.CONVERSATION_TYPE_USER)
Only group conversationsbuilder.setConversationType(CometChatConstants.CONVERSATION_TYPE_GROUP)
Limit to 10 per pagebuilder.setLimit(10)
With specific tagsbuilder.setTags(Arrays.asList("vip"))
Filter by user tagsbuilder.withUserAndGroupTags(true); builder.setUserTags(Arrays.asList("premium"))
Filter by group tagsbuilder.withUserAndGroupTags(true); builder.setGroupTags(Arrays.asList("support"))
Default page size is 30. The component uses infinite scroll — the next page loads as the user scrolls to the bottom. Refer to ConversationsRequestBuilder for the full builder API.

Actions and Events

Callback Methods

setOnItemClick

Fires when a conversation row is tapped. Primary navigation hook — set the active conversation and render the message view.
YourActivity.kt
cometchatConversations.onItemClick = OnItemClick { view, position, conversation ->
            
    }
What this does: Replaces the default item-click behavior. When a user taps a conversation, your custom lambda executes instead of the built-in navigation.

setOnItemLongClick

Fires when a conversation row is long-pressed. Use for additional actions like delete or select.
YourActivity.kt
cometchatConversations.onItemLongClick = OnItemLongClick({ view, position, conversation ->
            
    })
What this does: Replaces the default long-press behavior. When a user long-presses a conversation, your custom lambda executes.

setOnBackPressListener

Fires when the user presses the back button in the app bar. Default: navigates to the previous activity.
YourActivity.kt
cometchatConversations.onBackPressListener = OnBackPress {

    }
What this does: Overrides the default back-press navigation. When the user taps the back button, your custom logic runs instead.

setOnSelect

Fires when a conversation is checked/unchecked in multi-select mode. Requires setSelectionMode to be set.
YourActivity.kt
cometchatConversations.setOnSelect(object : OnSelection<Conversation?> {
        override fun onSelection(t: MutableList<Conversation?>?) {
                
        }
    })
What this does: Registers a callback that fires when the user selects one or more conversations. The callback receives the list of selected Conversation objects.

setOnError

Fires on internal errors (network failure, auth issue, SDK exception).
YourActivity.kt
cometchatConversations.setOnError {

    }
What this does: Registers an error listener. If the component encounters an error (e.g., network failure), your callback receives the CometChatException.

setOnLoad

Fires when the list is successfully fetched and loaded.
YourActivity.kt
cometchatConversations.setOnLoad(object : OnLoad<Conversation?> {
    override fun onLoad(list: MutableList<Conversation?>?) {

    }
})
What this does: Registers a callback that fires after the conversation list is fetched and rendered. The callback receives the list of loaded Conversation objects.

setOnEmpty

Fires when the list is empty, enabling custom handling such as showing a placeholder.
YourActivity.kt
cometchatConversations.setOnEmpty{
            
    }
What this does: Registers a callback that fires when the conversation list has no items. Use this to show a custom empty-state message or trigger other logic.

setOnSearchClickListener

Fires when the user taps the search icon in the toolbar.
YourActivity.kt
cometchatConversations.setOnSearchClickListener {

    }
What this does: Overrides the default search icon tap behavior. When the user taps the search icon, your custom logic runs instead.
  • Verify: After setting an action callback, trigger the corresponding user interaction (tap, long-press, back, select, search) and confirm your custom logic executes instead of the default behavior.

Global UI Events

CometChatConversationEvents emits events subscribable from anywhere in the application. Add a listener and remove it when no longer needed.
EventFires whenPayload
ccConversationDeletedA conversation is deleted from the listConversation
Add Listener
CometChatConversationEvents.addListener("LISTENER_TAG", object : CometChatConversationEvents() {
    override fun ccConversationDeleted(conversation: Conversation) {
        super.ccConversationDeleted(conversation)
    }
})
Remove Listener
 CometChatConversationEvents.removeListener("LISTENER_TAG");

SDK Events (Real-Time, Automatic)

The component listens to these SDK events internally. No manual attachment needed unless additional side effects are required.
SDK ListenerInternal behavior
onTextMessageReceived / onMediaMessageReceived / onCustomMessageReceivedMoves conversation to top, updates last message preview and unread count
onTypingStarted / onTypingEndedShows/hides typing indicator in the subtitle
onMessagesDelivered / onMessagesRead / onMessagesDeliveredToAll / onMessagesReadByAllUpdates receipt ticks (unless setReceiptsVisibility(View.GONE))
onUserOnline / onUserOfflineUpdates online/offline status dot (unless setUserStatusVisibility(View.GONE))
onGroupMemberJoined / onGroupMemberLeft / onGroupMemberKicked / onGroupMemberBanned / onMemberAddedToGroupUpdates group conversation metadata
Automatic: new messages, typing indicators, receipts, user presence, group membership changes. Manual: deleting a conversation via the SDK directly (not through the component’s context menu) requires emitting CometChatConversationEvents.ccConversationDeleted for the UI to update.

Functionality

Small functional customizations such as toggling visibility of UI elements, setting custom sounds, and configuring selection modes.
MethodsDescriptionCode
setBackIconVisibilityToggles visibility for the back button in the app bar.setBackIconVisibility(View.VISIBLE);
setToolbarVisibilityToggles visibility for the toolbar in the app bar.setToolbarVisibility(View.GONE);
setLoadingStateVisibilityHides the loading state while fetching conversations.setLoadingStateVisibility(View.GONE);
setDeleteConversationOptionVisibilityToggles visibility for the delete option on long press.setDeleteConversationOptionVisibility(View.GONE);
setErrorStateVisibilityHides the error state on fetching conversations.setErrorStateVisibility(View.GONE);
setEmptyStateVisibilityHides the empty state on fetching conversations.setEmptyStateVisibility(View.GONE);
setSeparatorVisibilityControls visibility of separators in the list view.setSeparatorVisibility(View.GONE);
setUserStatusVisibilityControls visibility of the online status indicator.setUserStatusVisibility(View.GONE);
setGroupTypeVisibilityControls visibility of the group type indicator.setGroupTypeVisibility(View.GONE);
setReceiptsVisibilityHides receipts shown in the subtitle without disabling read/delivered marking.setReceiptsVisibility(View.GONE);
setSearchBoxVisibilityControls visibility of the search box in the toolbar.setSearchBoxVisibility(View.GONE);
setSearchInputEndIconVisibilityControls visibility of the end icon in the search bar.setSearchInputEndIconVisibility(View.GONE);
hideReceiptsHides read receipts in the conversation list (adapter-level).hideReceipts(true);
disableSoundForMessagesDisables sound notifications for incoming messages.disableSoundForMessages(true);
setCustomSoundForMessagesSets a custom sound file for incoming message notifications.setCustomSoundForMessages(com.cometchat.chatuikit.R.raw.cometchat_beep2);
setSelectionModeDetermines the selection mode (single or multiple).setSelectionMode(UIKitConstants.SelectionMode.MULTIPLE);
setSearchInputTextSets the text in the search input field.setSearchInputText("Sample Text");
setSearchPlaceholderTextSets the placeholder text for the search input field.setSearchPlaceholderText("Enter search term");
  • Verify: After calling a visibility method, confirm the corresponding UI element is shown or hidden. After calling disableSoundForMessages(true), confirm no sound plays on incoming messages.

Custom View Slots

Each slot replaces a section of the default UI. Slots that accept a Conversation parameter receive the conversation object for that row via the ConversationsViewHolderListener pattern (createView + bindView).
SlotMethodReplaces
Leading viewsetLeadingView(ConversationsViewHolderListener)Avatar / left section
Title viewsetTitleView(ConversationsViewHolderListener)Name / title text
Subtitle viewsetSubtitleView(ConversationsViewHolderListener)Last message preview
Trailing viewsetTrailingView(ConversationsViewHolderListener)Timestamp / badge / right section
Item viewsetItemView(ConversationsViewHolderListener)Entire list item row
Loading viewsetLoadingView(@LayoutRes int)Loading spinner
Empty viewsetEmptyView(@LayoutRes int)Empty state
Error viewsetErrorView(@LayoutRes int)Error state
Overflow menusetOverflowMenu(View)Toolbar menu
Options (replace)setOptions(Function2)Long-press context menu (replaces defaults)
Options (append)addOptions(Function2)Long-press context menu (appends to defaults)

setLeadingView

Replace the avatar / left section. Typing-aware avatar example.
        cometchatConversations.setLeadingView(object :
            ConversationsViewHolderListener() {
            override fun createView(
                context: Context?,
                listItem: CometchatConversationsListItemsBinding?
            ): View? {
                return null
            }

            override fun bindView(
                context: Context,
                createdView: View,
                conversation: Conversation,
                holder: RecyclerView.ViewHolder,
                conversationList: List<Conversation>,
                position: Int
            ) {
            }
        })
What this does: Registers a ConversationsViewHolderListener that provides a custom view for the leading (left) area of each conversation item. createView inflates your layout, and bindView populates it with conversation data.
The following example shows a custom leading view with a chat-dots icon for typing indicators and an avatar with status indicator:
drawable/chat_dots.xml
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="48dp"
    android:height="48dp"
    android:viewportWidth="48"
    android:viewportHeight="48">
  <path
      android:pathData="M5,15.5C5,11.358 8.358,8 12.5,8H36C40.142,8 43.5,11.358 43.5,15.5V26.535C43.5,30.677 40.142,34.035 36,34.035H16.149C15.47,34.035 14.82,34.311 14.349,34.8L8.44,40.931C7.191,42.227 5,41.342 5,39.543V15.5Z"
      android:strokeLineJoin="round"
      android:strokeWidth="2.5"
      android:fillColor="#DCD7F6"
      android:strokeColor="#141414"/>
  <path
      android:pathData="M18.25,22.75C19.078,22.75 19.75,22.078 19.75,21.25C19.75,20.421 19.078,19.75 18.25,19.75C17.421,19.75 16.75,20.421 16.75,21.25C16.75,22.078 17.421,22.75 18.25,22.75ZM24.5,22.75C25.329,22.75 26,22.078 26,21.25C26,20.421 25.329,19.75 24.5,19.75C23.671,19.75 23,20.421 23,21.25C23,22.078 23.671,22.75 24.5,22.75ZM31,22.75C31.829,22.75 32.5,22.078 32.5,21.25C32.5,20.421 31.829,19.75 31,19.75C30.171,19.75 29.5,20.421 29.5,21.25C29.5,22.078 30.171,22.75 31,22.75Z"
      android:strokeWidth="0.5"
      android:fillColor="#141414"
      android:strokeColor="#141414"/>
</vector>
What this does: Defines a vector drawable of a chat bubble with three dots, used as a typing indicator icon in the custom leading view.
Create a leading_view.xml custom layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/conversation_leading_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true">

        <ImageView
            android:id="@+id/leading_iv"
            android:layout_width="@dimen/cometchat_48dp"
            android:layout_height="@dimen/cometchat_48dp"
            android:src="@drawable/chat_dots"
            android:visibility="gone"
            tools:ignore="MissingConstraints" />

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/leading_view"
            android:layout_width="@dimen/cometchat_48dp"
            android:layout_height="@dimen/cometchat_48dp"
            tools:ignore="MissingConstraints">

            <com.cometchat.chatuikit.shared.views.avatar.CometChatAvatar
                android:id="@+id/conversations_avatar"
                android:layout_width="@dimen/cometchat_48dp"
                android:layout_height="@dimen/cometchat_48dp" />

            <com.cometchat.chatuikit.shared.views.statusindicator.CometChatStatusIndicator
                android:id="@+id/status_and_type_indicator"
                android:layout_width="@dimen/cometchat_15dp"
                android:layout_height="@dimen/cometchat_15dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent" />
        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>

</LinearLayout>
Inflate and bind with typing indicator tracking:
        val typingIndicatorHashMap = HashMap<String, Boolean>()
        CometChat.addMessageListener(System.currentTimeMillis().toString() + "", object : MessageListener() {
            override fun onTypingStarted(typingIndicator: TypingIndicator) {
                if (typingIndicator.receiverType == CometChatConstants.RECEIVER_TYPE_USER) {
                    if (typingIndicatorHashMap.containsKey(typingIndicator.sender.uid)) {
                        return
                    }
                    typingIndicatorHashMap[typingIndicator.sender.uid] = true
                } else {
                    if (typingIndicatorHashMap.containsKey(typingIndicator.receiverId)) {
                        return
                    }
                    typingIndicatorHashMap[typingIndicator.receiverId] = true
                }
            }

            override fun onTypingEnded(typingIndicator: TypingIndicator) {
                if (typingIndicator.receiverType == CometChatConstants.RECEIVER_TYPE_USER) {
                    typingIndicatorHashMap.remove(typingIndicator.sender.uid)
                } else {
                    typingIndicatorHashMap.remove(typingIndicator.receiverId)
                }
            }
        })

        binding.cometchatConversations.setLeadingView(object : ConversationsViewHolderListener() {
            override fun createView(context: Context?, listItem: CometchatConversationsListItemsBinding?): View {
                return LayoutInflater.from(context).inflate(R.layout.leading_view, null)
            }

            override fun bindView(
                context: Context,
                createdView: View,
                conversation: Conversation,
                holder: RecyclerView.ViewHolder,
                conversationList: List<Conversation>,
                position: Int
            ) {
                val imageView = createdView.findViewById<ImageView>(R.id.leading_iv)
                val constraintLayout = createdView.findViewById<ConstraintLayout>(R.id.leading_view)
                val avatar = createdView.findViewById<CometChatAvatar>(R.id.conversations_avatar)
                val statusIndicator = createdView.findViewById<CometChatStatusIndicator>(R.id.status_and_type_indicator)

                avatar.setAvatar(ConversationsUtils.getConversationTitle(conversation), ConversationsUtils.getConversationAvatar(conversation))

                if (conversation.conversationType == CometChatConstants.RECEIVER_TYPE_USER) {
                    if ((conversation.conversationWith as User).status.equals(CometChatConstants.USER_STATUS_ONLINE, ignoreCase = true)) {
                        if (!Utils.isBlocked((conversation.conversationWith as User))) {
                            statusIndicator.statusIndicator = StatusIndicator.ONLINE
                        } else {
                            statusIndicator.statusIndicator = StatusIndicator.OFFLINE
                        }
                    } else {
                        statusIndicator.statusIndicator = StatusIndicator.OFFLINE
                    }
                    if (typingIndicatorHashMap.containsKey((conversation.conversationWith as User).uid)) {
                        imageView.visibility = View.VISIBLE
                        constraintLayout.visibility = View.GONE
                    } else {
                        imageView.visibility = View.GONE
                        constraintLayout.visibility = View.VISIBLE
                    }
                } else {
                    if (typingIndicatorHashMap.containsKey((conversation.conversationWith as Group).guid)) {
                        imageView.visibility = View.VISIBLE
                        constraintLayout.visibility = View.GONE
                    } else {
                        imageView.visibility = View.GONE
                        constraintLayout.visibility = View.VISIBLE
                    }
                }
            }
        })

setTrailingView

Replace the timestamp / badge / right section.
cometchatConversations.setTrailingView(object :
    ConversationsViewHolderListener() {
    override fun createView(
        context: Context?,
        listItem: CometchatConversationsListItemsBinding?
    ): View? {
        return null
    }

    override fun bindView(
        context: Context,
        createdView: View,
        conversation: Conversation,
        holder: RecyclerView.ViewHolder,
        conversationList: List<Conversation>,
        position: Int
    ) {
    }
})
Relative time badge example: Create a custom_tail_view.xml custom layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <com.google.android.material.card.MaterialCardView
        android:id="@+id/card"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:elevation="@dimen/cometchat_0dp"
        app:cardCornerRadius="@dimen/cometchat_6dp"
        app:cardElevation="@dimen/cometchat_0dp">

        <LinearLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:padding="@dimen/cometchat_5dp">

            <TextView
                android:id="@+id/hours"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:textAppearance="?attr/cometchatTextAppearanceHeading4Bold" />

            <TextView
                android:id="@+id/time_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textAppearance="?attr/cometchatTextAppearanceHeading4Bold" />

        </LinearLayout>

    </com.google.android.material.card.MaterialCardView>

</LinearLayout>
What this does: Defines a custom trailing view layout with a MaterialCardView containing two TextView elements for displaying the time value and unit (e.g., “5” and “Min ago”).
Inflate and bind with color-coded time badges:
cometchatConversations.setTrailingView(object : ConversationsViewHolderListener() {
            override fun createView(context: Context?, listItem: CometchatConversationsListItemsBinding?): View {
                return LayoutInflater.from(context).inflate(R.layout.custom_tail_view, null)
            }

            override fun bindView(
                context: Context,
                createdView: View,
                conversation: Conversation,
                holder: RecyclerView.ViewHolder,
                conversationList: List<Conversation>,
                position: Int
            ) {
                val card = createdView.findViewById<MaterialCardView>(R.id.card)
                val tvHours = createdView.findViewById<TextView>(R.id.hours)
                val tvMessage = createdView.findViewById<TextView>(R.id.time_title)

                var timestamp = conversation.updatedAt * 1000

                if (timestamp.toString().length == 10) {
                    timestamp *= 1000
                }

                val now = Calendar.getInstance()
                val lastSeen = Calendar.getInstance()
                lastSeen.timeInMillis = timestamp

                val diffInMillis = now.timeInMillis - lastSeen.timeInMillis
                val diffInMinutes = TimeUnit.MILLISECONDS.toMinutes(diffInMillis)
                val diffInHours = TimeUnit.MILLISECONDS.toHours(diffInMillis)

                if (diffInMinutes == 0L) {
                    tvHours.text = "1"
                    tvMessage.text = "Min ago"
                    card.setCardBackgroundColor(Utils.applyColorWithAlphaValue(Color.parseColor("#6852D6"), 40))
                    tvMessage.setTextColor(Color.parseColor("#6852D6"))
                    tvHours.setTextColor(Color.parseColor("#6852D6"))
                } else if (diffInMinutes < 60) {
                    tvHours.text = diffInMinutes.toString()
                    tvMessage.text = "Min ago"
                    card.setCardBackgroundColor(Utils.multiplyColorAlpha(Color.parseColor("#6852D6"), 40))
                    tvMessage.setTextColor(Color.parseColor("#6852D6"))
                    tvHours.setTextColor(Color.parseColor("#6852D6"))
                } else if (diffInHours < 10) {
                    tvHours.text = diffInHours.toString()
                    tvMessage.text = "Hr ago"
                    tvMessage.setTextColor(Color.parseColor("#FFAB00"))
                    tvHours.setTextColor(Color.parseColor("#FFAB00"))
                    card.setCardBackgroundColor(Utils.multiplyColorAlpha(Color.parseColor("#FFAB00"), 40))
                } else if (diffInHours < 1000) {
                    tvHours.text = diffInHours.toString()
                    tvMessage.text = "Hr ago"
                    tvMessage.setTextColor(Color.parseColor("#F44649"))
                    tvHours.setTextColor(Color.parseColor("#F44649"))
                    card.setCardBackgroundColor(Utils.multiplyColorAlpha(Color.parseColor("#F44649"), 40))
                }
            }
        })

setTitleView

Replace the name / title text.
cometchatConversations.setTitleView(object :
    ConversationsViewHolderListener() {
    override fun createView(
        context: Context?,
        listItem: CometchatConversationsListItemsBinding?
    ): View? {
        return null
    }

    override fun bindView(
        context: Context,
        createdView: View,
        conversation: Conversation,
        holder: RecyclerView.ViewHolder,
        conversationList: List<Conversation>,
        position: Int
    ) {
    }
})
Inline user status example: Create a custom_title_view.xml layout:
custom_title_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:gravity="center_vertical">

    <TextView
        android:id="@+id/name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?attr/cometchatTextAppearanceHeading4Bold"
        android:maxLines="1"
        android:ellipsize="end" />

    <TextView
        android:id="@+id/status"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="4dp"
        android:textAppearance="?attr/cometchatTextAppearanceCaption1Regular"
        android:textColor="?attr/cometchatTextColorSecondary"
        android:maxLines="1"
        android:ellipsize="end"
        android:visibility="gone" />

</LinearLayout>
What this does: Defines a custom title layout with the conversation name and an inline user status message.
cometchatConversations.setTitleView(object : ConversationsViewHolderListener() {
            override fun createView(context: Context?, listItem: CometchatConversationsListItemsBinding?): View {
                return LayoutInflater.from(context).inflate(R.layout.custom_title_view, null)
            }

            override fun bindView(
                context: Context,
                createdView: View,
                conversation: Conversation,
                holder: RecyclerView.ViewHolder,
                conversationList: List<Conversation>,
                position: Int
            ) {
                val name = createdView.findViewById<TextView>(R.id.name)
                val status = createdView.findViewById<TextView>(R.id.status)

                name.text = ConversationsUtils.getConversationTitle(conversation)
                if (conversation.conversationType == CometChatConstants.RECEIVER_TYPE_USER) {
                    status.visibility = View.VISIBLE
                    status.text = "• " + (conversation.conversationWith as User).statusMessage
                } else {
                    status.visibility = View.GONE
                }
            }
        })
What this does: Registers a ConversationsViewHolderListener that provides a custom title view for each conversation item. The example inflates a layout with the conversation name and an inline user status message. For group conversations, the status is hidden.

setSubtitleView

Replace the last message preview text.
cometchatConversations.setSubtitleView(object :
    ConversationsViewHolderListener() {
    override fun createView(
        context: Context?,
        listItem: CometchatConversationsListItemsBinding?
    ): View? {
        return null
    }

    override fun bindView(
        context: Context,
        createdView: View,
        conversation: Conversation,
        holder: RecyclerView.ViewHolder,
        conversationList: List<Conversation>,
        position: Int
    ) {
    }
})
Example with last-active timestamp:
    cometchatConversations.setSubtitleView(object :
            ConversationsViewHolderListener() {
            override fun createView(
                context: Context?,
                listItem: CometchatConversationsListItemsBinding?
            ): View {
                return TextView(context)
            }

            override fun bindView(
                context: Context,
                createdView: View,
                conversation: Conversation,
                holder: RecyclerView.ViewHolder,
                conversationList: List<Conversation>,
                position: Int
            ) {
                val tvSubtitle = createdView as TextView
                tvSubtitle.text =
                    "Last Active at: " + SimpleDateFormat("dd/MM/yyyy, HH:mm:ss").format(
                        conversation.updatedAt * 1000
                    )
                tvSubtitle.setTextColor(Color.BLACK)
            }
        })
What this does: Registers a ConversationsViewHolderListener that provides a custom subtitle view for each conversation item. The example creates a TextView showing the last active timestamp formatted as “dd/MM/yyyy, HH:mm:ss”.

setItemView

Replace the entire list item row.
cometchatConversations.setItemView(object :
    ConversationsViewHolderListener() {
    override fun createView(
        context: Context?,
        listItem: CometchatConversationsListItemsBinding?
    ): View? {
        return null
    }

    override fun bindView(
        context: Context,
        createdView: View,
        conversation: Conversation,
        holder: RecyclerView.ViewHolder,
        conversationList: List<Conversation>,
        position: Int
    ) {
    }
})
Example with compact layout:
Create an item_converation_list.xml custom layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginStart="@dimen/cometchat_padding_4"
        android:layout_marginBottom="@dimen/cometchat_padding_3"
        android:layout_marginEnd="@dimen/cometchat_padding_4"
        android:layout_marginTop="@dimen/cometchat_padding_3"
        android:gravity="center_vertical"
        android:orientation="horizontal">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/conversation_leading_view"
            android:layout_width="@dimen/cometchat_45dp"
            android:layout_height="@dimen/cometchat_45dp">

            <com.cometchat.chatuikit.shared.views.avatar.CometChatAvatar
                android:id="@+id/custom_avatar"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                app:cometchatAvatarPlaceHolderTextAppearance="@style/CometChatTextAppearanceHeading4.Bold"
                app:cometchatAvatarStrokeRadius="@dimen/cometchat_8dp" />

            <com.cometchat.chatuikit.shared.views.statusindicator.CometChatStatusIndicator
                android:id="@+id/status_and_type_indicator"
                android:layout_width="@dimen/cometchat_12dp"
                android:layout_height="@dimen/cometchat_12dp"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent" />

        </androidx.constraintlayout.widget.ConstraintLayout>

        <TextView
            android:id="@+id/tvName"
            android:layout_width="0dp"
            android:textAppearance="@style/CometChatTextAppearanceHeading4.Medium"
            android:layout_marginStart="@dimen/cometchat_margin_3"
            android:layout_height="wrap_content"
            android:layout_weight="1" />

        <TextView
            android:id="@+id/tvDate"
            android:textAppearance="@style/CometChatTextAppearanceCaption1.Regular"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
    </LinearLayout>

    <View
        android:layout_width="match_parent"
        android:layout_height="1dp"
        android:background="?attr/cometchatStrokeColorLight" />

</LinearLayout>
What this does: Defines a custom list item layout with a CometChatAvatar, status indicator, conversation name, and date — providing a compact, single-line conversation item design.
Inflate the XML and bind:
        cometchatConversations.setItemView(object :
            ConversationsViewHolderListener() {
            override fun createView(
                context: Context?,
                listItem: CometchatConversationsListItemsBinding?
            ): View {
                return LayoutInflater.from(context)
                    .inflate(R.layout.custom_list_item_view, null, false)
            }

            override fun bindView(
                context: Context,
                createdView: View,
                conversation: Conversation,
                holder: RecyclerView.ViewHolder,
                conversationList: List<Conversation>,
                position: Int
            ) {
                val avatar = createdView.findViewById<CometChatAvatar>(R.id.custom_avatar)
                val title = createdView.findViewById<TextView>(R.id.tvName)
                val tvDate = createdView.findViewById<TextView>(R.id.tvDate)

                val name = ConversationsUtils.getConversationTitle(conversation)
                title.text = name
                avatar.setStyle(com.cometchat.chatuikit.R.style.CometChatAvatarStyle)
                avatar.avatarPlaceHolderTextAppearance =
                    com.cometchat.chatuikit.R.style.CometChatTextAppearanceHeading4_Bold
                avatar.setAvatar(name, ConversationsUtils.getConversationAvatar(conversation))
                val simpleDateFormat = SimpleDateFormat("hh:mm a")
                val date = simpleDateFormat.format(conversation.updatedAt * 1000)
                tvDate.text = date
            }
        })
What this does: Registers a ConversationsViewHolderListener that replaces the entire list item row. The example inflates a compact layout with an avatar, name, and date — replacing the default multi-line conversation item.

setDateTimeFormatter

Custom date/time formatting for conversation timestamps. Implement the DateTimeFormatterCallback interface to control how each time range is displayed:
MethodCalled when
time(long timestamp)Custom full timestamp format
today(long timestamp)Message is from today
yesterday(long timestamp)Message is from yesterday
lastWeek(long timestamp)Message is from the past week
otherDays(long timestamp)Message is older than a week
minute(long timestamp)Exactly 1 minute ago
minutes(long diffInMinutesFromNow, long timestamp)Multiple minutes ago (e.g., “5 minutes ago”)
hour(long timestamp)Exactly 1 hour ago
hours(long diffInHourFromNow, long timestamp)Multiple hours ago (e.g., “2 hours ago”)
cometchatConversations.setDateTimeFormatter(object : DateTimeFormatterCallback {

        private val fullTimeFormatter = SimpleDateFormat("hh:mm a", Locale.getDefault())
        private val dateFormatter = SimpleDateFormat("dd MMM yyyy", Locale.getDefault())

        override fun time(timestamp: Long): String {
            return fullTimeFormatter.format(Date(timestamp))
        }

        override fun today(timestamp: Long): String {
            return "Today"
        }

        override fun yesterday(timestamp: Long): String {
            return "Yesterday"
        }

        override fun lastWeek(timestamp: Long): String {
            return "Last Week"
        }

        override fun otherDays(timestamp: Long): String {
            return dateFormatter.format(Date(timestamp))
        }

        override fun minutes(diffInMinutesFromNow: Long, timestamp: Long): String {
            return "$diffInMinutesFromNow mins ago"
        }

        override fun hours(diffInHourFromNow: Long, timestamp: Long): String {
            return "$diffInHourFromNow hrs ago"
        }
    })

setOptions

Replace the long-press context menu entirely. Generic signature:
cometchatConversations.setOptions { context, conversation -> emptyList<CometChatPopupMenu.MenuItem?>() }
Example with a custom delete option:
cometchatConversations.setOptions { context, conversation ->
            val optionsArrayList: MutableList<CometChatPopupMenu.MenuItem> =
                ArrayList()
            optionsArrayList.add(
                CometChatPopupMenu.MenuItem(
                    UIKitConstants.ConversationOption.DELETE,
                    "Delete",
                    ResourcesCompat. getDrawable(resources,com.cometchat.chatuikit.R.drawable.cometchat_ic_delete, getContext()?.theme),
                    null,
                    CometChatTheme.getErrorColor(context),
                    0,
                    CometChatTheme.getErrorColor(context),
                    CometChatTheme.getTextAppearanceBodyRegular(context)
                ) {
                    Toast.makeText(context, "Delete", Toast.LENGTH_SHORT).show()
                }
            )
            optionsArrayList
        }

addOptions

Append to the long-press context menu without removing defaults. Generic signature:
cometchatConversations.addOptions { context, conversation -> emptyList<CometChatPopupMenu.MenuItem?>() }
Example with archive, pin, and mark-as-read options:
cometchatConversations.addOptions { context, conversation ->
            val optionsArrayList: MutableList<CometChatPopupMenu.MenuItem> = ArrayList()
            optionsArrayList.add(
                CometChatPopupMenu.MenuItem(
                    "ARCHIVE", "Archive",
                    resources.getDrawable(R.drawable.archive), null,
                    CometChatTheme.getTextColorPrimary(context), 0,
                    CometChatTheme.getTextColorPrimary(context),
                    CometChatTheme.getTextAppearanceBodyRegular(context)
                ) { Toast.makeText(context, "Archive", Toast.LENGTH_SHORT).show() }
            )
            optionsArrayList.add(
                CometChatPopupMenu.MenuItem(
                    "PIN", "Pin",
                    resources.getDrawable(R.drawable.pin), null,
                    CometChatTheme.getTextColorPrimary(context), 0,
                    CometChatTheme.getTextColorPrimary(context),
                    CometChatTheme.getTextAppearanceBodyRegular(context)
                ) { Toast.makeText(context, "Pin", Toast.LENGTH_SHORT).show() }
            )
            optionsArrayList.add(
                CometChatPopupMenu.MenuItem(
                    "MARKASREAD", "Mark as read",
                    resources.getDrawable(R.drawable.mark_as_read), null,
                    CometChatTheme.getTextColorPrimary(context), 0,
                    CometChatTheme.getTextColorPrimary(context),
                    CometChatTheme.getTextAppearanceBodyRegular(context)
                ) { Toast.makeText(context, "Mark as read", Toast.LENGTH_SHORT).show() }
            )
            optionsArrayList
        }

setTextFormatters

Custom text formatters for the conversation subtitle. See the MentionsFormatter Guide for details.
themes.xml
<style name="CustomConversationsMentionsStyle" parent="CometChatConversationsMentionsStyle">
    <item name="cometchatMentionTextAppearance">?attr/cometchatTextAppearanceBodyRegular</item>
    <item name="cometchatMentionTextColor">#D6409F</item>
    <item name="cometchatMentionBackgroundColor">#D6409F</item>
    <item name="cometchatSelfMentionTextColor">#30A46C</item>
    <item name="cometchatSelfMentionTextAppearance">?attr/cometchatTextAppearanceBodyRegular</item>
    <item name="cometchatSelfMentionBackgroundColor">#30A46C</item>
</style>
val mentionFormatter = CometChatMentionsFormatter(context)
mentionFormatter.setConversationsMentionTextStyle(context, R.style.CustomConversationsMentionsStyle)

val textFormatters: MutableList<CometChatTextFormatter> = ArrayList()
textFormatters.add(mentionFormatter)
cometChatConversations.setTextFormatters(textFormatters)

setLoadingView

Sets a custom loading view displayed when data is being fetched.
cometchatConversations.loadingView = R.layout.your_loading_view
What this does: Replaces the default loading spinner with your custom layout resource. The custom view displays while conversations are being fetched.

setEmptyView

Configures a custom view displayed when there are no conversations in the list.
cometchatConversations.emptyView = R.layout.your_empty_view
What this does: Replaces the default empty state with your custom layout resource. The custom view displays when the conversation list has no items.

setErrorView

Defines a custom error state view that appears when an issue occurs while loading conversations.
cometchatConversations.errorView = R.layout.your_error_view
What this does: Replaces the default error state with your custom layout resource. The custom view displays when the component encounters an error during data fetching.

setDateFormat

Customizes the date format used for displaying timestamps in conversations. Generic signature:
cometChatConversations.setDateFormat(SimpleDateFormat)
Example:
cometchatConversations.setDateFormat(SimpleDateFormat("dd MMM, hh:mm a",Locale.getDefault()))
What this does: Sets the conversation date format to “dd MMM, hh:mm a” (e.g., “10 Jul, 02:30 PM”) using the device’s default locale.

setOverflowMenu

Replace the toolbar overflow menu. Generic signature:
cometChatConversations.setOverflowMenu(view)
Example with a user profile popup menu:
Create a user_profile_popup_menu_layout.xml custom view file:
user_profile_popup_menu_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="@dimen/cometchat_30dp"
    android:layout_height="wrap_content"
    android:layout_gravity="end"
    android:layout_marginTop="@dimen/cometchat_margin_10"
    android:background="?attr/cometchatBackgroundColor1"
    android:padding="@dimen/cometchat_padding_2"
    app:cardCornerRadius="@dimen/cometchat_radius_2"
    app:strokeColor="?attr/cometchatStrokeColorDefault"
    app:strokeWidth="@dimen/cometchat_1dp">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <TextView
            android:id="@+id/tv_create_conversation"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:drawablePadding="@dimen/cometchat_padding_2"
            android:gravity="center_vertical"
            android:padding="@dimen/cometchat_padding_4"
            android:text="@string/app_create_conversation"
            android:textAppearance="?attr/textAppearanceSubtitle1"
            app:drawableStartCompat="@drawable/ic_start_conversation" />

        <View
            android:id="@+id/view_separator"
            android:layout_width="match_parent"
            android:layout_height="@dimen/cometchat_1dp"
            android:layout_gravity="center"
            android:background="?attr/cometchatStrokeColorDefault" />

        <TextView
            android:id="@+id/tv_user_name"
            style="?attr/cometchatTextAppearanceBodyRegular"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:drawablePadding="@dimen/cometchat_padding_2"
            android:gravity="center_vertical"
            android:paddingStart="@dimen/cometchat_padding_4"
            android:paddingTop="@dimen/cometchat_padding_2"
            android:paddingEnd="@dimen/cometchat_padding_4"
            android:paddingBottom="@dimen/cometchat_padding_2"
            android:textAppearance="?attr/textAppearanceSubtitle1"
            android:textColor="?attr/cometchatTextColorPrimary"
            app:drawableStartCompat="@drawable/ic_user_profile" />

        <TextView
            android:id="@+id/tv_logout"
            style="?attr/cometchatTextAppearanceBodyRegular"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:drawablePadding="@dimen/cometchat_padding_2"
            android:gravity="center_vertical"
            android:paddingStart="@dimen/cometchat_padding_4"
            android:paddingTop="@dimen/cometchat_padding_2"
            android:paddingEnd="@dimen/cometchat_padding_4"
            android:paddingBottom="@dimen/cometchat_padding_2"
            android:text="@string/app_logout"
            android:textAppearance="?attr/textAppearanceSubtitle1"
            android:textColor="?attr/cometchatErrorColor"
            app:drawableStartCompat="@drawable/ic_logout" />

        <View
            android:layout_width="match_parent"
            android:layout_height="@dimen/cometchat_1dp"
            android:layout_gravity="center"
            android:background="?attr/cometchatStrokeColorDefault" />

        <TextView
            android:id="@+id/tv_version"
            style="?attr/cometchatTextAppearanceBodyRegular"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center_vertical"
            android:padding="@dimen/cometchat_padding_4"
            android:textAppearance="?attr/textAppearanceCaption"
            android:textColor="?attr/cometchatTextColorSecondary" />

    </LinearLayout>

</com.google.android.material.card.MaterialCardView>
What this does: Defines a popup menu layout with options for creating a conversation, viewing the user profile, logging out, and displaying the app version.
Inflate the view and pass it to setOverflowMenu:
class YourActivity : AppCompatActivity() {
    private lateinit var cometChatConversations: CometChatConversations

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        cometChatConversations = findViewById(R.id.conversations)
        cometChatConversations.setOverflowMenu(logoutView)
    }

    private val logoutView: View?
        get() {
            val user = CometChatUIKit.getLoggedInUser()
            if (user != null) {
                val cometchatAvatar = CometChatAvatar(this)
                cometchatAvatar.setAvatar(user.name, user.avatar)
                val layoutParams = LinearLayout.LayoutParams(
                    resources.getDimensionPixelSize(com.cometchat.chatuikit.R.dimen.cometchat_40dp),
                    resources.getDimensionPixelSize(com.cometchat.chatuikit.R.dimen.cometchat_40dp))
                layoutParams.layoutDirection = Gravity.CENTER_VERTICAL
                cometchatAvatar.layoutParams = layoutParams
                cometchatAvatar.setOnClickListener {
                    showCustomMenu(cometChatConversations.binding.toolbarLayout)
                }
                return cometchatAvatar
            }
            return null
        }

    private fun showCustomMenu(anchorView: View) {
        val popupMenuBinding = UserProfilePopupMenuLayoutBinding.inflate(LayoutInflater.from(this))
        val popupWindow = PopupWindow(popupMenuBinding.root,
            resources.getDimensionPixelSize(com.cometchat.chatuikit.R.dimen.cometchat_250dp),
            LinearLayout.LayoutParams.WRAP_CONTENT, true)

        popupMenuBinding.tvUserName.text = CometChatUIKit.getLoggedInUser().name
        val version = "V" + BuildConfig.VERSION_NAME + "(" + BuildConfig.VERSION_CODE + ")"
        popupMenuBinding.tvVersion.text = version

        popupMenuBinding.tvCreateConversation.setOnClickListener { popupWindow.dismiss() }
        popupMenuBinding.tvUserName.setOnClickListener { popupWindow.dismiss() }
        popupMenuBinding.tvLogout.setOnClickListener { popupWindow.dismiss() }

        popupWindow.elevation = 5f
        val endMargin = resources.getDimensionPixelSize(com.cometchat.chatuikit.R.dimen.cometchat_margin_2)
        val anchorWidth = anchorView.width
        val offsetX = anchorWidth - popupWindow.width - endMargin
        popupWindow.showAsDropDown(anchorView, offsetX, 0)
    }
}
What this does: Creates a complete overflow menu implementation. An avatar of the logged-in user is set as the overflow menu view. When tapped, it shows a popup window with options for creating a conversation, viewing the user profile, logging out, and displaying the app version.
  • Verify: After setting any custom view slot, confirm the custom view renders in the correct position within the conversation list item, and the data binding populates correctly for each conversation.

Common Patterns

Custom empty state with action button

layout/empty_conversations.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="40dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="No conversations yet"
        android:textAppearance="?attr/cometchatTextAppearanceHeading3Bold" />

    <Button
        android:id="@+id/btn_start_chat"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Start a conversation" />

</LinearLayout>
cometChatConversations.emptyView = R.layout.empty_conversations

Hide all chrome — minimal list

cometChatConversations.setReceiptsVisibility(View.GONE)
cometChatConversations.setUserStatusVisibility(View.GONE)
cometChatConversations.setGroupTypeVisibility(View.GONE)
cometChatConversations.setDeleteConversationOptionVisibility(View.GONE)
cometChatConversations.setToolbarVisibility(View.GONE)

Filter to user conversations only

val builder = ConversationsRequest.ConversationsRequestBuilder()
    .setConversationType(CometChatConstants.CONVERSATION_TYPE_USER)
cometChatConversations.setConversationsRequestBuilder(builder)

Advanced Methods

Programmatic Selection

selectConversation

Programmatically selects or deselects a conversation. Works with both SINGLE and MULTIPLE selection modes.
// Select a conversation in single-select mode
cometChatConversations.selectConversation(conversation, UIKitConstants.SelectionMode.SINGLE)

// Select a conversation in multi-select mode
cometChatConversations.selectConversation(conversation, UIKitConstants.SelectionMode.MULTIPLE)
In SINGLE mode, selecting a new conversation replaces the previous selection. In MULTIPLE mode, calling this on an already-selected conversation deselects it (toggle behavior).

clearSelection

Clears all selected conversations and resets the selection UI.
cometChatConversations.clearSelection()

getSelectedConversations

Returns the list of currently selected Conversation objects.
val selected = cometChatConversations.selectedConversations

Selected Conversations List

When using multi-select mode, a horizontal list of selected conversations can be shown above the main list.
MethodTypeDescription
setSelectedConversationsListVisibilityint (View.VISIBLE / View.GONE)Show or hide the selected conversations strip
setSelectedConversationAvatarStyle@StyleRes intAvatar style for selected conversation chips
setSelectedConversationItemTextColor@ColorInt intText color for selected conversation names
setSelectedConversationItemTextAppearance@StyleRes intText appearance for selected conversation names
setSelectedConversationItemRemoveIconDrawableIcon for the remove button on each chip
setSelectedConversationItemRemoveIconTint@ColorInt intTint color for the remove icon

Search Input Customization

The built-in search box can be customized programmatically:
MethodTypeDescription
setSearchInputTextStringSets the search input text programmatically
setSearchPlaceholderTextStringSets the placeholder text for the search input
setSearchInputTextColor@ColorInt intText color of the search input
setSearchInputTextAppearance@StyleRes intText appearance of the search input
setSearchInputPlaceHolderTextColor@ColorInt intPlaceholder text color
setSearchInputPlaceHolderTextAppearance@StyleRes intPlaceholder text appearance
setSearchInputStartIconDrawableLeading icon in the search box
setSearchInputStartIconTint@ColorInt intTint for the leading icon
setSearchInputEndIconDrawableTrailing icon in the search box
setSearchInputEndIconTint@ColorInt intTint for the trailing icon
setSearchInputEndIconVisibilityintVisibility of the trailing icon
setSearchInputStrokeWidth@Dimension intStroke width of the search box border
setSearchInputStrokeColor@ColorInt intStroke color of the search box border
setSearchInputBackgroundColor@ColorInt intBackground color of the search box
setSearchInputCornerRadius@Dimension intCorner radius of the search box

Internal Access

These methods provide direct access to internal components for advanced use cases.
MethodReturnsDescription
getRecyclerView()RecyclerViewThe underlying RecyclerView displaying conversations
getViewModel()ConversationsViewModelThe ViewModel managing conversation data and state
getConversationsAdapter()ConversationsAdapterThe adapter powering the RecyclerView
setAdapter(ConversationsAdapter)voidReplaces the default adapter with a custom one
getBinding()CometchatConversationsListViewBindingThe ViewBinding for the component’s root layout
Use these only when the standard API is insufficient. Directly manipulating the adapter or ViewModel may conflict with the component’s internal state management.

Other Methods

MethodTypeDescription
setMentionAllLabelId(String id, String mentionAllLabel)voidSets the label used for “mention all” in conversation subtitles
setDateStyle(@StyleRes int)voidSets the style for the date label in conversation items
setOptionListStyle(@StyleRes int)voidSets the style for the long-press popup menu

Style

The component uses XML theme styles. Define a custom style with parent CometChatConversationsStyle in themes.xml, then apply with setStyle().
themes.xml
    <style name="CustomAvatarStyle" parent="CometChatAvatarStyle">
        <item name="cometchatAvatarStrokeRadius">8dp</item>
        <item name="cometchatAvatarBackgroundColor">#FBAA75</item>
    </style>

    <style name="CustomBadgeCountStyle" parent="CometChatBadgeStyle">
        <item name="cometchatBadgeBackgroundColor">#F76808</item>
        <item name="cometchatBadgeTextColor">#FFFFFF</item>
    </style>

    <style name="CustomConversationsStyle" parent="CometChatConversationsStyle">
        <item name="cometchatConversationsAvatarStyle">@style/CustomAvatarStyle</item>
        <item name="cometchatConversationsBadgeStyle">@style/CustomBadgeCountStyle</item>
    </style>
cometChatConversations.setStyle(R.style.CustomConversationsStyle)
To know more such attributes, visit the attributes file.

Programmatic Style Properties

In addition to XML theme styles, the component exposes programmatic setters for fine-grained control:
MethodTypeDescription
setBackgroundColor@ColorInt intBackground color of the component
setBackIconTint@ColorInt intTint color for the back icon
setBackIconDrawableCustom back icon drawable
setTitleTextColor@ColorInt intTitle text color in the toolbar
setTitleTextAppearance@StyleRes intTitle text appearance in the toolbar
setItemTitleTextColor@ColorInt intText color for conversation item titles
setItemTitleTextAppearance@StyleRes intText appearance for conversation item titles
setItemSubtitleTextColor@ColorInt intText color for conversation item subtitles
setItemSubtitleTextAppearance@StyleRes intText appearance for conversation item subtitles
setItemMessageTypeIconTint@ColorInt intTint for message type icons in subtitles
setSeparatorColor@ColorInt intColor of list item separators
setSeparatorHeight@Dimension intHeight of list item separators
setStrokeColor@ColorInt intStroke color of the component border
setStrokeWidth@Dimension intStroke width of the component border
setCornerRadius@Dimension intCorner radius of the component
setEmptyStateTitleTextColor@ColorInt intTitle text color for the empty state
setEmptyStateSubtitleTextColor@ColorInt intSubtitle text color for the empty state
setEmptyStateTextTitleAppearance@StyleRes intTitle text appearance for the empty state
setEmptyStateTextSubtitleAppearance@StyleRes intSubtitle text appearance for the empty state
setErrorStateTitleTextColor@ColorInt intTitle text color for the error state
setErrorStateSubtitleTextColor@ColorInt intSubtitle text color for the error state
setErrorStateTextTitleAppearance@StyleRes intTitle text appearance for the error state
setErrorStateTextSubtitleAppearance@StyleRes intSubtitle text appearance for the error state
setDeleteOptionIconDrawableIcon for the delete option in the context menu
setDeleteOptionIconTintintTint for the delete option icon
setDeleteOptionTextColorintText color for the delete option
setDeleteOptionTextAppearanceintText appearance for the delete option
setAvatarStyle@StyleRes intStyle for conversation avatars
setStatusIndicatorStyle@StyleRes intStyle for online/offline status indicators
setBadgeStyle@StyleRes intStyle for unread badge counts
setReceiptStyle@StyleRes intStyle for read/delivered receipt icons
setTypingIndicatorStyle@StyleRes intStyle for typing indicator text
setMentionsStyle@StyleRes intStyle for @mention text in subtitles
setItemBackgroundColor@ColorInt intBackground color for list items
setItemSelectedBackgroundColor@ColorInt intBackground color for selected list items

Checkbox Style Properties (Selection Mode)

When using SINGLE or MULTIPLE selection mode, checkboxes appear on each item:
MethodTypeDescription
setCheckBoxStrokeWidth@Dimension intStroke width of the checkbox border
setCheckBoxCornerRadius@Dimension intCorner radius of the checkbox
setCheckBoxStrokeColor@ColorInt intStroke color of the checkbox border
setCheckBoxBackgroundColor@ColorInt intBackground color of unchecked checkbox
setCheckBoxCheckedBackgroundColor@ColorInt intBackground color of checked checkbox
setCheckBoxSelectIconDrawableIcon shown when checkbox is checked
setCheckBoxSelectIconTint@ColorInt intTint for the checkbox select icon
setDiscardSelectionIconDrawableIcon for the discard selection button
setDiscardSelectionIconTint@ColorInt intTint for the discard selection icon
setSubmitSelectionIconDrawableIcon for the submit selection button
setSubmitSelectionIconTint@ColorInt intTint for the submit selection icon

Customization Matrix

What to changeWhereProperty/APIExample
Override behavior on user interactionActivity/FragmentsetOn<Event> callbackssetOnItemClick((v, pos, c) -> { ... })
Filter which conversations appearActivity/FragmentsetConversationsRequestBuildersetConversationsRequestBuilder(builder)
Toggle visibility of UI elementsActivity/Fragmentset<Feature>Visibility(int)setReceiptsVisibility(View.GONE)
Replace a section of the list itemActivity/Fragmentset<Slot>ViewsetLeadingView(listener)
Change colors, fonts, spacingthemes.xmlCometChatConversationsStyle<item name="cometchatConversationsBadgeStyle">@style/...</item>
Avatar style (corner radius, background)themes.xmlcometchatConversationsAvatarStyle<item name="cometchatAvatarStrokeRadius">8dp</item>
Badge count style (background, text color)themes.xmlcometchatConversationsBadgeStyle<item name="cometchatBadgeBackgroundColor">#F76808</item>
Apply a custom styleActivity/FragmentsetStyle(int styleRes)cometChatConversations.setStyle(R.style.CustomConversationsStyle);
Back button visibilityActivity/FragmentsetBackIconVisibility(int).setBackIconVisibility(View.VISIBLE);
Toolbar visibilityActivity/FragmentsetToolbarVisibility(int).setToolbarVisibility(View.GONE);
Loading state visibilityActivity/FragmentsetLoadingStateVisibility(int).setLoadingStateVisibility(View.GONE);
Delete option visibility on long pressActivity/FragmentsetDeleteConversationOptionVisibility(int).setDeleteConversationOptionVisibility(View.GONE);
Error state visibilityActivity/FragmentsetErrorStateVisibility(int).setErrorStateVisibility(View.GONE);
Empty state visibilityActivity/FragmentsetEmptyStateVisibility(int).setEmptyStateVisibility(View.GONE);
Separator visibilityActivity/FragmentsetSeparatorVisibility(int).setSeparatorVisibility(View.GONE);
User online status visibilityActivity/FragmentsetUserStatusVisibility(int).setUserStatusVisibility(View.GONE);
Group type indicator visibilityActivity/FragmentsetGroupTypeVisibility(int).setGroupTypeVisibility(View.GONE);
Read/delivered receipts visibilityActivity/FragmentsetReceiptsVisibility(int).setReceiptsVisibility(View.GONE);
Incoming message soundActivity/FragmentdisableSoundForMessages(boolean).disableSoundForMessages(true);
Custom message soundActivity/FragmentsetCustomSoundForMessages(int).setCustomSoundForMessages(R.raw.cometchat_beep2);
Selection mode (single/multiple)Activity/FragmentsetSelectionMode(SelectionMode).setSelectionMode(UIKitConstants.SelectionMode.MULTIPLE);
Date/time formattingActivity/FragmentsetDateTimeFormatter(DateTimeFormatterCallback)See setDateTimeFormatter code above
Date formatActivity/FragmentsetDateFormat(SimpleDateFormat).setDateFormat(new SimpleDateFormat("dd MMM, hh:mm a", Locale.getDefault()));
Long-press options (replace)Activity/FragmentsetOptions(Function2)See setOptions code above
Long-press options (append)Activity/FragmentaddOptions(Function2)See addOptions code above
Loading viewActivity/FragmentsetLoadingView(int).setLoadingView(R.layout.your_loading_view);
Empty viewActivity/FragmentsetEmptyView(int).setEmptyView(R.layout.your_empty_view);
Error viewActivity/FragmentsetErrorView(int).setErrorView(R.layout.your_error_view);
Leading view (avatar area)Activity/FragmentsetLeadingView(ConversationsViewHolderListener)See setLeadingView code above
Title viewActivity/FragmentsetTitleView(ConversationsViewHolderListener)See setTitleView code above
Trailing viewActivity/FragmentsetTrailingView(ConversationsViewHolderListener)See setTrailingView code above
Entire list itemActivity/FragmentsetItemView(ConversationsViewHolderListener)See setItemView code above
Subtitle viewActivity/FragmentsetSubtitleView(ConversationsViewHolderListener)See setSubtitleView code above
Text formatters (mentions)Activity/FragmentsetTextFormatters(List<CometChatTextFormatter>)See setTextFormatters code above
Overflow menuActivity/FragmentsetOverflowMenu(View)cometChatConversations.setOverflowMenu(view);
Filter conversationsActivity/FragmentsetConversationsRequestBuilder(ConversationsRequestBuilder)See Filters code above
Search box visibilityActivity/FragmentsetSearchBoxVisibility(int).setSearchBoxVisibility(View.GONE);
Search input textActivity/FragmentsetSearchInputText(String).setSearchInputText("search term");
Search placeholder textActivity/FragmentsetSearchPlaceholderText(String).setSearchPlaceholderText("Search...");
Search input colorsActivity/FragmentsetSearchInputTextColor, setSearchInputBackgroundColor.setSearchInputTextColor(Color.BLACK);
Search input iconsActivity/FragmentsetSearchInputStartIcon, setSearchInputEndIcon.setSearchInputStartIcon(drawable);
Hide receipts (adapter)Activity/FragmenthideReceipts(boolean).hideReceipts(true);
Programmatic selectionActivity/FragmentselectConversation(Conversation, SelectionMode).selectConversation(conv, SelectionMode.SINGLE);
Clear selectionActivity/FragmentclearSelection().clearSelection();
Selected conversations stripActivity/FragmentsetSelectedConversationsListVisibility(int).setSelectedConversationsListVisibility(View.VISIBLE);
Selected conversation avatar styleActivity/FragmentsetSelectedConversationAvatarStyle(int).setSelectedConversationAvatarStyle(R.style.CustomAvatarStyle);
Internal adapter accessActivity/FragmentgetConversationsAdapter() / setAdapter()Advanced use only
Internal ViewModel accessActivity/FragmentgetViewModel()Advanced use only
Mention-all labelActivity/FragmentsetMentionAllLabelId(String, String).setMentionAllLabelId("all", "Everyone");

Accessibility

The component renders a scrollable RecyclerView of interactive conversation items. Each conversation row responds to tap and long-press gestures. The unread badge count is rendered as a TextView with the count as text content, accessible to TalkBack. Avatar images include the conversation name as content description. For custom views provided via setLeadingView, setTitleView, setTrailingView, or setItemView, ensure you set android:contentDescription on visual-only elements (status indicators, icons) so TalkBack can announce them. The default views handle this automatically. Group type indicators and online/offline status dots are visual-only by default. If screen reader descriptions are needed for these, provide them via a custom view with appropriate contentDescription attributes.

Next Steps