Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -1856,7 +1856,6 @@
"pages": [
"ui-kit/android/guide-overview",
"ui-kit/android/guide-threaded-messages",
"ui-kit/android/guide-thread-subscription",
"ui-kit/android/guide-pin-and-save-messages",
"ui-kit/android/guide-block-unblock-user",
"ui-kit/android/guide-new-chat",
Expand Down Expand Up @@ -6939,6 +6938,10 @@
}
},
"redirects": [
{
"source": "/ui-kit/android/guide-thread-subscription",
"destination": "/ui-kit/android/guide-threaded-messages#thread-subscription"
},
{
"source": "/sdk/flutter/group-kick-member",
"destination": "/sdk/flutter/group-kick-ban-members"
Expand Down
7 changes: 4 additions & 3 deletions ui-kit/android/core-features.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

| Field | Value |
| --- | --- |
| Packages | `com.cometchat:chatuikit-kotlin-android` (Kotlin XML Views), `com.cometchat:chatuikit-compose-android` (Jetpack Compose) |

Check warning on line 10 in ui-kit/android/core-features.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/core-features.mdx#L10

Did you really mean 'Jetpack'?
| Required setup | `CometChatUIKit.init()` then `CometChatUIKit.login()` — must complete before rendering any component |
| Core features | Instant Messaging, Media Sharing, Read Receipts, Mark as Unread, Typing Indicator, User Presence, Reactions, Mentions, Rich Text Formatting, Quoted Reply, Search, Threaded Conversations, Moderation, Report Message, Group Chat |
| Key components | `CometChatConversations`, `CometChatMessageList`, `CometChatMessageComposer`, `CometChatMessageHeader`, `CometChatUsers`, `CometChatGroups`, `CometChatGroupMembers`, `CometChatMessageInformation`, `CometChatThreadHeader` |
Expand Down Expand Up @@ -169,6 +169,7 @@
| --- | --- |
| [CometChatMessageComposer](/ui-kit/android/message-composer) | Provides a built-in rich text editor with formatting toolbar and text selection menu items for bold, italic, strikethrough, code, links, lists, blockquotes, and code blocks. |
| [CometChatMessageList](/ui-kit/android/message-list) | Renders formatted messages with the appropriate styling automatically applied, ensuring that rich text formatting is displayed exactly as intended by the sender. |

## Threaded Conversations

Respond directly to a specific message, keeping conversations organized.
Expand All @@ -183,16 +184,16 @@
| [CometChatMessageComposer](/ui-kit/android/message-composer) | Allows composing messages within a thread. |
| [CometChatMessageList](/ui-kit/android/message-list) | Displays threaded messages in context. |

## Thread Subscription
### Thread Subscription

Let users subscribe to or unsubscribe from a thread to control whether its replies notify them. Opt-in feature — enable it with `UIKitSettings.setEnableThreadSubscription(true)`.
Let users subscribe to or unsubscribe from a thread to control whether its replies notify them. Enabled by default — remove a surface with `setThreadSubscriptionOptionVisibility(View.GONE)` or `setThreadSubscriptionVisibility(View.GONE)`.

| Component | Role |
| --- | --- |
| [CometChatMessageList](/ui-kit/android/message-list) | Provides the Subscribe to thread / Unsubscribe from thread option in the message action sheet. |
| [CometChatThreadHeader](/ui-kit/android/threaded-messages-header) | Shows the subscription bell on the thread view. |

See the [Thread Subscription guide](/ui-kit/android/guide-thread-subscription) for setup and behavior.
See [Threaded Messages → Thread Subscription](/ui-kit/android/guide-threaded-messages#thread-subscription) for setup and behavior.

## Quoted Replies

Expand Down
2 changes: 1 addition & 1 deletion ui-kit/android/events.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
| Field | Value |
| --- | --- |
| Kotlin (XML Views) | `com.cometchat:chatuikit-kotlin-android` |
| Jetpack Compose | `com.cometchat:chatuikit-compose-android` |

Check warning on line 11 in ui-kit/android/events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/events.mdx#L11

Did you really mean 'Jetpack'?
| Import | `com.cometchat.uikit.core.events.CometChatEvents` |
| Event flows | `CometChatEvents.messageEvents`, `CometChatEvents.callEvents`, `CometChatEvents.conversationEvents`, `CometChatEvents.groupEvents`, `CometChatEvents.userEvents`, `CometChatEvents.uiEvents` |
| Pattern | Kotlin `SharedFlow` with sealed class event types — collect in `viewModelScope` or `lifecycleScope` |
Expand Down Expand Up @@ -74,14 +74,14 @@
| `MessageEvent.MessagePinned(message)` | Triggered when a message is pinned. |
| `MessageEvent.MessageUnpinned(message)` | Triggered when a message is unpinned. |
| `MessageEvent.MessageSaved(message)` | Triggered when the logged-in user saves a message. |
| `MessageEvent.MessageUnsaved(message)` | Triggered when the logged-in user unsaves a message. |

Check warning on line 77 in ui-kit/android/events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/events.mdx#L77

Did you really mean 'unsaves'?

**Collecting events:**

<Tabs>
<Tab title="Kotlin (XML Views)">
```kotlin
// In an Activity or Fragment — use lifecycleScope

Check warning on line 84 in ui-kit/android/events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/events.mdx#L84

'lifecycleScope' is repeated!

Check warning on line 84 in ui-kit/android/events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/events.mdx#L84

Did you really mean 'lifecycleScope'?
lifecycleScope.launch {
CometChatEvents.messageEvents.collect { event ->
when (event) {
Expand Down Expand Up @@ -164,7 +164,7 @@

### Thread Events

`CometChatEvents.threadEvents` emits `CometChatThreadEvent` instances when the logged-in user subscribes to or unsubscribes from a message thread, so every surface showing a subscription control can stay in sync without a refetch.

Check warning on line 167 in ui-kit/android/events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/events.mdx#L167

Did you really mean 'refetch'?

Every event on this bus is published by the UI Kit itself — the Chat SDK has no thread listener, and a `subscribeToThread()` / `unsubscribeFromThread()` callback *is* the acknowledgement. Because surfaces do not share message instances, a handler should update its own state **and** stamp the flag onto the message objects it holds with `BaseMessage.setThreadSubscribed()`.

Expand All @@ -190,7 +190,7 @@
}
```

See the [Thread Subscription guide](/ui-kit/android/guide-thread-subscription) for the feature end to end.
See [Threaded Messages → Thread Subscription](/ui-kit/android/guide-threaded-messages#thread-subscription) for the feature end to end.

### Call Events

Expand Down Expand Up @@ -552,7 +552,7 @@
</Tab>
</Tabs>

> **About `CardActionClicked`:** The UI Kit renders card bubbles automatically (`CometChatCardBubble`) and emits this event when a user taps an action inside one — so a single subscriber handles every card action across your app. `event.message` is a `CardMessage` for standalone [card messages](/sdk/android/v5/send-message#card-message) and an `AIAssistantMessage` for cards embedded in AI agent replies. `event.actionEvent` is typed `Any`; import and cast it to `com.cometchat.cards.actions.CometChatCardActionEvent` to read its `action`, `elementId`, and `cardJson`.

Check warning on line 555 in ui-kit/android/events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/events.mdx#L555

Did you really mean 'elementId'?

Check warning on line 555 in ui-kit/android/events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/events.mdx#L555

Did you really mean 'cardJson'?

---

Expand All @@ -560,8 +560,8 @@

Since `SharedFlow` collection is coroutine-based, lifecycle management is handled automatically:

- In XML Views, use `lifecycleScope.launch` — the coroutine is cancelled when the lifecycle owner is destroyed.

Check warning on line 563 in ui-kit/android/events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/events.mdx#L563

Did you really mean 'coroutine'?
- In Jetpack Compose, use `LaunchedEffect` — the coroutine is cancelled when the composable leaves the composition.

Check warning on line 564 in ui-kit/android/events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/events.mdx#L564

Did you really mean 'Jetpack'?

Check warning on line 564 in ui-kit/android/events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/events.mdx#L564

Did you really mean 'coroutine'?

Check warning on line 564 in ui-kit/android/events.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/events.mdx#L564

Did you really mean 'composable'?

No manual `removeListener` calls are needed, unlike the old static listener pattern.

Expand Down
157 changes: 0 additions & 157 deletions ui-kit/android/guide-thread-subscription.mdx

This file was deleted.

113 changes: 111 additions & 2 deletions ui-kit/android/guide-threaded-messages.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@
val rawMessage = intent.getStringExtra("raw_json")
val viewModel = ViewModelProvider(this)[ThreadMessageViewModel::class.java]
if (rawMessage != null) {
val parentMessage = BaseMessage.processMessage(JSONObject(rawMessage))

Check warning on line 120 in ui-kit/android/guide-threaded-messages.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/guide-threaded-messages.mdx#L120

Did you really mean 'parentMessage'?
viewModel.setParentMessage(parentMessage)

Check warning on line 121 in ui-kit/android/guide-threaded-messages.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/guide-threaded-messages.mdx#L121

Did you really mean 'parentMessage'?
}

lifecycleScope.launch {
Expand Down Expand Up @@ -274,6 +274,114 @@
| Blocked User | Composer hidden; unblock layout shown. |
| Not in Group | Show option to join group first. |

## Thread Subscription

Thread subscription gives users Slack-style control over thread noise: they can **subscribe** to a thread to be notified about its replies, or **unsubscribe** from one to mute it. Users are automatically subscribed when they start a thread, reply in one, or are @-mentioned in one — subscribing explicitly is how they opt in to a conversation they haven't participated in yet.

The UI Kit ships two surfaces for the same toggle, wired out of the box and kept in sync automatically:

1. A **Subscribe to thread / Unsubscribe from thread** option in the message action sheet.
2. A **subscription bell** on the thread view.

### The Message Action Sheet Option

[CometChatMessageList](/ui-kit/android/message-list) adds a **Subscribe to thread** / **Unsubscribe from thread** option to the long-press action sheet. The label reflects the current state, and the option appears on regular messages of every type (agent messages and moderation-blocked messages are excluded) — on a thread reply it targets the thread's root message, so subscribing from anywhere in the thread works.

To hide the option while keeping the rest of the feature:

<Tabs>
<Tab title="Kotlin (XML Views)">
```kotlin lines
messageList.setThreadSubscriptionOptionVisibility(View.GONE)
```
</Tab>
</Tabs>

### The Thread Header Bell

[CometChatThreadHeader](/ui-kit/android/threaded-messages-header) renders a subscription bell as a trailing control on the reply-count bar. It flips optimistically on tap and reverts with a toast if the request fails.

<Tabs>
<Tab title="Kotlin (XML Views)">
```kotlin lines
// Hide the bell (e.g. because you host your own — see below)
threadHeader.setThreadSubscriptionVisibility(View.GONE)

// Observe state changes (isSubscribed = the new state)

Check warning on line 310 in ui-kit/android/guide-threaded-messages.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/guide-threaded-messages.mdx#L310

Did you really mean 'isSubscribed'?
threadHeader.setOnThreadSubscriptionChange { isSubscribed ->
Log.d(TAG, "Thread subscribed: $isSubscribed")
}
```

The visibility can also be set in XML with the `app:cometchatThreadSubscriptionVisibility` attribute.

</Tab>
<Tab title="Jetpack Compose">
```kotlin lines
CometChatThreadHeader(
parentMessage = parentMessage,
hideThreadSubscription = false, // hide the built-in bell when true
isSubscribed = null, // null = seed from parentMessage.isThreadSubscribed()
onSubscriptionToggle = { isSubscribed ->
Log.d(TAG, "Thread subscribed: $isSubscribed")
},
threadSubscriptionView = null // or your own composable replacing the bell
)
```
</Tab>
</Tabs>

#### Hosting the Bell in Your Own Top Bar

Many apps (matching the CometChat sample apps and Figma) place the subscription bell in the thread screen's **top title bar** rather than the reply-count row. In Compose, the bell is available as a standalone public composable — hide the header's built-in one and host `ThreadSubscriptionBell` wherever you like:

Check warning on line 336 in ui-kit/android/guide-threaded-messages.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/guide-threaded-messages.mdx#L336

Did you really mean 'Figma'?

Check warning on line 336 in ui-kit/android/guide-threaded-messages.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/guide-threaded-messages.mdx#L336

Did you really mean 'composable'?

<Tabs>
<Tab title="Jetpack Compose">
```kotlin lines
TopAppBar(
title = { Text(stringResource(R.string.thread)) },
actions = {
ThreadSubscriptionBell(parentMessage = parentMessage)
}
)

CometChatThreadHeader(
parentMessage = parentMessage,
hideThreadSubscription = true // the bell lives in the top bar instead
)
```
</Tab>
<Tab title="Kotlin (XML Views)">
```kotlin lines
// Hide the kit header's bell and drive your own ImageView in the activity's title bar:
threadHeader.setThreadSubscriptionVisibility(View.GONE)

// On tap: flip your icon optimistically, then call the SDK
CometChat.subscribeToThread(parentMessage.id, object : CometChat.CallbackListener<String>() {
override fun onSuccess(response: String?) { }
override fun onError(e: CometChatException?) {
// revert the icon and show a toast
}
})
```
</Tab>
</Tabs>

### Subscription Behavior

- **Optimistic with revert** — both surfaces flip instantly on tap, keep one request in flight per thread, and revert with a toast if the server rejects the change. An offline tap fails visibly and reverts; nothing is queued.
- **Auto-subscribe on reply** — sending a reply in a thread subscribes the user, and every surface flips to the subscribed state automatically.
- **Unsubscribing is not sticky** — replying again, or being @-mentioned, re-subscribes the user.
- **Unknown state renders as unsubscribed** — a message whose subscription state hasn't been learned yet (for example, one that just arrived in real time) shows the enabled subscribe control, never a spinner.

### Cross-Surface Sync

Both surfaces observe the UI Kit event bus, so toggling in one place updates the other without a refetch. If you build your own subscription control, emit and collect `CometChatThreadEvent` through `CometChatEvents.threadEvents` — see [Events](/ui-kit/android/events).

Check warning on line 379 in ui-kit/android/guide-threaded-messages.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/guide-threaded-messages.mdx#L379

Did you really mean 'refetch'?

### Notifications

Whether a subscribed thread actually produces a push notification is governed by the user's notification preferences: the replies preference supports notifying only for **threads the user is subscribed to** (`SUBSCRIBE_TO_SUBSCRIBED_THREADS`). See [Thread Subscription (SDK)](/sdk/android/v5/thread-subscription#notification-preferences).

## Summary / Feature Matrix

| Feature | Component / Method |
Expand All @@ -283,12 +391,13 @@
| Show parent message | `header.setParentMessage(parentMessage)` |
| Compose reply | `composer.setParentMessageId(parentMessage.getId())` |
| Handle blocked users | `isBlockedByMe()`, hide composer + show unblock UI |
| Subscribe / unsubscribe | Action-sheet option + `CometChatThreadHeader` bell |

## Next Steps & Further Reading

<CardGroup>
<Card title="Thread Subscription" href="/ui-kit/android/guide-thread-subscription">
Let users subscribe to or unsubscribe from a thread to control whether its replies notify them.
<Card title="Thread Subscription (SDK)" href="/sdk/android/v5/thread-subscription">
The underlying APIs, including fetching the threads a user participates in to build a thread inbox.
</Card>
<Card title="Android Sample App (Kotlin)">
Explore this feature in the CometChat SampleApp:
Expand Down
6 changes: 3 additions & 3 deletions ui-kit/android/message-list.mdx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
---
title: "Message List"
description: "Scrollable list of messages for a conversation with real-time updates, reactions, threaded replies, and message actions."

Check warning on line 3 in ui-kit/android/message-list.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/message-list.mdx#L3

Did you really mean 'Scrollable'?
---

`CometChatMessageList` renders a scrollable list of messages for a conversation with real-time updates for new messages, edits, deletions, reactions, and threaded replies.

Check warning on line 6 in ui-kit/android/message-list.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/message-list.mdx#L6

Did you really mean 'scrollable'?

<Frame>
<img src="/images/7c6476ad-message_list-f571abbea715b343416ff5ca1cbc8c9b.png" />
Expand Down Expand Up @@ -407,7 +407,7 @@
</Tab>
</Tabs>

### Text Formatters (Mentions)

Check warning on line 410 in ui-kit/android/message-list.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/message-list.mdx#L410

Did you really mean 'Formatters'?

<Frame>
<img src="/images/e2732868-mentions_message_bubble-fccf9cbdd63a54c2f734803e4480418a.png" />
Expand Down Expand Up @@ -758,7 +758,7 @@
</Tab>
<Tab title="Jetpack Compose">

Pass composable lambdas that receive the message and its alignment:

Check warning on line 761 in ui-kit/android/message-list.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/message-list.mdx#L761

Did you really mean 'composable'?

```kotlin lines
CometChatMessageList(
Expand Down Expand Up @@ -850,17 +850,17 @@
| `setTranslateMessageOptionVisibility()` | `VISIBLE` | Translate message |
| `setShareMessageOptionVisibility()` | `VISIBLE` | Share message |
| `setMarkAsUnreadOptionVisibility()` | `GONE` | Mark as unread |
| `setThreadSubscriptionOptionVisibility()` | `VISIBLE`* | Subscribe / Unsubscribe thread option (*renders only when the thread-subscription feature gate is on) |
| `setThreadSubscriptionOptionVisibility()` | `VISIBLE` | Subscribe / Unsubscribe thread option |

### Feature Options (Pin, Save, Thread Subscription)

Three groups of options appear automatically when their feature is enabled for the app — no wiring needed:

- **Pin message / Unpin message** — shown on text and media messages when `CometChatUIKit.isPinMessageEnabled()`. The option is shown to **every** participant: permission is enforced by the server, and a user who lacks it gets a "you don't have permission" toast (`ERR_PERMISSION_DENIED`) rather than a hidden option. Pinning applies immediately with a toast; unpinning asks for confirmation first. Pinned messages get a pin indicator in the bubble footer.
- **Save message / Unsave message** — shown on text and media messages when `CometChatUIKit.isSaveMessageEnabled()`, for every user. Saving applies immediately with a toast; unsaving asks for confirmation first. Saved messages get a bookmark indicator in the bubble footer.

Check warning on line 860 in ui-kit/android/message-list.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/message-list.mdx#L860

Did you really mean 'Unsave'?

Check warning on line 860 in ui-kit/android/message-list.mdx

View check run for this annotation

Mintlify / Mintlify Validation (cometchat-22654f5b) - vale-spellcheck

ui-kit/android/message-list.mdx#L860

Did you really mean 'unsaving'?
- **Subscribe to thread / Unsubscribe from thread** — shown on regular messages (not agent or moderation-blocked ones) when thread subscription is enabled via `UIKitSettings.setEnableThreadSubscription(true)`. On a thread reply the action targets the thread's root message. Hide it with `setThreadSubscriptionOptionVisibility(View.GONE)`.
- **Subscribe to thread / Unsubscribe from thread** — shown on regular messages (not agent or moderation-blocked ones). On a thread reply the action targets the thread's root message. Hide it with `setThreadSubscriptionOptionVisibility(View.GONE)`.

Pin and Save are withheld on messages where the action is meaningless or would fail — deleted messages, messages that have not finished sending, and messages held or rejected by moderation. The labels toggle with the message's current state, and if a pin/save limit is exceeded the limit toast is generated from the server response automatically. See the [Pin & Save Messages](/ui-kit/android/guide-pin-and-save-messages) and [Thread Subscription](/ui-kit/android/guide-thread-subscription) guides.
Pin and Save are withheld on messages where the action is meaningless or would fail — deleted messages, messages that have not finished sending, and messages held or rejected by moderation. The labels toggle with the message's current state, and if a pin/save limit is exceeded the limit toast is generated from the server response automatically. See the [Pin & Save Messages](/ui-kit/android/guide-pin-and-save-messages) guide and [Threaded Messages → Thread Subscription](/ui-kit/android/guide-threaded-messages#thread-subscription).

### Replacing All Options (`setOptions`)

Expand Down
Loading