diff --git a/calls/android/migrating-from-legacy-ringing.mdx b/calls/android/migrating-from-legacy-ringing.mdx
index 6f4e9fedf..69ce35dfd 100644
--- a/calls/android/migrating-from-legacy-ringing.mdx
+++ b/calls/android/migrating-from-legacy-ringing.mdx
@@ -11,7 +11,7 @@ This page is the diff. For the full description of the current flow see [Ringing
## Before you start
-Ringing with the Calls SDK is opt-in on the client: moving your app to the Calls SDK 5.1 and Push Notifications SDK 2.0 APIs below is the migration. While some of your users are still on legacy ringing, one dashboard setting lets the two flows call each other; see [While some users are still on legacy ringing](#while-some-users-are-still-on-legacy-ringing).
+Ringing with the Calls SDK is opt-in on the client: moving your app to the Calls SDK 5.1 and Push Notifications SDK 2.0 APIs below is the migration. While some of your users are still on legacy ringing, one dashboard setting lets the two flows call each other; see [While some users are still on legacy ringing](#while-some-users-are-still-on-legacy-ringing). If you use the CometChat UI Kit, UI Kit 6.2.0 initialises the Calls SDK and logs the user into it for you; you add the Calls SDK and Push Notifications SDK artifacts as shown in [Calling Integration](/ui-kit/android/calling-integration).
## Why it changed
@@ -74,7 +74,7 @@ dependencies {
}
```
-- `minSdk` 26 or higher.
+- `minSdk` 26 or higher (28 with the UI Kit, its own floor).
- `compileSdk` 36 and Android Gradle Plugin 8.9.1 or later: the Push Notifications SDK 2.0 depends on `androidx.core` 1.18, which requires them.
- Keep your Firebase Messaging dependency and `google-services.json`; the Push SDK never compiles against Firebase.
@@ -188,7 +188,7 @@ Everything in this list is now done by the Push SDK. Remove it, or it will fight
| `CometChat.getActiveCall()` / `clearActiveCall()` | `CometChatCalls.getActiveSession()` (null when no session is prepared or joined) | |
| `CometChat.startCall(...)`, `CallSettings`, `OngoingCallListener` | `joinSession` + the 5.x listeners (see the [v5 migration guide](/calls/android/migration-guide-v5)) | |
| `CometChatCallsConstants.*` | `com.cometchat.calls.constants.Constants.*` | The whole `CometChatCallsConstants` class has been deprecated since 5.0 |
-| `CometChat.logout(...)` | `CometChatCalls.logout(...)` as well | Otherwise the Calls SDK stays signed in as the old user and a ring is not stopped |
+| `CometChat.logout(...)` | `CometChatCalls.logout(...)` as well (the UI Kit's `CometChatUIKit.logout` does both) | Otherwise the Calls SDK stays signed in as the old user and a ring is not stopped |
### Caller screen, before and after
@@ -260,10 +260,19 @@ override fun onCreate(savedInstanceState: Bundle?) {
## Step 7: Change the logout
+
+
```kotlin
CometChat.logout(chatCallback)
CometChatCalls.logout(callsCallback) // new: stops a ringing call, unregisters the push token
```
+
+
+```kotlin
+CometChatUIKit.logout(callback) // instead of CometChat.logout: logs out of Chat and Calls
+```
+
+
## If you drew your own incoming-call screen
@@ -298,7 +307,7 @@ To let users on the two flows call each other, turn on **Connect legacy ringing*
## Checklist
-- [ ] Dependencies bumped, `push-notifications-calls-android` added, `minSdk 26`
+- [ ] Dependencies bumped, `push-notifications-calls-android` added, `minSdk 26` (28 with the UI Kit)
- [ ] `PNConfiguration` has `setProviderId` and `setAnswerActivity`
- [ ] Firebase service passes every push to `handlePushNotification` with `message.sentTime`
- [ ] Manual token registration removed
diff --git a/calls/android/ringing.mdx b/calls/android/ringing.mdx
index bbf66dec2..5b212ad8b 100644
--- a/calls/android/ringing.mdx
+++ b/calls/android/ringing.mdx
@@ -551,7 +551,7 @@ To ring a group, call `initiateCall` with the group's GUID as `receiverUid` and
## Logout
-Log out of the Calls SDK when the user signs out (`CometChatCalls.logout`). On that logout the Push SDK stops a call still ringing for the old user and unregisters the push token. Logging out of the Chat SDK alone leaves the Calls SDK signed in, so a ring keeps playing for the old user and later declines go out with the old token.
+Log out of the Calls SDK when the user signs out (`CometChatCalls.logout`, or `CometChatUIKit.logout` when you use the UI Kit). On that logout the Push SDK stops a call still ringing for the old user and unregisters the push token. Logging out of the Chat SDK alone leaves the Calls SDK signed in, so a ring keeps playing for the old user and later declines go out with the old token.
End or cancel any call of your own before logging out; the Calls SDK cannot send after the session is gone.
diff --git a/calls/android/troubleshooting.mdx b/calls/android/troubleshooting.mdx
index 2d84c4019..bce5e7b30 100644
--- a/calls/android/troubleshooting.mdx
+++ b/calls/android/troubleshooting.mdx
@@ -55,7 +55,7 @@ sdk_version: "5.x"
| Rings, then the process dies before Answer | Call push sent at normal FCM priority (`BFGS denied`) | Send call pushes at high priority |
| Caller never sees "Ringing…" / answered | Caller joined right after `initiateCall` | Stay on the prepared session and join on `onParticipantJoined`. See [Ringing](/calls/android/ringing) |
| Audio call opens the camera | Session type not set at `initiateCall` | Pass `sessionSettings` with `setSessionType(SessionType.VOICE)` in `InitiateCallOptions` |
-| Ring keeps playing after logout | Only the Chat SDK was logged out | Call `CometChatCalls.logout` too |
+| Ring keeps playing after logout | Only the Chat SDK was logged out | Call `CometChatCalls.logout` too (or `CometChatUIKit.logout`) |
| App killed by OS during call | Missing foreground service notification | Show a persistent notification during active calls |
---
diff --git a/calls/android/voip-calling.mdx b/calls/android/voip-calling.mdx
index 91c0ca5ce..064e5183d 100644
--- a/calls/android/voip-calling.mdx
+++ b/calls/android/voip-calling.mdx
@@ -312,7 +312,7 @@ The high-priority push starts the app process and the SDK rings from its saved c
## Tokens and logout
-You do not register the push token: the SDK registers it on login and unregisters it on logout. Log out of the Calls SDK when the user signs out (`CometChatCalls.logout`). That stops a ring still playing for the old user and unregisters the token. `CometChat.logout` alone unregisters the token but does not stop a ring or sign out of the Calls SDK.
+You do not register the push token: the SDK registers it on login and unregisters it on logout. Log out of the Calls SDK when the user signs out (`CometChatCalls.logout`, or `CometChatUIKit.logout` with the UI Kit). That stops a ring still playing for the old user and unregisters the token. `CometChat.logout` alone unregisters the token but does not stop a ring or sign out of the Calls SDK.
## Your own incoming-call screen
@@ -408,7 +408,7 @@ Declare the screen so it can show over the lock screen and turn the display on,
The SDK's incoming-call screen and its call notifications take each colour from, in order:
1. a `CometChatPNIncomingCallTheme` set in code;
-2. the `cometchat_pn_call_*` colour resources, which your app can override by name;
+2. the `cometchat_pn_call_*` colour resources, which your app (or a UI Kit) can override by name;
3. the SDK's defaults.
Each colour is looked up on its own, so a theme can set only the colours it cares about.
@@ -518,6 +518,9 @@ The strings, drawables and layout of the screen (all prefixed `cometchat_pn_`) c
On Android 12 and later the Answer and Decline buttons in the notification are drawn by the system, so there only the accent colour and the avatar take your colours.
+
+If you use the CometChat UI Kit, it already overrides the colour resources with its own light and dark palette, and can pass your UI Kit theme as a `CometChatPNIncomingCallTheme`; see [Calling integration](/ui-kit/android/calling-integration). Your app's own theme and resources still win over the UI Kit's.
+
## Knowing about a call in your own code
diff --git a/docs.json b/docs.json
index a6aec0c15..6f8c673ca 100644
--- a/docs.json
+++ b/docs.json
@@ -1784,6 +1784,7 @@
"ui-kit/android/threaded-messages-header",
"ui-kit/android/incoming-call",
"ui-kit/android/outgoing-call",
+ "ui-kit/android/ongoing-call",
"ui-kit/android/call-buttons",
"ui-kit/android/call-logs",
"ui-kit/android/search",
diff --git a/notifications/android-push-notifications.mdx b/notifications/android-push-notifications.mdx
index 0fd8ccc30..26998a3ce 100644
--- a/notifications/android-push-notifications.mdx
+++ b/notifications/android-push-notifications.mdx
@@ -3,6 +3,14 @@ title: "Android Push Notifications"
description: "Set up FCM and the CometChat Push Notifications SDK 2.0 on Android for message notifications with inline reply and call ringing."
---
+
+ Reference implementations: `sample-app-kotlin-push` and `sample-app-compose-push`.
+
+
## What this guide covers
- FCM setup and the CometChat provider (credentials, Gradle, manifest).
@@ -42,22 +50,24 @@ From the same screen, click **Add Provider** to upload the Firebase service acco
## 2. Add dependencies (Gradle)
-Two CometChat artifacts, published in lockstep. The calls one is optional: add it to make incoming calls ring.
+The Push Notifications SDK is two artifacts, published in lockstep. The calls one is optional: add it to make incoming calls ring. The snippet also lists the UI Kit, or the Chat and Calls SDKs, that your app already uses.
```toml lines
[versions]
-minSdk = "26" # the Calls SDK's floor; 24 for the core push artifact alone
+minSdk = "28" # the UI Kit's floor; 26 without the UI Kit
compileSdk = "36"
targetSdk = "36"
-cometChatSdk = "5.0.7"
+cometChatUikit = "6.2.0"
+cometChatSdk = "5.1.1"
cometChatCalls = "5.1.0"
cometChatPush = "2.0.0"
firebaseBom = "33.7.0"
[libraries]
+cometchat-uikit = { group = "com.cometchat", name = "chatuikit-kotlin-android", version.ref = "cometChatUikit" }
cometchat-sdk = { group = "com.cometchat", name = "chat-sdk-android", version.ref = "cometChatSdk" }
cometchat-calls = { group = "com.cometchat", name = "calls-sdk-android", version.ref = "cometChatCalls" }
cometchat-push = { group = "com.cometchat", name = "push-notifications-android", version.ref = "cometChatPush" }
@@ -77,12 +87,16 @@ plugins {
}
android {
- defaultConfig { minSdk = 26 } // the Calls SDK's floor
+ defaultConfig { minSdk = 28 } // the UI Kit's floor; 26 with the Chat and Calls SDKs directly
}
dependencies {
- implementation(libs.cometchat.sdk) // chat; optional for a calls-only app
- implementation(libs.cometchat.calls) // needed with the calls module
+ // UI Kit app: the UI Kit does not bundle the Calls SDK, so add both
+ implementation(libs.cometchat.uikit) // or chatuikit-compose-android
+ implementation(libs.cometchat.calls) // needed for calling (enableCalling) and for the calls module
+ // SDK-direct app instead:
+ // implementation(libs.cometchat.sdk) // chat; optional for a calls-only app
+ // implementation(libs.cometchat.calls) // needed with the calls module
implementation(libs.cometchat.push)
implementation(libs.cometchat.push.calls) // only if you want incoming calls to ring
@@ -94,8 +108,9 @@ dependencies {
+- The UI Kit bundles neither the push artifacts nor the Calls SDK; a UI Kit app adds all three.
- Apply the `google-services` plugin and place `google-services.json` in the same module.
-- `minSdk`: the calls module needs 26, the Calls SDK's floor; the core artifact alone works from 24.
+- `minSdk`: 28 with the UI Kit (6.2.0 or later, which initialises the Calls SDK you add). Without the UI Kit, the calls module needs 26, the Calls SDK's floor; the core artifact alone works from 24.
- `compileSdk` 36 and Android Gradle Plugin 8.9.1 or later: the Push Notifications SDK 2.0 depends on `androidx.core` 1.18, which requires them.
- The Chat SDK is optional for the push SDK: a calls-only app ships without it. Chat pushes are still shown without it; delivery receipts, inline reply and tap deep links need it.
@@ -121,7 +136,7 @@ Everything else the ringer needs is declared by the SDK and merges in.
## 4. Initialise the SDK
-In `Application.onCreate`, on every app start, after the Chat and Calls SDKs are initialised and before the user logs in. Token registration on login and logout only runs in a process where `init` ran. The configuration is also saved, so a push that wakes a killed app can still be shown before your code runs.
+In `Application.onCreate`, on every app start, after the UI Kit (or the Chat and Calls SDKs) is initialised and before the user logs in. Token registration on login and logout only runs in a process where `init` ran. The configuration is also saved, so a push that wakes a killed app can still be shown before your code runs.
```kotlin lines
import com.cometchat.pushnotification.CometChatPushNotifications
@@ -142,7 +157,7 @@ CometChatPushNotifications.init(this, config)
| `setProviderId(id)` | none; **required** | Provider the device token is registered under. Without it no token is registered |
| `setNotificationSmallIcon(res)` | a system mail icon | Status-bar icon for chat notifications. Call notifications use the drawables `cometchat_pn_ic_notification_call` and `cometchat_pn_ic_notification_call_missed`; override those by name |
| `setInlineReplyEnabled(bool)` | `true` | Reply action on chat notifications |
-| `setAnswerActivity(Class)` | none; required with the calls module | Opened when the user answers a call, with the call in its extras; it joins the session. See [Join from the answer activity](/calls/android/voip-calling#step-4-join-from-the-answer-activity) |
+| `setAnswerActivity(Class)` | none; required with the calls module | Opened when the user answers a call, with the call in its extras; it joins the session. UI Kit apps use a small activity that opens `CometChatOngoingCallActivity`; see [Calling Integration](/ui-kit/android/calling-integration#step-3-configure-push-ringing). With the SDKs directly, see [Join from the answer activity](/calls/android/voip-calling#step-4-join-from-the-answer-activity) |
| `setIncomingCallActivity(Class?)` | the SDK's screen | Your own incoming-call screen |
| `setIncomingCallTheme(theme?)` | none | Colours of the SDK's incoming-call screen and call notifications, light and dark. Change it later with `CometChatPushNotifications.setIncomingCallTheme(context, theme)`; see [Theming the SDK's incoming-call screen](/calls/android/voip-calling#theming-the-sdks-incoming-call-screen) |
| `setMissedCallActivity(Class?)` | none | Opened when a missed-call notification is tapped |
@@ -199,7 +214,7 @@ class AppFCMService : FirebaseMessagingService() {
You do not register the device token. The SDK fetches it from Firebase and registers it when the user logs in, re-registers it when Firebase rotates it, and unregisters it on logout.
-Log out of both the Chat SDK and the Calls SDK. Only a Calls SDK logout stops a call still ringing for the old user; a Chat SDK logout unregisters the token but leaves the ring and the Calls SDK session alone.
+Log out through the UI Kit (`CometChatUIKit.logout`) or, with the SDKs directly, log out of both the Chat SDK and the Calls SDK. Only a Calls SDK logout stops a call still ringing for the old user; a Chat SDK logout unregisters the token but leaves the ring and the Calls SDK session alone.
### Permissions and ring readiness
@@ -302,7 +317,7 @@ CometChat.sendCustomMessage(customMessage, object : CometChat.CallbackListener
+#### `onCallInitiated`
+
+Fires when a 1:1 call was created, with the `Call`. Default: opens the Outgoing Call screen. Provide the callback to host the outgoing screen yourself.
+
+
+
+
+```kotlin lines
+callButtons.setOnCallInitiated { call ->
+ // Open your own outgoing screen with call.sessionId
+}
+```
+
+
+
+
+```kotlin lines
+CometChatCallButtons(
+ user = user,
+ onCallInitiated = { call -> /* host the outgoing screen yourself */ }
+)
+```
+
+
+
+
+#### `onStartDirectCall`
+
+Fires when a group call message was sent, with the `BaseMessage`. Default: opens the conference screen.
+
#### `onError`
-Fires on internal errors (network failure, auth issue, SDK exception).
+Fires when a call could not be started: another call is active (`ACTIVE_CALL`), the Calls SDK refused, or the network failed.
@@ -188,14 +220,20 @@ CometChatCallButtons(
-### Global UI Events (CometChatCallEvents)
+### Global UI Events (`CometChatEvents.callEvents`)
| Event | Fires when | Payload |
| --- | --- | --- |
-| `ccOutgoingCall` | An outgoing call is initiated | `Call` |
-| `ccCallAccepted` | A call is accepted by the recipient | `Call` |
-| `ccCallRejected` | A call is rejected by the recipient | `Call` |
-| `ccCallEnded` | A call is ended | `Call` |
+| `CometChatCallEvent.OutgoingCall` | An outgoing 1:1 call was created | `Call` |
+| `CometChatCallEvent.CallRejected` | The outgoing screen's End was tapped, or its ring timer (45 s) expired. Declined, busy and unanswered emit no global event | `Call` |
+
+### SDK Events (Real-Time, Automatic)
+
+| Calls SDK listener | Internal behavior |
+| --- | --- |
+| `IncomingCallListener.onIncomingCallEnded` | Re-enables the buttons after a call ends anywhere |
+
+A second call is refused with `ACTIVE_CALL` while a Calls SDK session is prepared or joined, while the UI Kit tracks an active call, or while a meeting is active.
---
@@ -207,6 +245,8 @@ CometChatCallButtons(
| `setGroup(group)` | `group = group` | Set the group to call (required for group calls) |
| `setOnVoiceCallClick { }` | `onVoiceCallClick = { }` | Override voice call button behavior |
| `setOnVideoCallClick { }` | `onVideoCallClick = { }` | Override video call button behavior |
+| `setOnCallInitiated { }` | `onCallInitiated = { }` | Override what happens once a 1:1 call is created |
+| `setOnStartDirectCall { }` | `onStartDirectCall = { }` | Override what happens once a group call message is sent |
| `setOnError { }` | `onError = { }` | Error callback |
| `setVoiceCallButtonVisibility(View.GONE)` | `hideVoiceCallButton = true` | Toggle voice call button |
| `setVideoCallButtonVisibility(View.GONE)` | `hideVideoCallButton = true` | Toggle video call button |
@@ -292,11 +332,11 @@ See [ViewModel & Data](/ui-kit/android/customization-viewmodel-data) for state o
View call history
-
- Incoming call notification with accept/reject
-
- Outgoing call screen with end-call button
+ Calling and ringing states before the answer
+
+
+ The in-call screen
Display user/group info in the toolbar
diff --git a/ui-kit/android/call-features.mdx b/ui-kit/android/call-features.mdx
index d410a4c2e..fd47ecbb9 100644
--- a/ui-kit/android/call-features.mdx
+++ b/ui-kit/android/call-features.mdx
@@ -7,149 +7,65 @@ description: "Add one-on-one and group audio/video calling to your Android app u
| Field | Value |
| --- | --- |
-| Kotlin (XML Views) | `com.cometchat:chatuikit-kotlin-android` + `com.cometchat:calls-sdk-android` |
-| Jetpack Compose | `com.cometchat:chatuikit-compose-android` + `com.cometchat:calls-sdk-android` |
-| Required setup | `CometChatUIKit.init()` then `CometChatUIKit.login()` — Calls SDK must also be installed |
-| Call features | Incoming Call, Outgoing Call, Call Logs, Call Buttons, Ongoing Call |
-| Key components | `CometChatCallButtons`, `CometChatIncomingCall`, `CometChatOutgoingCall`, `CometChatCallLogs`, `CometChatOngoingCall` |
-| Auto-detection | UI Kit automatically detects the Calls SDK and enables call UI components |
-| Related | [Getting Started](/ui-kit/android/getting-started), [Core Features](/ui-kit/android/core-features), [Call Log Details Guide](/ui-kit/android/guide-call-log-details) |
+| Kotlin (XML Views) | `com.cometchat:chatuikit-kotlin-android`; the app adds `calls-sdk-android` (not bundled with the UI Kit), `push-notifications-android` and `push-notifications-calls-android` |
+| Jetpack Compose | `com.cometchat:chatuikit-compose-android`; the app adds the same three artifacts |
+| Required setup | `CometChatUIKit.init()` with `setEnableCalling(true)`, `CometChatUIKit.login()`, `CometChatPushNotifications.init()` with `setAnswerActivity(CallAnswerActivity::class.java)` (a small activity that opens `CometChatOngoingCallActivity`), a `FirebaseMessagingService` that calls `handlePushNotification` |
+| Incoming calls | Rung by the Push Notifications SDK (notification, full-screen screen, ringtone), in every app state. No call listener, no incoming-call component |
+| Call features | Call Buttons, Outgoing Call, Ongoing Call, Call Logs, Call Log Details |
+| Key components | `CometChatCallButtons`, `CometChatOutgoingCall`, `CometChatOngoingCall` / `CometChatOngoingCallActivity`, `CometChatCallLogs` |
+| Deprecated / superseded | `CometChatIncomingCall` (deprecated, kept for a custom ring screen); Chat SDK `CometChat.addCallListener` / `initiateCall` / `acceptCall` / `rejectCall` / `endCall` (superseded, no longer used) |
+| Related | [Calling Integration](/ui-kit/android/calling-integration), [Push Ringing](/calls/android/voip-calling), [Migrating from Legacy Ringing](/calls/android/migrating-from-legacy-ringing), [Call Log Details Guide](/ui-kit/android/guide-call-log-details) |
-CometChat's Calls feature allows you to seamlessly integrate one-on-one as well as group audio and video calling capabilities into your application. This document provides a technical overview of these features, as implemented in the Android UI Kit.
-
-## Integration
-
-First, make sure that you've correctly integrated the UI Kit library into your project. If you haven't done this yet or are facing difficulties, refer to our [Getting Started](/ui-kit/android/getting-started) guide.
-
-Once you've successfully integrated the UI Kit, the next step is to add the CometChat Calls SDK to your project. This is necessary to enable the calling features in the UI Kit.
-
-### Step 1: Add Calls SDK Dependency
+CometChat's Calls feature lets you add one-on-one and group audio and video calling to your application. This page is an overview of how calling works in the Android UI Kit and which component handles each part.
+
+## How a call works
+
+```mermaid
+sequenceDiagram
+ participant A as Caller · UI Kit
+ participant BE as CometChat backend
+ participant P as Callee · Push SDK
+ participant U as Callee · UI Kit
+
+ A->>BE: Call button → CometChatCalls.initiateCall
+ Note over A: Outgoing call screen: "calling…"
+ BE-->>P: Push (initiated)
+ P->>P: Rings: notification, full-screen screen, ringtone
+ P-->>A: Callee's device reached → "ringing…"
+ P->>U: Answer → your answer activity → CometChatOngoingCallActivity
+ U->>U: joinSession
+ U-->>A: Callee joined → caller joins
+ Note over A,U: In call. Hang-up leaves the session on both sides.
+```
-Add the following dependency to your `build.gradle.kts` file:
+- **Placing a call** is done by the UI Kit through the Calls SDK. The Chat SDK is not involved.
+- **Ringing the callee** is done by the Push Notifications SDK from the call push. It rings with the app in the foreground, in the background or not running, draws the incoming-call screen, asks for the microphone (and the camera for a video call) on Answer, and opens the UI Kit's call screen.
+- **Decline, timeout, busy and missed calls** are handled by the Push SDK. The caller's outgoing screen closes with the reason.
+- **The call itself** runs in the Calls SDK session hosted by `CometChatOngoingCallActivity`; hanging up leaves the session.
-
-
-```kotlin build.gradle.kts
-dependencies {
- implementation("com.cometchat:chatuikit-kotlin-android:6.1.0")
- implementation("com.cometchat:calls-sdk-android:5.0.1")
-}
-```
-
-
-
-```kotlin build.gradle.kts
-dependencies {
- implementation("com.cometchat:chatuikit-compose-android:6.1.0")
- implementation("com.cometchat:calls-sdk-android:5.0.1")
-}
-```
-
-
+## Integration
-After adding this dependency, sync your project. The Android UI Kit will automatically detect the Calls SDK and activate the calling features.
+Three steps, all on the [Calling Integration](/ui-kit/android/calling-integration) page:
-### Step 2: Verify Call Buttons Appear
+1. `setEnableCalling(true)` in `UIKitSettings`. The UI Kit initialises the Calls SDK and logs the user into it.
+2. `CometChatPushNotifications.init()` with `setProviderId` and `setAnswerActivity(CallAnswerActivity::class.java)`, a small answer activity that opens `CometChatOngoingCallActivity`, plus a three-line `FirebaseMessagingService`.
+3. `ensureRingReady` after sign-in. `POST_NOTIFICATIONS`, `RECORD_AUDIO` and `CAMERA` merge into your manifest from the SDKs; they only need granting at runtime.
-Once the Calls SDK is integrated, you will see the `CometChatCallButtons` component automatically rendered in the [MessageHeader](/ui-kit/android/message-header) component. This provides users with quick access to initiate audio and video calls.
+Once calling is active, the call buttons render in the [MessageHeader](/ui-kit/android/message-header):
-### Step 3: Add Call Listener for Incoming Calls
-
-To receive incoming calls globally in your app, you will need to add a `CallListener`. This should be added before you initialize the CometChat UI Kit. We recommend creating a custom Application class and adding the call listener there.
-
-When an incoming call is received, you can display the `CometChatIncomingCall` component using the current activity context.
-
-
-
-```kotlin
-class BaseApplication : Application() {
-
- companion object {
- private val LISTENER_ID = "${BaseApplication::class.java.simpleName}${System.currentTimeMillis()}"
- }
-
- override fun onCreate() {
- super.onCreate()
-
- CometChat.addCallListener(LISTENER_ID, object : CometChat.CallListener {
- override fun onIncomingCallReceived(call: Call) {
- // Get the current activity context
- val currentActivity = getCurrentActivity() // Implement this method
-
- currentActivity?.let {
- // Create and display the incoming call component
- val incomingCallView = CometChatIncomingCall(it)
- incomingCallView.call = call
- incomingCallView.fitsSystemWindows = true
- incomingCallView.onError = OnError { exception ->
- // Handle errors
- }
-
- // Display the component (e.g., as dialog or snackbar)
- }
- }
-
- override fun onOutgoingCallAccepted(call: Call) {
- // Handle outgoing call acceptance
- }
-
- override fun onOutgoingCallRejected(call: Call) {
- // Handle outgoing call rejection
- }
-
- override fun onIncomingCallCancelled(call: Call) {
- // Handle incoming call cancellation
- }
- })
- }
-}
-```
-
-
-
-```kotlin
-class BaseApplication : Application() {
-
- companion object {
- private val LISTENER_ID = "${BaseApplication::class.java.simpleName}${System.currentTimeMillis()}"
- }
-
- override fun onCreate() {
- super.onCreate()
-
- CometChat.addCallListener(LISTENER_ID, object : CometChat.CallListener {
- override fun onIncomingCallReceived(call: Call) {
- CometChatCallActivity.launchIncomingCallScreen(this@BaseApplication, call, null)
- // Pass null or IncomingCallConfiguration if need to configure CometChatIncomingCall component
- }
-
- override fun onOutgoingCallAccepted(call: Call) {
- // Handle outgoing call acceptance
- }
-
- override fun onOutgoingCallRejected(call: Call) {
- // Handle outgoing call rejection
- }
-
- override fun onIncomingCallCancelled(call: Call) {
- // Handle incoming call cancellation
- }
- })
- }
-}
-```
-
-
+
+There is no call listener to add. Apps that registered `CometChat.addCallListener` to show `CometChatIncomingCall` should remove it; the Chat SDK's calling APIs are superseded and no longer used. See [Migrating from Legacy Ringing](/calls/android/migrating-from-legacy-ringing).
+
## Call Components
-The CometChat Android UI Kit provides five main components for implementing calling features in your app. Each component handles a specific part of the calling experience.
+The CometChat Android UI Kit provides these components for calling. Each handles a specific part of the experience.
### Call Buttons
@@ -163,17 +79,17 @@ The `CometChatCallButtons` component provides users with quick access to initiat
### Incoming Call
-The `CometChatIncomingCall` component displays when a user receives an incoming call. It provides a full-screen interface showing caller information and call controls.
+Incoming calls are rung and drawn by the Push Notifications SDK, so there is no UI Kit component to place. Its screen uses the UI Kit's default colours, and follows your own UI Kit theme, light and dark, once you pass it with `setIncomingCallTheme` ([Styling the incoming-call screen](/ui-kit/android/calling-integration#styling-the-incoming-call-screen)). The deprecated `CometChatIncomingCall` component remains for apps that register their own ring screen.
-[Learn more about Incoming Call →](/ui-kit/android/incoming-call)
+[Incoming Call (deprecated) →](/ui-kit/android/incoming-call)
### Outgoing Call
-The `CometChatOutgoingCall` component manages the outgoing call experience. It displays while waiting for the recipient to answer and automatically transitions to the active call screen once accepted.
+The `CometChatOutgoingCall` component manages the outgoing call experience. It shows "calling…", then "ringing…" once the callee's device is reached, closes with the reason if the call is declined, busy or unanswered, and transitions to the active call screen once the callee joins.
@@ -193,7 +109,9 @@ The `CometChatCallLogs` component displays a history of all call activities, inc
### Ongoing Call
-The `CometChatOngoingCall` component renders the active call screen with video feeds, mute/unmute controls, camera toggle, and end-call actions.
+The `CometChatOngoingCall` component renders the active call screen with video feeds, mute/unmute controls, camera toggle, and end-call actions. `CometChatOngoingCallActivity` hosts it, supports picture-in-picture, and is the activity the Push SDK opens when the user answers an incoming call.
+
+[Learn more about Ongoing Call →](/ui-kit/android/ongoing-call)
### Call Log Details
diff --git a/ui-kit/android/call-logs.mdx b/ui-kit/android/call-logs.mdx
index 03a2d90f5..88ddb3f8b 100644
--- a/ui-kit/android/call-logs.mdx
+++ b/ui-kit/android/call-logs.mdx
@@ -3,7 +3,7 @@ title: "Call Logs"
description: "Scrollable list of call logs for the logged-in user with caller names, avatars, call status, and timestamps."
---
-`CometChatCallLogs` renders a scrollable list of call logs for the logged-in user with caller names, avatars, call status indicators, and timestamps.
+`CometChatCallLogs` renders a scrollable list of call logs for the logged-in user with caller names, avatars, call status indicators, and timestamps. The call icon on a row calls that user back through the Calls SDK and opens the [Outgoing Call](/ui-kit/android/outgoing-call) screen. The list refreshes itself when a call starts or ends.
@@ -13,24 +13,29 @@ description: "Scrollable list of call logs for the logged-in user with caller na
-**Requires the Calls SDK AND calling to be initialized — this component CRASHES otherwise.**
-`CometChatCallLogs` builds a `CallLogRequest` in its ViewModel's `init {}`, with **no availability
-check**, so simply placing it on a screen is fatal unless both conditions hold. Verified on device:
+**Requires the Calls SDK AND calling to be enabled.** `CometChatCallLogs` fetches through the Calls
+SDK, which the UI Kit does not bundle. Since UI Kit 6.2.0 the component checks for the SDK before it
+fetches, so a missing artifact no longer crashes the app (UI Kits before 6.2.0 threw
+`NoClassDefFoundError` as soon as the component was placed on a screen):
| Calls artifact | `uiKit.enableCalling` | Result |
|---|---|---|
-| absent | anything | `java.lang.NoClassDefFoundError: …CallLogRequest$CallLogRequestBuilder` |
-| present | `false` | `java.lang.RuntimeException: Please call the CometChatCalls.init() method …` |
+| absent | anything | Empty list; logcat warns `Calls SDK is not on the classpath; skipping call-log fetch` |
+| present | `false` | Error state; the Calls SDK reports `Please call the CometChatCalls.init() method …` |
| present | `true` | renders (empty/error state when there are no logs) |
Both are required:
```kotlin
-// app/build.gradle.kts
-implementation("com.cometchat:calls-sdk-android:5.0.+")
+// app/build.gradle.kts — the UI Kit does not bundle the Calls SDK
+implementation("com.cometchat:calls-sdk-android:5.1.0")
+```
+```kotlin
+// UIKitSettings — this is what makes the UI Kit init CometChatCalls
+UIKitSettings.UIKitSettingsBuilder().setEnableCalling(true)
```
```json
-// app/src/main/assets/cometchat-settings.json — this is what makes the UI Kit init CometChatCalls
+// or app/src/main/assets/cometchat-settings.json
{ "uiKit": { "enableCalling": true } }
```
@@ -273,9 +278,34 @@ CometChatCallLogs(
+#### `onCallIconClick`
+
+Fires when the call icon on a row is tapped. Default: calls the other participant back with the log's call type (`audio` or `video`) and opens the Outgoing Call screen. Provide the callback to replace that.
+
+
+
+
+```kotlin lines
+callLogs.setOnCallTypeIconClick { callLog ->
+ // Custom call-back logic
+}
+```
+
+
+
+
+```kotlin lines
+CometChatCallLogs(
+ onCallIconClick = { callLog -> /* custom call-back logic */ }
+)
+```
+
+
+
+
#### `onError`
-Fires on internal errors (network failure, auth issue, SDK exception).
+Fires when the list could not be loaded, and when a call-back from the list could not be placed (another call active, the Calls SDK refused). A failed call-back does not replace the list with an error state.
@@ -362,6 +392,15 @@ CometChatCallLogs(
| `setToolbarVisibility(View.GONE)` | `hideToolbar = true` | Toggle toolbar |
| `setSeparatorVisibility(View.GONE)` | `hideSeparator = true` | Toggle list separators |
| `setTitle("Call History")` | `title = "Call History"` | Custom toolbar title |
+| `setOnCallTypeIconClick { }` | `onCallIconClick = { }` | Override the call-back on the row's call icon |
+
+### Call status
+
+Rows are classified with `CallLogsUtils`: a call is **missed** when it was incoming and its status is `unanswered`, `missed` or `cancelled`; **video** when its type is `video` or `audio/video`. Statuses come from `UIKitConstants.CallStatusConstants`; the Calls SDK's `CometChatCallsConstants` is deprecated.
+
+### Live updates
+
+The list registers a Calls SDK `IncomingCallListener` and reloads when a call for the logged-in user starts or ends, on either side, and again when the connection is restored. No Chat SDK listener is involved.
---
@@ -802,6 +841,8 @@ CometChatCallLogs(
+`CometChatCallLogsViewModel` also exposes `initiateCall(callLog)` / `initiateCall(callLog, callType)` to call back from your own row UI, `initiatedCall` (the `Call` to open an outgoing screen with) and `callInitiationError`.
+
See [ViewModel & Data](/ui-kit/android/customization-viewmodel-data) for ListOperations, state observation, and custom repositories.
---
diff --git a/ui-kit/android/calling-integration.mdx b/ui-kit/android/calling-integration.mdx
index dca02cef3..9213c2afd 100644
--- a/ui-kit/android/calling-integration.mdx
+++ b/ui-kit/android/calling-integration.mdx
@@ -1,43 +1,50 @@
---
title: "Calling Integration"
-description: "Add voice and video calling to your Android UI Kit application using chatuikit-kotlin or chatuikit-compose."
+description: "Add voice and video calling to your Android UI Kit application using chatuikit-kotlin or chatuikit-compose: Calls SDK, push ringing, permissions and logout."
---
## Overview
-This guide walks you through adding voice and video calling capabilities to your Android application using the CometChat UI Kit.
+With calling enabled, the UI Kit places calls from the message header and call logs, shows the outgoing call screen, and hosts the in-call screen. Incoming calls ring through the CometChat Push Notifications SDK: it shows the call notification and the full-screen incoming-call screen, plays the ringtone, and opens your answer activity when the user answers, which hands the call to the UI Kit's call screen, with the app in the foreground, in the background or not running at all.
Make sure you've completed the [Getting Started](/ui-kit/android/getting-started) guide before proceeding.
-
-
-**`enableCalling: true` makes the Calls SDK dependency mandatory — otherwise the app crashes at
-launch.** With the flag set, `CometChatUIKit.initFromSettings()` calls `initCometChatCalls()`, which
-throws if `com.cometchat:calls-sdk-android` is absent:
-
-```
-java.lang.NoClassDefFoundError: Failed resolution of:
- Lcom/cometchat/calls/core/CometChatCalls$SessionSettingsBuilder;
- at com.cometchat.uikit.core.CometChatUIKit.initCometChatCalls(CometChatUIKit.kt:229)
+```mermaid
+sequenceDiagram
+ participant A as Caller · UI Kit
+ participant BE as CometChat backend
+ participant P as Callee · Push SDK
+ participant U as Callee · UI Kit
+
+ A->>BE: Call button → CometChatCalls.initiateCall
+ Note over A: Outgoing call screen: "calling…"
+ BE-->>P: Push (initiated)
+ P->>P: Rings: notification, full-screen screen, ringtone
+ P-->>A: Callee's device reached → "ringing…"
+ P->>U: Answer → mic/camera prompt → your answer activity → CometChatOngoingCallActivity
+ U->>U: joinSession
+ U-->>A: Callee joined → caller joins
+ Note over A,U: In call. Hang-up leaves the session on both sides.
```
-The crash happens in `onCreate`, **before any UI renders** — the flag name gives no hint that a
-dependency is implied. Change the flag and the dependency together, or leave `enableCalling` as
-`false` in an app that does not use calling.
-
+What you write: the UI Kit settings with calling enabled, the Push SDK configuration, a small answer activity, a three-line Firebase service, and the manifest permissions. There is no call listener to register and no incoming-call UI to show.
-## Add the Calls SDK
+## Step 1: Dependencies
-Add the CometChat Calls SDK dependency alongside your chosen UI Kit module:
+The UI Kit does not bundle the Calls SDK: `calls-sdk-android` is a compile-only dependency of the kit, so your app declares it. Add the UI Kit module, the Calls SDK, the two Push Notifications SDK artifacts and Firebase Messaging. List the UI Kit before the Push SDK artifacts, so the UI Kit's incoming-call colours take effect (see [Styling the incoming-call screen](#styling-the-incoming-call-screen)):
```kotlin build.gradle.kts
dependencies {
- implementation("com.cometchat:chatuikit-kotlin-android:6.1.0")
- implementation("com.cometchat:calls-sdk-android:5.0.1")
+ implementation("com.cometchat:chatuikit-kotlin-android:6.2.0")
+ implementation("com.cometchat:calls-sdk-android:5.1.0")
+ implementation("com.cometchat:push-notifications-android:2.0.0")
+ implementation("com.cometchat:push-notifications-calls-android:2.0.0")
+ implementation(platform("com.google.firebase:firebase-bom:33.7.0"))
+ implementation("com.google.firebase:firebase-messaging")
}
```
@@ -45,14 +52,218 @@ dependencies {
```kotlin build.gradle.kts
dependencies {
- implementation("com.cometchat:chatuikit-compose-android:6.1.0")
- implementation("com.cometchat:calls-sdk-android:5.0.1")
+ implementation("com.cometchat:chatuikit-compose-android:6.2.0")
+ implementation("com.cometchat:calls-sdk-android:5.1.0")
+ implementation("com.cometchat:push-notifications-android:2.0.0")
+ implementation("com.cometchat:push-notifications-calls-android:2.0.0")
+ implementation(platform("com.google.firebase:firebase-bom:33.7.0"))
+ implementation("com.google.firebase:firebase-messaging")
}
```
-After adding this dependency, the Android UI Kit will automatically detect it and activate the calling features.
+Apply the `google-services` plugin and put `google-services.json` in the app module. `minSdk` must be 28 or higher, the UI Kit's minimum.
+
+
+**`setEnableCalling(true)` and the `calls-sdk-android` artifact are a pair.** UI Kit 6.2.0 checks for the Calls SDK during `init`: with the flag on and the artifact missing it logs `enableCalling=true but the CometChat Calls SDK is not on the classpath; skipping Calls init`, finishes chat init without calling. The calling components then fail quietly, each with its own `Calls SDK is not on the classpath` warning in logcat: Call Logs shows an empty list, the call buttons fail with `CALLS_SDK_MISSING`, and the call screen does not start. UI Kits before 6.2.0 crashed at launch with `NoClassDefFoundError` instead. The UI Kit requires `calls-sdk-android` 5.1 or newer and `push-notifications-android` / `push-notifications-calls-android` 2.0 or newer.
+
+
+## Step 2: Enable calling in the UI Kit
+
+Set `setEnableCalling(true)` in `UIKitSettings`. `CometChatUIKit.init` then initialises the Calls SDK with the same app id, region and hosts, and `CometChatUIKit.login` logs the user into it. Do not call `CometChatCalls.init` yourself: the Calls SDK keeps the hosts of its first initialisation.
+
+
+
+```kotlin
+val settings = UIKitSettings.UIKitSettingsBuilder()
+ .setAppId(APP_ID)
+ .setRegion(REGION)
+ .setAuthKey(AUTH_KEY)
+ .subscribePresenceForAllUsers()
+ .setEnableCalling(true)
+ .build()
+
+CometChatUIKit.init(this, settings, object : CometChat.CallbackListener() {
+ override fun onSuccess(result: String) { initPushNotifications() }
+ override fun onError(e: CometChatException) { Log.e(TAG, "init failed: ${e.message}") }
+})
+```
+
+
+```kotlin
+val settings = UIKitSettings.UIKitSettingsBuilder()
+ .setAppId(APP_ID)
+ .setRegion(REGION)
+ .setAuthKey(AUTH_KEY)
+ .subscribePresenceForAllUsers()
+ .setEnableCalling(true)
+ .build()
+
+CometChatUIKit.init(this, settings, object : CometChat.CallbackListener() {
+ override fun onSuccess(result: String) { initPushNotifications() }
+ override fun onError(e: CometChatException) { Log.e(TAG, "init failed: ${e.message}") }
+})
+```
+
+
+
+Run this in `Application.onCreate` from stored credentials, not only after a screen opens. A push can start the app process to ring a call, and the answer must find a ready SDK.
+
+
+**Enabling calling in an app that already has logged-in users requires a re-login.** `CometChatUIKit.login()` short-circuits when the same uid already has a persisted session, so the Calls SDK never logs in: the Calls SDK has no auth token and `CometChatCallLogs` shows a silent error state (logcat: `User auth token cannot be null`). After calling is first enabled, log out and log in again (`CometChatUIKit.logout(...)` then `CometChatUIKit.login(...)`), and wire `onError` on `CometChatCallLogs` during development so this failure is visible.
+
+
+## Step 3: Configure push ringing
+
+Initialise the Push Notifications SDK once, after the UI Kit. It needs the FCM provider id and an answer activity: the screen the Push SDK opens when the user taps **Answer**.
+
+```kotlin
+private fun initPushNotifications() {
+ val config = PNConfiguration.Builder(APP_ID, REGION)
+ .setProviderId(PROVIDER_ID) // FCM provider id from the dashboard
+ .setAnswerActivity(CallAnswerActivity::class.java) // opened on Answer, call in its intent
+ .setNotificationSmallIcon(R.drawable.ic_notification)
+ .build()
+ CometChatPushNotifications.init(this, config)
+}
+```
+
+The UI Kit does not depend on the Push SDK, so the answer activity is a small class in your app. It reads the answered call from its intent, opens the UI Kit's call screen with it, and finishes:
+
+
+
+```kotlin
+import com.cometchat.chat.constants.CometChatConstants
+import com.cometchat.pushnotification.calls.CallBundles
+import com.cometchat.uikit.core.utils.CallRingingUtils
+import com.cometchat.uikit.kotlin.presentation.ongoingcall.ui.CometChatOngoingCallActivity
+
+class CallAnswerActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setShowWhenLocked(true)
+ setTurnScreenOn(true)
+
+ val incoming = CallBundles.getCall(intent) ?: return finish()
+ val call = CallRingingUtils.buildIncomingCall(
+ sessionId = incoming.sessionId,
+ callType = if (incoming.isVideo) CometChatConstants.CALL_TYPE_VIDEO else CometChatConstants.CALL_TYPE_AUDIO,
+ receiverType = incoming.receiverType,
+ receiverUid = incoming.receiverUid,
+ callerUid = incoming.callerUid,
+ callerName = incoming.callerName,
+ callerAvatarUrl = incoming.callerAvatarUrl,
+ initiatedAtMs = incoming.initiatedAtMs,
+ conversationId = incoming.conversationId
+ )
+ CometChatOngoingCallActivity.launchOngoingCallActivity(this, call)
+ finish()
+ }
+}
+```
+
+
+```kotlin
+import com.cometchat.chat.constants.CometChatConstants
+import com.cometchat.pushnotification.calls.CallBundles
+import com.cometchat.uikit.core.utils.CallRingingUtils
+import com.cometchat.uikit.compose.presentation.ongoingcall.ui.CometChatOngoingCallActivity
+
+class CallAnswerActivity : Activity() {
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setShowWhenLocked(true)
+ setTurnScreenOn(true)
+
+ val incoming = CallBundles.getCall(intent) ?: return finish()
+ val call = CallRingingUtils.buildIncomingCall(
+ sessionId = incoming.sessionId,
+ callType = if (incoming.isVideo) CometChatConstants.CALL_TYPE_VIDEO else CometChatConstants.CALL_TYPE_AUDIO,
+ receiverType = incoming.receiverType,
+ receiverUid = incoming.receiverUid,
+ callerUid = incoming.callerUid,
+ callerName = incoming.callerName,
+ callerAvatarUrl = incoming.callerAvatarUrl,
+ initiatedAtMs = incoming.initiatedAtMs,
+ conversationId = incoming.conversationId
+ )
+ CometChatOngoingCallActivity.launchOngoingCallActivity(this, call)
+ finish()
+ }
+}
+```
+
+
+
+```xml AndroidManifest.xml
+
+```
+
+Then hand every push to the SDK from your Firebase service:
+
+```kotlin
+class AppFCMService : FirebaseMessagingService() {
+ override fun onMessageReceived(message: RemoteMessage) {
+ CometChatPushNotifications.handlePushNotification(this, message.data, message.sentTime)
+ }
+ override fun onNewToken(token: String) {
+ CometChatPushNotifications.handleTokenRefresh(token)
+ }
+}
+```
+
+```xml AndroidManifest.xml
+
+
+
+
+
+```
+
+The push token is registered on login and unregistered on logout by the SDK. Do not skip call pushes when the app is in the foreground: the push is the only incoming-call signal, in every app state. Call pushes must be sent at high FCM priority.
+
+To colour the incoming-call screen with your UI Kit theme, see [Styling the incoming-call screen](#styling-the-incoming-call-screen). The other `PNConfiguration` options (ring timeout, missed-call notifications, your own ring screen) are on the Calls SDK's [Push Ringing](/calls/android/voip-calling) page.
+
+## Step 4: Permissions
+
+There is nothing to declare. `RECORD_AUDIO`, `CAMERA` and `POST_NOTIFICATIONS` merge into your manifest from the UI Kit, the Calls SDK and the Push SDK's calls module, along with the permissions ringing needs. You only request them at runtime:
+
+- **Notifications**: after sign-in, call `CometChatPushNotifications.ensureRingReady(activity) { result -> … }` (snippet below) and show the user any `RingReadiness` other than `READY`. On Android 13+ it asks for `POST_NOTIFICATIONS` itself when it is missing, and reports `NOTIFICATIONS_OFF` while that prompt is still on screen. Notifications off, a muted call channel, a revoked full-screen intent and Do Not Disturb all silence calls with no error.
+- **Microphone and camera**: the Push SDK asks when the user answers, and the UI Kit's call screen asks once more when it opens; a denied microphone joins muted. Ask at sign-in as well if you do not want the first call to be where the user meets the prompt.
+
+```kotlin
+CometChatPushNotifications.ensureRingReady(this) { result ->
+ val readiness = result.valueOrNull() ?: return@ensureRingReady
+ if (readiness != RingReadiness.READY) {
+ Toast.makeText(this, "Incoming calls may not ring: $readiness", Toast.LENGTH_LONG).show()
+ }
+}
+```
+
+If your app asks for other permissions at sign-in, call `ensureRingReady` after those requests have returned; Android drops a permission request made while another dialog is open.
+
+## Step 5: Log out through the UI Kit
+
+```kotlin
+CometChatUIKit.logout(object : CometChat.CallbackListener() {
+ override fun onSuccess(s: String) { /* go to login */ }
+ override fun onError(e: CometChatException) { }
+})
+```
+
+`CometChatUIKit.logout` ends a call in progress, cancels an outgoing call still ringing, then logs out of the Chat SDK and the Calls SDK; the Push SDK stops a ring still playing for the old user and unregisters the token. `CometChat.logout` alone does none of the calling clean-up.
+
+## What the UI Kit does from here
Once calling is active, the voice and video call buttons render in the [MessageHeader](/ui-kit/android/message-header):
@@ -74,91 +285,108 @@ CometChatMessageHeader(
-
-**Enabling calling in an app that already has logged-in users requires a re-login.** `CometChatUIKit.login()` short-circuits when the same uid already has a persisted session — it returns `onSuccess` without logging the Calls SDK in, so the Calls SDK has no auth token and `CometChatCallLogs` shows a silent error state (logcat: `User auth token cannot be null`). After calling is first enabled, log out and log in again (`CometChatUIKit.logout(...)` then `CometChatUIKit.login(...)`), and wire `onError` on `CometChatCallLogs` during development so this failure is visible.
-
-
-## Set Up Call Listener
+| Moment | What happens | Component |
+|--------|--------------|-----------|
+| Tap a call button | `CometChatCalls.initiateCall`, then the outgoing screen opens | [Call Buttons](/ui-kit/android/call-buttons) |
+| Waiting | "calling…", then "ringing…" when the callee's device is reached; End cancels the call; 45 s with no answer ends it as unanswered | [Outgoing Call](/ui-kit/android/outgoing-call) |
+| Callee declines / is busy / does not answer | The outgoing screen closes with the reason | Outgoing Call |
+| Incoming call | The Push SDK rings and draws the incoming-call screen | Push SDK (no UI Kit component) |
+| Answer | Your answer activity opens `CometChatOngoingCallActivity`, which joins the session | [Ongoing Call](/ui-kit/android/ongoing-call) |
+| Hang-up | The session is left; the other side's screen closes | Ongoing Call |
+| Call history | Call logs list from the Calls SDK; the call icon calls back | [Call Logs](/ui-kit/android/call-logs) |
+| Group call | The group call button sends a meeting message; tapping it joins the conference | Call Buttons, Ongoing Call |
+
+To react to calls in your own code (a badge, analytics, refreshing a screen), listen on the Calls SDK with `CometChatCalls.addIncomingCallListener`, the same callback the UI Kit uses internally. Do not use the Chat SDK's `CallListener`; it is superseded and fires nothing in this flow.
+
+## Placing a call from your own screen
+
+```kotlin
+lifecycleScope.launch {
+ CallInitiationUtils.initiateUserCall(user.uid, CometChatConstants.CALL_TYPE_AUDIO, user)
+ .onSuccess { call -> CometChatCallActivity.launchOutgoingCallScreen(context, call, null) }
+ .onFailure { e -> Log.e(TAG, "Could not start the call: ${e.message}") }
+}
+```
+
+`CometChatCallActivity` is `com.cometchat.uikit.kotlin.calls.CometChatCallActivity` for XML Views and `com.cometchat.uikit.compose.calls.CometChatCallActivity` for Compose. `initiateUserCall` fails with `CALL_IN_PROGRESS` while another start is in flight; it does not check for a call already in progress, so do that yourself, or use [Call Buttons](/ui-kit/android/call-buttons), which refuse with `ACTIVE_CALL` while another session is active.
+
+## Styling the incoming-call screen
-To receive incoming calls globally in your app, add a `CallListener` before initializing the CometChat UI Kit. We recommend creating a custom Application class:
+The incoming-call screen belongs to the Push SDK, which draws it even when your app is not running, so it cannot read the UI Kit theme at that moment. Instead you pass it your UI Kit theme once, as a `CometChatPNIncomingCallTheme`, when you initialise push. Light and dark are both included, and the screen and the call notifications then follow your theme.
+
+The UI Kit reads its own theme into a `CometChatIncomingCallTheme`:
```kotlin
-class BaseApplication : Application() {
- companion object {
- private val LISTENER_ID = "${BaseApplication::class.java.simpleName}${System.currentTimeMillis()}"
- }
-
- override fun onCreate() {
- super.onCreate()
- CometChat.addCallListener(LISTENER_ID, object : CometChat.CallListener {
- override fun onIncomingCallReceived(call: Call) {
- // Get the current activity context
- val currentActivity = getCurrentActivity() // Implement this method
-
- currentActivity?.let {
- val incomingCallView = CometChatIncomingCall(it)
- incomingCallView.call = call
- incomingCallView.fitsSystemWindows = true
- incomingCallView.onError = OnError { exception ->
- // Handle errors
- }
-
- // Display the component (e.g., as dialog or full-screen overlay)
- }
- }
-
- override fun onOutgoingCallAccepted(call: Call) {
- // Handle accepted outgoing call
- }
-
- override fun onOutgoingCallRejected(call: Call) {
- // Handle rejected outgoing call
- }
-
- override fun onIncomingCallCancelled(call: Call) {
- // Handle cancelled incoming call
- }
- })
- }
-}
+// Reads your app's manifest theme (based on CometChatTheme.DayNight), light and dark
+val uikitTheme = CometChatTheme.getIncomingCallTheme(this)
```
-
```kotlin
-class BaseApplication : Application() {
- companion object {
- private val LISTENER_ID = "${BaseApplication::class.java.simpleName}${System.currentTimeMillis()}"
- }
-
- override fun onCreate() {
- super.onCreate()
- CometChat.addCallListener(LISTENER_ID, object : CometChat.CallListener {
- override fun onIncomingCallReceived(call: Call) {
- CometChatCallActivity.launchIncomingCallScreen(this@BaseApplication, call, null)
- // Pass null or IncomingCallConfiguration if need to configure CometChatIncomingCall component
- }
-
- override fun onOutgoingCallAccepted(call: Call) {
- // Handle accepted outgoing call
- }
-
- override fun onOutgoingCallRejected(call: Call) {
- // Handle rejected outgoing call
- }
-
- override fun onIncomingCallCancelled(call: Call) {
- // Handle cancelled incoming call
- }
- })
- }
-}
+// The same colour schemes you give CometChatTheme { }
+val uikitTheme = incomingCallTheme(light = lightScheme, dark = darkScheme)
```
+
+The UI Kit has no dependency on the Push SDK, so a small function in your app copies the colours across. The field names match one to one:
+
+```kotlin calls/IncomingCallTheme.kt
+fun CometChatIncomingCallTheme.toCometChatPNIncomingCallTheme(): CometChatPNIncomingCallTheme =
+ CometChatPNIncomingCallTheme.Builder()
+ .setLightColors(light.toPNColors())
+ .setDarkColors(dark.toPNColors())
+ .build()
+
+private fun CometChatIncomingCallTheme.Colors.toPNColors(): CometChatPNIncomingCallTheme.Colors =
+ CometChatPNIncomingCallTheme.Colors.Builder()
+ .setBackground(background)
+ .setCallerName(callerName)
+ .setSubtitle(subtitle)
+ .setAvatarBackground(avatarBackground)
+ .setAvatarText(avatarText)
+ .setAcceptButton(acceptButton)
+ .setAcceptIcon(acceptIcon)
+ .setAcceptLabel(acceptLabel)
+ .setDeclineButton(declineButton)
+ .setDeclineIcon(declineIcon)
+ .setDeclineLabel(declineLabel)
+ .build()
+```
+
+Then add one line to the configuration from [Step 3](#step-3-configure-push-ringing):
+
+```kotlin
+val config = PNConfiguration.Builder(APP_ID, REGION)
+ // ...
+ .setIncomingCallTheme(uikitTheme.toCometChatPNIncomingCallTheme())
+ .build()
+```
+
+If your app changes its UI Kit colours while running, pass the new theme with `CometChatPushNotifications.setIncomingCallTheme(context, theme)`.
+
+Each field comes from one UI Kit colour:
+
+| Incoming-call field | UI Kit colour |
+|---|---|
+| `background` | `backgroundColor1` |
+| `callerName` | `textColorPrimary` |
+| `subtitle`, `acceptLabel`, `declineLabel` | `textColorSecondary` |
+| `avatarBackground` | `primaryColor` |
+| `avatarText`, `acceptIcon`, `declineIcon` | `colorWhite` |
+| `acceptButton` | `successColor` |
+| `declineButton` | `errorColor` |
+
+Without this line, the screen still uses the UI Kit's default colours. The UI Kit overrides the `cometchat_pn_call_*` colour resources with its light and dark palette at build time. Those overrides win over the Push SDK's defaults only when your app lists the UI Kit dependency before the Push SDK artifacts. Your app's own overrides always win.
+
+To change a single colour, override its resource, or chain any `CometChatPNIncomingCallTheme.Colors.Builder` setter after mapping. The full list of colours, strings, drawables and the layout is under [Theming the SDK's incoming-call screen](/calls/android/voip-calling#theming-the-sdks-incoming-call-screen). To draw the screen yourself, register your own activity with `setIncomingCallActivity`, for example one that hosts the deprecated [Incoming Call](/ui-kit/android/incoming-call) component. The UI Kit's `CometChatCallActivity` does not work there: it does not read the call the Push SDK passes in its intent, so it would open empty.
+
+## Upgrading from an app that used `CometChat.addCallListener`
+
+Remove the Application-level `CallListener`, the incoming-call overlay or `launchIncomingCallScreen` call, any ringtone you played, the busy check, and the Push SDK 1.0 `setVoIPEnabled` / `setOnCallAnsweredHandler` wiring, and follow the steps above. The method-by-method mapping is on [Migrating from Legacy Ringing](/calls/android/migrating-from-legacy-ringing).
diff --git a/ui-kit/android/color-resources.mdx b/ui-kit/android/color-resources.mdx
index a3a1f2c70..dcd499085 100644
--- a/ui-kit/android/color-resources.mdx
+++ b/ui-kit/android/color-resources.mdx
@@ -217,3 +217,28 @@ Background, stroke, text, and icon colors are derived from the neutral scale by
| `iconTintHighlight` | `primary` (#6852D6) | `primary` (#6852D6) |
This means overriding a neutral color automatically updates all tokens that reference it.
+
+---
+
+## Incoming Call Screen Colors
+
+The incoming-call screen is drawn by the Push Notifications SDK, which cannot read the UI Kit theme when a call arrives with the app killed. It takes its colours from two places:
+
+- **A theme passed in code.** `CometChatTheme.getIncomingCallTheme(context)` (XML Views) or `incomingCallTheme(light, dark)` (Compose) reads your UI Kit theme, light and dark, and your app passes it to the Push SDK with `setIncomingCallTheme`. A palette changed in code, including at runtime, reaches the screen this way. See [Styling the incoming-call screen](/ui-kit/android/calling-integration#styling-the-incoming-call-screen).
+- **Colour resources.** These apply to any colour the theme doesn't set. The UI Kit overrides six of them with its palette (background, text, secondary text, avatar, accept and decline); the other five keep the Push SDK's defaults, which follow those six, so the values below hold for all of them. The UI Kit's overrides take effect only when your app lists the UI Kit dependency before the Push SDK artifacts. Your app's own `res/values/` and `res/values-night/` overrides always win.
+
+| Resource | UI Kit colour | UI Kit light | UI Kit dark | Used for |
+| --- | --- | --- | --- | --- |
+| `cometchat_pn_call_background` | `backgroundColor1` | #FFFFFF | #141414 | Screen background |
+| `cometchat_pn_call_text` | `textColorPrimary` | #141414 | #FFFFFF | Caller name |
+| `cometchat_pn_call_text_secondary` | `textColorSecondary` | #727272 | #989898 | Call type line |
+| `cometchat_pn_call_avatar` | `primaryColor` | #6852D6 | #6852D6 | Initials circle |
+| `cometchat_pn_call_avatar_text` | `colorWhite` | #FFFFFF | #FFFFFF | Initials |
+| `cometchat_pn_call_accept` | `successColor` | #09C26F | #0B9F5D | Answer button and notification accent |
+| `cometchat_pn_call_accept_icon` | `colorWhite` | #FFFFFF | #FFFFFF | Icon on the Answer button |
+| `cometchat_pn_call_accept_label` | `textColorSecondary` | #727272 | #989898 | "Answer" label |
+| `cometchat_pn_call_decline` | `errorColor` | #F44649 | #C73C3E | Decline button and missed-call accent |
+| `cometchat_pn_call_decline_icon` | `colorWhite` | #FFFFFF | #FFFFFF | Icon on the Decline button |
+| `cometchat_pn_call_decline_label` | `textColorSecondary` | #727272 | #989898 | "Decline" label |
+
+Strings, drawables and the layout can be overridden the same way; see [Theming the SDK's incoming-call screen](/calls/android/voip-calling#theming-the-sdks-incoming-call-screen).
diff --git a/ui-kit/android/getting-started-jetpack.mdx b/ui-kit/android/getting-started-jetpack.mdx
index dd7f027e8..d844a9aa7 100644
--- a/ui-kit/android/getting-started-jetpack.mdx
+++ b/ui-kit/android/getting-started-jetpack.mdx
@@ -14,7 +14,7 @@ description: "Step-by-step guide to integrate the CometChat Jetpack Compose UI K
| Login | `CometChatUIKit.login("UID", callback)` — must resolve before rendering components |
| Order | `init()` → `login()` → render. Breaking this order = blank screen |
| Auth Key | Dev/testing only. Use Auth Token in production |
-| Calling | Optional. Add `com.cometchat:calls-sdk-android` to enable voice/video |
+| Calling | Optional. `setEnableCalling(true)` in `UIKitSettings` + push ringing setup. Your app adds `com.cometchat:calls-sdk-android` (the UI Kit does not bundle it); push ringing adds the Push Notifications SDK and Firebase Messaging |
| Min SDK | Android 9.0 (API 28) |
@@ -38,7 +38,7 @@ You also need:
- An Android emulator or physical device running Android 9.0 (API 28) or higher
- `compileSdk = 36` (or newer) in your app module — the UI Kit's transitive `androidx.core:core-ktx:1.18.0` requires it; a `compileSdk`-35 app fails `checkDebugAarMetadata` with an AAR-metadata error
- Kotlin configured with Compose compiler plugin
-- Gradle plugin 8.0+ with Kotlin DSL
+- Android Gradle Plugin 8.9.1 or later with Kotlin DSL
Auth Key is for development only. In production, generate Auth Tokens server-side via the [REST API](https://api-explorer.cometchat.com/) and use `loginWithAuthToken()`. Never ship Auth Keys in client code.
@@ -47,21 +47,9 @@ Auth Key is for development only. In production, generate Auth Tokens server-sid
---
-
-**`enableCalling: true` makes the Calls SDK dependency mandatory — otherwise the app crashes at
-launch.** With the flag set, `CometChatUIKit.initFromSettings()` calls `initCometChatCalls()`, which
-throws if `com.cometchat:calls-sdk-android` is absent:
-
-```
-java.lang.NoClassDefFoundError: Failed resolution of:
- Lcom/cometchat/calls/core/CometChatCalls$SessionSettingsBuilder;
- at com.cometchat.uikit.core.CometChatUIKit.initCometChatCalls(CometChatUIKit.kt:229)
-```
-
-The crash happens in `onCreate`, **before any UI renders** — the flag name gives no hint that a
-dependency is implied. Change the flag and the dependency together, or leave `enableCalling` as
-`false` in an app that does not use calling.
-
+
+**Calling.** Set `enableCalling` to `true` to turn on voice and video calling; the UI Kit then initialises the Calls SDK and logs the user into it. The UI Kit does not bundle the Calls SDK: add `com.cometchat:calls-sdk-android:5.1.0` to your app. With the flag on and the artifact missing, UI Kit 6.2.0 logs a warning at `init`, skips the Calls SDK, and the calling components fail quietly (empty call logs, call buttons failing with `CALLS_SDK_MISSING`, no call screen); UI Kits before 6.2.0 crashed at launch. Incoming calls ring through the Push Notifications SDK, which your app adds along with Firebase Messaging when it sets up push ringing. See [Calling Integration](/ui-kit/android/calling-integration). Leave the flag `false` in an app that does not use calling.
+
## Step 1 — Create an Android Project
@@ -106,7 +94,7 @@ android {
compose = true
}
compileOptions {
- // Java 17 is required by the optional Calls SDK below; the UI Kit alone needs only Java 11
+ // Java 17 is required by the Calls SDK, added below for voice/video calling
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
@@ -117,10 +105,10 @@ android {
dependencies {
// CometChat Jetpack Compose UI Kit
- implementation("com.cometchat:chatuikit-compose-android:6.1.0")
-
- // (Optional) Voice/video calling
- implementation("com.cometchat:calls-sdk-android:5.0.1")
+ implementation("com.cometchat:chatuikit-compose-android:6.2.0")
+ // Voice/video calling: the UI Kit does not bundle the Calls SDK, so add it here. Incoming
+ // calls ring through the Push Notifications SDK and Firebase Messaging, added in Calling Integration.
+ implementation("com.cometchat:calls-sdk-android:5.1.0")
}
```
diff --git a/ui-kit/android/getting-started-kotlin.mdx b/ui-kit/android/getting-started-kotlin.mdx
index 5020425b5..71cd44bfe 100644
--- a/ui-kit/android/getting-started-kotlin.mdx
+++ b/ui-kit/android/getting-started-kotlin.mdx
@@ -14,7 +14,7 @@ description: "Step-by-step guide to integrate the CometChat Kotlin UI Kit with X
| Login | `CometChatUIKit.login("UID", callback)` — must resolve before rendering components |
| Order | `init()` → `login()` → render. Breaking this order = blank screen |
| Auth Key | Dev/testing only. Use Auth Token in production |
-| Calling | Optional. Add `com.cometchat:calls-sdk-android` to enable voice/video |
+| Calling | Optional. `setEnableCalling(true)` in `UIKitSettings` + push ringing setup. Your app adds `com.cometchat:calls-sdk-android` (the UI Kit does not bundle it); push ringing adds the Push Notifications SDK and Firebase Messaging |
| Min SDK | Android 9.0 (API 28) |
@@ -38,7 +38,7 @@ You also need:
- An Android emulator or physical device running Android 9.0 (API 28) or higher
- `compileSdk = 36` (or newer) in your app module — the UI Kit's transitive `androidx.core:core-ktx:1.18.0` requires it; a `compileSdk`-35 app fails `checkDebugAarMetadata` with an AAR-metadata error
- Kotlin configured in your project
-- Gradle plugin 8.0+ with Kotlin DSL
+- Android Gradle Plugin 8.9.1 or later with Kotlin DSL
Auth Key is for development only. In production, generate Auth Tokens server-side via the [REST API](https://api-explorer.cometchat.com/) and use `loginWithAuthToken()`. Never ship Auth Keys in client code.
@@ -47,21 +47,9 @@ Auth Key is for development only. In production, generate Auth Tokens server-sid
---
-
-**`enableCalling: true` makes the Calls SDK dependency mandatory — otherwise the app crashes at
-launch.** With the flag set, `CometChatUIKit.initFromSettings()` calls `initCometChatCalls()`, which
-throws if `com.cometchat:calls-sdk-android` is absent:
-
-```
-java.lang.NoClassDefFoundError: Failed resolution of:
- Lcom/cometchat/calls/core/CometChatCalls$SessionSettingsBuilder;
- at com.cometchat.uikit.core.CometChatUIKit.initCometChatCalls(CometChatUIKit.kt:229)
-```
-
-The crash happens in `onCreate`, **before any UI renders** — the flag name gives no hint that a
-dependency is implied. Change the flag and the dependency together, or leave `enableCalling` as
-`false` in an app that does not use calling.
-
+
+**Calling.** Set `enableCalling` to `true` to turn on voice and video calling; the UI Kit then initialises the Calls SDK and logs the user into it. The UI Kit does not bundle the Calls SDK: add `com.cometchat:calls-sdk-android:5.1.0` to your app. With the flag on and the artifact missing, UI Kit 6.2.0 logs a warning at `init`, skips the Calls SDK, and the calling components fail quietly (empty call logs, call buttons failing with `CALLS_SDK_MISSING`, no call screen); UI Kits before 6.2.0 crashed at launch. Incoming calls ring through the Push Notifications SDK, which your app adds along with Firebase Messaging when it sets up push ringing. See [Calling Integration](/ui-kit/android/calling-integration). Leave the flag `false` in an app that does not use calling.
+
## Step 1 — Create an Android Project
@@ -101,7 +89,7 @@ android {
viewBinding = true
}
compileOptions {
- // Java 17 is required by the optional Calls SDK below; the UI Kit alone needs only Java 11
+ // Java 17 is required by the Calls SDK, added below for voice/video calling
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
@@ -112,10 +100,10 @@ android {
dependencies {
// CometChat Kotlin UI Kit
- implementation("com.cometchat:chatuikit-kotlin-android:6.1.0")
-
- // (Optional) Voice/video calling
- implementation("com.cometchat:calls-sdk-android:5.0.1")
+ implementation("com.cometchat:chatuikit-kotlin-android:6.2.0")
+ // Voice/video calling: the UI Kit does not bundle the Calls SDK, so add it here. Incoming
+ // calls ring through the Push Notifications SDK and Firebase Messaging, added in Calling Integration.
+ implementation("com.cometchat:calls-sdk-android:5.1.0")
}
```
diff --git a/ui-kit/android/getting-started.mdx b/ui-kit/android/getting-started.mdx
index cab4196da..1b6556a2d 100644
--- a/ui-kit/android/getting-started.mdx
+++ b/ui-kit/android/getting-started.mdx
@@ -15,7 +15,7 @@ description: "Install, configure, and launch the CometChat Android UI Kit in you
| Order | `init()` → `login()` → render. Breaking this order = blank screen |
| Auth Key | Dev/testing only. Use Auth Token in production |
| Theme | Set `CometChatTheme.DayNight` as parent theme in `themes.xml` |
-| Calling | Optional. Add `com.cometchat:calls-sdk-android` to enable voice/video |
+| Calling | Optional. `setEnableCalling(true)` in `UIKitSettings` + push ringing setup. Your app adds `com.cometchat:calls-sdk-android` (the UI Kit does not bundle it); push ringing adds the Push Notifications SDK and Firebase Messaging |
| Min SDK | Android 9.0 (API 28) |
@@ -41,8 +41,8 @@ You need three things from the [CometChat Dashboard](https://app.cometchat.com/)
You also need:
- Android Studio installed
- An Android emulator or physical device running Android 9.0 (API 28) or higher
-- Java 8 or higher
-- Gradle plugin 4.0.1 or later
+- Java 17 (required by the Calls SDK, which you add alongside the UI Kit for calling)
+- Android Gradle Plugin 8.9.1 or later and `compileSdk` 36 (required by the UI Kit's `androidx.core` 1.18 and by push ringing)
Auth Key is for development only. In production, generate Auth Tokens server-side via the [REST API](https://api-explorer.cometchat.com/) and use [`loginWithAuthToken()`](/ui-kit/android/methods#login-using-auth-token). Never ship Auth Keys in client code.
@@ -88,8 +88,8 @@ Inside `libs.versions.toml`, add the versions:
```toml libs.versions.toml
[versions]
-cometchat-ui-kit = "6.0.3"
-cometchat-calls-sdk = "5.0.1"
+cometchat-ui-kit = "6.2.0"
+cometchat-calls-sdk = "5.1.0" # for calling; the UI Kit does not bundle it
```
Define the libraries — pick the module for your UI toolkit:
@@ -117,8 +117,8 @@ Now, in your app-level `build.gradle.kts` file:
```kotlin build.gradle.kts
dependencies {
implementation(libs.cometchat.ui.kit)
-
- // (Optional) Include if using voice/video calling features
+ // Voice/video calling: the UI Kit does not bundle the Calls SDK, so add it here. Incoming
+ // calls ring through the Push Notifications SDK and Firebase Messaging, added in Calling Integration.
implementation(libs.cometchat.calls.sdk)
}
```
@@ -132,10 +132,9 @@ Open the app-level `build.gradle.kts` file and add the dependency for your chose
```kotlin build.gradle.kts
dependencies {
- implementation("com.cometchat:chatuikit-kotlin-android:6.1.0")
-
- // (Optional) Include if using voice/video calling features
- implementation("com.cometchat:calls-sdk-android:5.0.1")
+ implementation("com.cometchat:chatuikit-kotlin-android:6.2.0")
+ // Calling: the UI Kit does not bundle the Calls SDK; push ringing is added in Calling Integration
+ implementation("com.cometchat:calls-sdk-android:5.1.0")
}
```
@@ -143,10 +142,9 @@ dependencies {
```kotlin build.gradle.kts
dependencies {
- implementation("com.cometchat:chatuikit-compose-android:6.1.0")
-
- // (Optional) Include if using voice/video calling features
- implementation("com.cometchat:calls-sdk-android:5.0.1")
+ implementation("com.cometchat:chatuikit-compose-android:6.2.0")
+ // Calling: the UI Kit does not bundle the Calls SDK; push ringing is added in Calling Integration
+ implementation("com.cometchat:calls-sdk-android:5.1.0")
}
```
diff --git a/ui-kit/android/incoming-call.mdx b/ui-kit/android/incoming-call.mdx
index ac6ddb5bf..0821e08c2 100644
--- a/ui-kit/android/incoming-call.mdx
+++ b/ui-kit/android/incoming-call.mdx
@@ -1,9 +1,13 @@
---
title: "Incoming Call"
-description: "Full-screen incoming call banner with caller avatar, name, call type indicator, and accept/reject buttons."
+description: "Deprecated incoming call screen, kept for apps that draw the ring screen themselves; incoming calls now ring through the Push Notifications SDK."
---
-`CometChatIncomingCall` renders when the logged-in user receives an incoming voice or video call, displaying the caller's information and providing accept/reject controls.
+
+**Deprecated.** Incoming calls now ring through the CometChat Push Notifications SDK, which draws its own incoming-call screen in every app state, including when the app is not running. `CometChatIncomingCall` is kept only for apps that opt into drawing the ring screen themselves with `PNConfiguration.setIncomingCallActivity`. New integrations need no incoming-call component: see [Calling Integration](/ui-kit/android/calling-integration).
+
+
+`CometChatIncomingCall` shows the caller's avatar, name and call type with accept and decline controls. It is a view only: it does not ring, and the UI Kit has no dependency on the Push SDK. If you use it as your ring screen, your activity connects its Accept and Decline to the Push SDK.
@@ -11,81 +15,89 @@ description: "Full-screen incoming call banner with caller avatar, name, call ty
---
-## Where It Fits
-
-`CometChatIncomingCall` is a call-handling component. It renders the incoming call screen and transitions to the ongoing call screen when the user accepts. Wire it to `CometChatOngoingCallActivity` after the user accepts the call.
-
-
-
-
-```xml activity_call.xml lines
-
-```
-
-```kotlin lines
-val incomingCall = findViewById(R.id.incoming_call)
-incomingCall.setCall(call)
-```
-
-
-
+## When to use it
-```kotlin lines
-CometChatIncomingCall(
- call = call
-)
-```
-
-
-
+| You want | Do this |
+| --- | --- |
+| The default incoming-call screen | Nothing. The Push SDK draws it, in the UI Kit's default colours. To follow your own UI Kit theme, light and dark, pass it with `setIncomingCallTheme`; see [Styling the incoming-call screen](/ui-kit/android/calling-integration#styling-the-incoming-call-screen) |
+| This component as the ring screen | Host it in your own activity registered with `PNConfiguration.setIncomingCallActivity` (below) |
+| A fully custom ring screen | Your own activity with the Push SDK's `CometChatCallNotifications` API; see [Your own incoming-call screen](/calls/android/voip-calling#your-own-incoming-call-screen) |
---
-## Quick Start
-
-
-
+## Quick Start: the component as the ring screen
-Add to your layout XML:
+Register your ring activity alongside your [answer activity](/ui-kit/android/calling-integration#step-3-configure-push-ringing):
-```xml lines
-
+```kotlin lines
+PNConfiguration.Builder(APP_ID, REGION)
+ .setProviderId(PROVIDER_ID)
+ .setAnswerActivity(CallAnswerActivity::class.java)
+ .setIncomingCallActivity(RingActivity::class.java)
+ .build()
```
-Set the `Call` object — this is required:
+The activity reads the ringing call from its intent, tells the Push SDK when it opens and closes (so the SDK can close it when the caller cancels or the ring times out), and sends Accept and Decline to the Push SDK. On Accept the Push SDK opens your answer activity.
```kotlin lines
-override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- setContentView(R.layout.layout_activity)
+class RingActivity : AppCompatActivity() {
+ private var sessionId: String? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setShowWhenLocked(true); setTurnScreenOn(true)
+ setContentView(R.layout.activity_ring)
+
+ val incoming = CallBundles.getCall(intent) ?: return finish()
+ sessionId = incoming.sessionId
+ CometChatCallNotifications.onIncomingCallScreenOpened(incoming.sessionId) { finish() }
+
+ val call = CallRingingUtils.buildIncomingCall(
+ sessionId = incoming.sessionId,
+ callType = if (incoming.isVideo) CometChatConstants.CALL_TYPE_VIDEO else CometChatConstants.CALL_TYPE_AUDIO,
+ receiverType = incoming.receiverType,
+ receiverUid = incoming.receiverUid,
+ callerUid = incoming.callerUid,
+ callerName = incoming.callerName,
+ callerAvatarUrl = incoming.callerAvatarUrl,
+ initiatedAtMs = incoming.initiatedAtMs,
+ conversationId = incoming.conversationId
+ )
+ findViewById(R.id.incoming_call).apply {
+ setCall(call)
+ setOnAcceptClickListener {
+ // Ask for RECORD_AUDIO (+ CAMERA for video) first: answer() skips the SDK prompt.
+ CometChatCallNotifications.answer(this@RingActivity, incoming.sessionId)
+ }
+ setOnRejectClickListener {
+ CometChatCallNotifications.decline(this@RingActivity, incoming.sessionId)
+ finish()
+ }
+ }
+ }
- val incomingCall = findViewById(R.id.incoming_call)
- incomingCall.setCall(call)
+ override fun onDestroy() {
+ sessionId?.let { CometChatCallNotifications.onIncomingCallScreenClosed(it) }
+ super.onDestroy()
+ }
}
```
-
-
+In Compose, pass `CometChatCallNotifications.answer` through `onAcceptRequested` and `decline` through `onRejectClick`, inside `setContent`. Do not use `onAcceptClick` for this: in Compose it runs after the component has already accepted the call and opened `CometChatOngoingCallActivity`.
-```kotlin lines
-@Composable
-fun IncomingCallScreen() {
- CometChatIncomingCall(
- call = call
- )
-}
+```kotlin
+CometChatIncomingCall(
+ call = call,
+ // Replaces the default accept: proceed() is never called, so the component does not join itself.
+ onAcceptRequested = { _ -> CometChatCallNotifications.answer(this@RingActivity, incoming.sessionId) },
+ onRejectClick = {
+ CometChatCallNotifications.decline(this@RingActivity, incoming.sessionId)
+ finish()
+ }
+)
```
-
-
-
-Prerequisites: CometChat SDK initialized with `CometChatUIKit.init()`, a user logged in, and the UI Kit dependency added.
+Prerequisites: `CometChatUIKit.init()` with `setEnableCalling(true)`, the Push SDK initialised with an answer activity, and a user logged in.
---
@@ -95,7 +107,7 @@ Prerequisites: CometChat SDK initialized with `CometChatUIKit.init()`, a user lo
#### `onAcceptClick`
-Fires when the user taps the accept button.
+Fires when the user taps Accept. By default the component opens `CometChatOngoingCallActivity` itself. In XML Views, `setOnAcceptClickListener` replaces that default: on a Push SDK ring screen, set it to call `CometChatCallNotifications.answer(context, sessionId)`, so the Push SDK stops the ring and opens your answer activity. In Compose, `onAcceptClick` does not replace the default; it runs after the component has accepted and opened the call screen. Use `onAcceptRequested` instead, and don't call the `proceed` it receives. Ask for the microphone (and camera) before answering: the Push SDK only prompts for its notification's Answer action.
@@ -112,7 +124,9 @@ incomingCall.setOnAcceptClickListener {
```kotlin lines
CometChatIncomingCall(
call = call,
- onAcceptClick = { /* custom accept logic */ }
+ onAcceptClick = { /* custom accept logic */ },
+ // Runs before the default answer; call proceed() once permissions are granted
+ onAcceptRequested = { proceed -> requestMicAndCamera { proceed() } }
)
```
@@ -121,7 +135,7 @@ CometChatIncomingCall(
#### `onRejectClick`
-Fires when the user taps the reject button.
+Fires when the user taps Decline. By default the component declines through the Calls SDK as `rejected`. On a Push SDK ring screen, set it to call `CometChatCallNotifications.decline(context, sessionId)` instead, so the Push SDK stops the ring and sends `rejected` or `busy`.
@@ -174,19 +188,20 @@ CometChatIncomingCall(
-### Global UI Events (CometChatCallEvents)
+### Global UI Events (`CometChatEvents.callEvents`)
| Event | Fires when | Payload |
| --- | --- | --- |
-| `ccCallAccepted` | A call is accepted | `Call` |
-| `ccCallRejected` | A call is rejected | `Call` |
+| `CometChatCallEvent.CallAccepted` | Accept tapped on this screen, with the default accept (not emitted when you replace it) | `Call` |
+| `CometChatCallEvent.CallRejected` | Decline tapped on this screen, with the default decline (not emitted when you replace it) | `Call` |
### SDK Events (Real-Time, Automatic)
-| SDK Listener | Internal behavior |
+| Calls SDK listener | Internal behavior |
| --- | --- |
-| `onIncomingCallReceived` | Triggers the incoming call screen display |
-| `onIncomingCallCancelled` | Dismisses the incoming call screen |
+| `IncomingCallListener.onIncomingCallEnded` for this session | The ring ended elsewhere (caller cancelled, answered on another device, timed out): `rejectedCall` is emitted so the screen closes |
+
+The component does not open itself on an incoming call. The Push SDK launches the activity registered with `setIncomingCallActivity`; the Chat SDK's `CallListener` is superseded and not used.
---
@@ -198,8 +213,9 @@ CometChatIncomingCall(
| `setOnAcceptClickListener { }` | `onAcceptClick = { }` | Override accept button behavior |
| `setOnRejectClickListener { }` | `onRejectClick = { }` | Override reject button behavior |
| `setOnError { }` | `onError = { }` | Error callback |
-| `setDisableSoundForCalls(true)` | `disableSoundForCalls = true` | Disable incoming call ringtone |
-| `setCustomSoundForCalls(R.raw.tone)` | `customSoundForCalls = R.raw.tone` | Custom ringtone |
+| — | `onAcceptRequested = { proceed -> }` | Compose only: gate the answer (e.g. a permission prompt), then call `proceed()` |
+| `setDisableSoundForCalls(true)` | `disableSoundForCalls = true` | No effect when the Push SDK is present: the Push SDK plays the ringtone and the component stays silent to avoid two ringtones |
+| `setCustomSoundForCalls(R.raw.tone)` | `customSoundForCalls = R.raw.tone` | Only used without the Push SDK calls module |
---
@@ -400,6 +416,20 @@ CometChatIncomingCall(
+`CometChatIncomingCallStyle` is deprecated with the component; it only styles this screen. The Push SDK's own screen is themed with `CometChatPNIncomingCallTheme` (see [Styling the incoming-call screen](/ui-kit/android/calling-integration#styling-the-incoming-call-screen)). Its colours map like this:
+
+| `CometChatIncomingCallStyle` | `CometChatPNIncomingCallTheme.Colors` |
+| --- | --- |
+| `backgroundColor` | `background` |
+| `titleTextColor` | `callerName` |
+| `subtitleTextColor` | `subtitle` |
+| `avatarStyle` background | `avatarBackground` (and `avatarText` for the initials) |
+| `acceptButtonBackgroundColor` / `rejectButtonBackgroundColor` | `acceptButton` / `declineButton` |
+| `acceptButtonTextColor` / `rejectButtonTextColor` | `acceptLabel` / `declineLabel` (the labels sit under the buttons) |
+| `iconTint`, `voiceCallIcon`, `videoCallIcon` | none: the screen shows "Incoming audio call" text, not a call-type icon. The button icons are `acceptIcon` / `declineIcon` |
+| `cornerRadius`, `strokeWidth`, `strokeColor` | none: the screen is full page, not a card |
+| text appearances (font, size) | none yet: colours only |
+
See [Component Styling](/ui-kit/android/component-styling) for the full reference.
---
diff --git a/ui-kit/android/llms-android-v6.mdx b/ui-kit/android/llms-android-v6.mdx
index b7b1e4190..040c53529 100644
--- a/ui-kit/android/llms-android-v6.mdx
+++ b/ui-kit/android/llms-android-v6.mdx
@@ -99,9 +99,10 @@ attributes, long-tail components, theming tokens, or feature enablement.
## Components — calling
- [Call Buttons](/ui-kit/android/call-buttons)
-- [Incoming Call](/ui-kit/android/incoming-call)
- [Outgoing Call](/ui-kit/android/outgoing-call)
+- [Ongoing Call](/ui-kit/android/ongoing-call) — the in-call screen; your answer activity opens it for an answered incoming call
- [Call Logs](/ui-kit/android/call-logs)
+- [Incoming Call](/ui-kit/android/incoming-call) — deprecated; incoming calls ring through the Push Notifications SDK, no component needed
## Components — search, AI & notifications
- [Search](/ui-kit/android/search)
@@ -134,7 +135,8 @@ the symptom, not the component.
| App crashes on the chat screen: `The style on this component requires your app theme to be Theme.MaterialComponents` | [Getting started (Kotlin)](/ui-kit/android/getting-started-kotlin) — inherit `CometChatTheme.DayNight` |
| Toolbar renders **under the status bar** / title collides with the clock | [Conversation + message view](/ui-kit/android/conversation-message-view) · [One-to-one chat](/ui-kit/android/one-to-one-chat) — `enableEdgeToEdge()` needs insets CONSUMED |
| Composer crashes / `ActivityResultLauncher not initialized` | [Troubleshooting](/ui-kit/android/troubleshooting) — build the composer in `onCreate` |
-| Calling crashes: `NoClassDefFoundError: CometChatCalls…` or `Please call the CometChatCalls.init()` | [Calling integration](/ui-kit/android/calling-integration) — the artifact AND `enableCalling` are a pair |
+| Calling does nothing: empty call logs, `CALLS_SDK_MISSING`, logcat `Calls SDK is not on the classpath` (a `NoClassDefFoundError` crash before 6.2.0); or `Please call the CometChatCalls.init()` | [Calling integration](/ui-kit/android/calling-integration) — the `calls-sdk-android` artifact AND `setEnableCalling(true)` are a pair; the UI Kit does not bundle the Calls SDK |
+| Incoming calls never ring | [Calling integration](/ui-kit/android/calling-integration) — `CometChatPushNotifications.init` with `setAnswerActivity(CallAnswerActivity::class.java)` (your activity that opens `CometChatOngoingCallActivity`) + a `FirebaseMessagingService` that calls `handlePushNotification`; the Chat SDK `CallListener` is superseded and fires nothing |
| Components render but stay empty | init/login did not resolve — [Getting started (Kotlin)](/ui-kit/android/getting-started-kotlin) / [(Compose)](/ui-kit/android/getting-started-jetpack) |
| Keyboard covers the composer | `windowSoftInputMode="adjustResize"` — [One-to-one chat](/ui-kit/android/one-to-one-chat) |
| Colours ignored / theme not applied | [Theme introduction](/ui-kit/android/theme-introduction) |
diff --git a/ui-kit/android/methods.mdx b/ui-kit/android/methods.mdx
index e4e53101e..67a6bc879 100644
--- a/ui-kit/android/methods.mdx
+++ b/ui-kit/android/methods.mdx
@@ -74,6 +74,8 @@ The `UIKitSettings` is an important parameter of the `init()` function. It serve
| **setAIFeatures** | `List` | Sets the AI Features that need to be added in UI Kit |
| **setExtensions** | `List` | Sets the list of extension that need to be added in UI Kit |
| **dateTimeFormatterCallback** | `DateTimeFormatterCallback` | Interface containing callback methods to format different types of timestamps. |
+| **setEnableCalling** | `Boolean` | Turns on calling: the UI Kit initialises the Calls SDK during `init()` and logs the user into it during `login()`. Default `false`. See [Calling Integration](/ui-kit/android/calling-integration) |
+| **setCallSettingsBuilder** | `Any` (a `CometChatCalls.SessionSettingsBuilder`) | Default session settings for the call screens. Typed `Any` so the UI Kit compiles without the Calls SDK, which your app adds |
| **setEnableThreadSubscription** | `Boolean` | *Deprecated in v6.1.0.* Global switch for the thread subscription feature. Default `true` from v6.1.0 (`false` on v6.0.x, where it is the opt-in). Kept as the only app-wide opt-out — pass `false` to turn the feature off everywhere. To hide a single surface, prefer the per-screen visibility methods. See [Thread Subscription](/ui-kit/android/guide-threaded-messages#thread-subscription) |
**Usage:**
@@ -192,7 +194,11 @@ CometChatUIKit.logout(object : CometChat.CallbackListener() {
})
```
-> **What this does:** Logs out the current user, clears the session data, and tears down the internal event system. Call this before logging in a different user to avoid conflicts.
+> **What this does:** Ends a call in progress and cancels an outgoing call still ringing, logs out of the Chat SDK and then the Calls SDK, clears the session data and tears down the internal event system. On the Calls logout the Push Notifications SDK stops a ring still playing for the old user and unregisters the push token. Call this before logging in a different user to avoid conflicts.
+
+
+Use `CometChatUIKit.logout`, not `CometChat.logout`. The Chat SDK's logout alone leaves the Calls SDK signed in: a ringing call is not stopped and later declines go out with the old user's token.
+
---
diff --git a/ui-kit/android/ongoing-call.mdx b/ui-kit/android/ongoing-call.mdx
new file mode 100644
index 000000000..e91a4963f
--- /dev/null
+++ b/ui-kit/android/ongoing-call.mdx
@@ -0,0 +1,338 @@
+---
+title: "Ongoing Call"
+description: "In-call screen that joins the call session and renders the Calls SDK's call UI, for outgoing, answered incoming and group calls."
+---
+
+`CometChatOngoingCall` renders the active call: the Calls SDK's video tiles and controls (mute, camera, switch camera, end). `CometChatOngoingCallActivity` hosts it full-screen, supports picture-in-picture, and is where an answered incoming call ends up: your app's answer activity opens it with the answered call, and it joins the session.
+
+---
+
+## Where It Fits
+
+Every call ends up here:
+
+| Path | How the screen opens |
+|------|----------------------|
+| Incoming call answered | The Push SDK starts your answer activity (`PNConfiguration.setAnswerActivity`), which builds the call with `CallRingingUtils.buildIncomingCall` and calls `CometChatOngoingCallActivity.launchOngoingCallActivity(context, call)` |
+| Outgoing call answered | The [Outgoing Call](/ui-kit/android/outgoing-call) screen hands over once the callee joins: XML Views embed `CometChatOngoingCall` in the same activity, Compose launches `CometChatOngoingCallActivity` |
+| Group call / meeting | [Call Buttons](/ui-kit/android/call-buttons) send a meeting message; tapping it launches the activity with `CallWorkFlow.MEETING` |
+| Your own flow | `CometChatOngoingCallActivity.launchOngoingCallActivity(context, sessionId, callType)` or the component directly |
+
+
+
+
+```kotlin lines
+import com.cometchat.uikit.kotlin.presentation.ongoingcall.ui.CometChatOngoingCallActivity
+
+CometChatOngoingCallActivity.launchOngoingCallActivity(
+ context = this,
+ sessionId = sessionId,
+ callType = CometChatConstants.CALL_TYPE_VIDEO
+)
+```
+
+
+
+
+```kotlin lines
+import com.cometchat.uikit.compose.presentation.ongoingcall.ui.CometChatOngoingCallActivity
+
+CometChatOngoingCallActivity.launchOngoingCallActivity(
+ context = context,
+ sessionId = sessionId,
+ callType = CometChatConstants.CALL_TYPE_VIDEO
+)
+```
+
+
+
+
+---
+
+## Quick Start
+
+### From your answer activity (incoming calls)
+
+The Push SDK opens your answer activity with the answered call in its intent. Turn it into a UI Kit `Call` and launch this screen; it asks for the microphone (and the camera for a video call) and joins:
+
+```kotlin lines
+val incoming = CallBundles.getCall(intent) ?: return finish()
+val call = CallRingingUtils.buildIncomingCall(
+ sessionId = incoming.sessionId,
+ callType = if (incoming.isVideo) CometChatConstants.CALL_TYPE_VIDEO else CometChatConstants.CALL_TYPE_AUDIO,
+ receiverType = incoming.receiverType,
+ receiverUid = incoming.receiverUid,
+ callerUid = incoming.callerUid,
+ callerName = incoming.callerName,
+ callerAvatarUrl = incoming.callerAvatarUrl,
+ initiatedAtMs = incoming.initiatedAtMs,
+ conversationId = incoming.conversationId
+)
+CometChatOngoingCallActivity.launchOngoingCallActivity(this, call)
+finish()
+```
+
+See [Calling Integration](/ui-kit/android/calling-integration#step-3-configure-push-ringing) for the full answer activity and its manifest entry.
+
+### Launching it yourself
+
+
+
+
+```kotlin lines
+CometChatOngoingCallActivity.launchOngoingCallActivity(
+ context = this,
+ sessionId = sessionId,
+ callType = CometChatConstants.CALL_TYPE_AUDIO,
+ callWorkFlow = CallWorkFlow.DEFAULT, // MEETING for a group call
+ callSettingsBuilder = null, // or your SessionSettingsBuilder
+ style = null
+)
+```
+
+
+
+
+```kotlin lines
+CometChatOngoingCallActivity.launchOngoingCallActivity(
+ context = context,
+ sessionId = sessionId,
+ callType = CometChatConstants.CALL_TYPE_AUDIO,
+ callWorkFlow = CallWorkFlow.DEFAULT, // MEETING for a group call
+ callSettingsBuilder = null, // or your SessionSettingsBuilder
+ style = null
+)
+```
+
+
+
+
+### Embedding the component
+
+Use the component directly when you host the call in your own screen.
+
+
+
+
+```xml activity_call.xml lines
+
+```
+
+```kotlin lines
+val ongoingCall = findViewById(R.id.ongoing_call)
+ongoingCall.setSessionId(sessionId)
+ongoingCall.setCallType(CometChatConstants.CALL_TYPE_VIDEO)
+ongoingCall.setCallWorkFlow(CallWorkFlow.DEFAULT)
+ongoingCall.setSessionSettingsBuilder(CometChatCalls.SessionSettingsBuilder())
+ongoingCall.startCall()
+```
+
+
+
+
+```kotlin lines
+CometChatOngoingCall(
+ sessionId = sessionId,
+ callType = CometChatConstants.CALL_TYPE_VIDEO,
+ callWorkFlow = CallWorkFlow.DEFAULT,
+ onCallEnded = { finish() },
+ onError = { e -> Log.e("Call", e.message ?: "") }
+)
+```
+
+
+
+
+Prerequisites: `CometChatUIKit.init()` with `setEnableCalling(true)`, a user logged in, and `com.cometchat:calls-sdk-android` in your app's dependencies (the UI Kit does not bundle it). There is nothing to declare for permissions: `RECORD_AUDIO` and `CAMERA` merge into your manifest from the SDKs and are requested at runtime.
+
+---
+
+## Actions and Events
+
+### Callback Methods
+
+#### `onCallEnded`
+
+Fires when this device has left the session: the user tapped End, the other side hung up, the session timed out, or the connection closed. Close the screen here.
+
+
+
+
+The hosting `CometChatOngoingCallActivity` finishes itself, and an embedded component finishes its host activity on `CallEnded` and `SessionTimeout` too. Observe the ViewModel's events only for your own clean-up:
+
+```kotlin lines
+lifecycleScope.launch {
+ ongoingCall.getViewModel()?.events?.collect { event ->
+ if (event is OngoingCallEvent.CallEnded) finish()
+ }
+}
+```
+
+
+
+
+```kotlin lines
+CometChatOngoingCall(
+ sessionId = sessionId,
+ callType = callType,
+ onCallEnded = { finish() }
+)
+```
+
+
+
+
+#### `onError`
+
+Fires when the session could not be joined (bad session id, no network, the Calls SDK not logged in). A denied microphone does not fire it: the call joins muted.
+
+
+
+
+```kotlin lines
+ongoingCall.setOnError { exception ->
+ Log.e("OngoingCall", "Error: ${exception.message}")
+}
+```
+
+
+
+
+```kotlin lines
+CometChatOngoingCall(
+ sessionId = sessionId,
+ callType = callType,
+ onError = { exception ->
+ Log.e("OngoingCall", "Error: ${exception.message}")
+ }
+)
+```
+
+
+
+
+### ViewModel Events (`OngoingCallEvent`)
+
+| Event | Fires when |
+| --- | --- |
+| `CallEnded` | This device left the session (End tapped, remote hang-up, connection closed) |
+| `SessionTimeout` | The session timed out |
+| `UserJoined(userId, isCurrentUser)` | A participant joined |
+| `UserLeft(userId)` | A participant left |
+| `Error(exception)` | Joining the session failed |
+
+### Global UI Events (`CometChatEvents.callEvents`)
+
+| Event | Fires when | Payload |
+| --- | --- | --- |
+| `CometChatCallEvent.CallEnded` | This device hangs up a 1:1 call (End, or the call screen's own leave button) that the screen was opened with, as your answer activity does with `launchOngoingCallActivity(context, call)`. Not emitted when the other side hangs up, when the connection closes, or for a screen opened with a session id | `Call` |
+
+### SDK Events (Real-Time, Automatic)
+
+| Calls SDK listener | Internal behavior |
+| --- | --- |
+| `SessionStatusListener.onSessionLeft` | Local clean-up, `CallEnded` |
+| `SessionStatusListener.onSessionTimedOut` | `SessionTimeout`, screen ends |
+| `SessionStatusListener.onConnectionClosed` | Screen ends, `CallEnded` (a join whose connection closed without a leave event) |
+| `ParticipantEventListener.onParticipantJoined / Left` | `UserJoined` / `UserLeft` |
+| `ButtonClickListener.onLeaveSessionButtonClicked` | The Calls SDK has already left; local clean-up only |
+
+---
+
+## Functionality
+
+| Method (Kotlin XML) | Compose Parameter | Description |
+| --- | --- | --- |
+| `setSessionId(id)` | `sessionId` | Session to join (required) |
+| `setCallType(type)` | `callType` | `CometChatConstants.CALL_TYPE_AUDIO` or `CALL_TYPE_VIDEO`; sets the session type |
+| `setCallWorkFlow(flow)` | `callWorkFlow` | `DEFAULT` for 1:1 calls, `MEETING` for group calls |
+| `setSessionSettingsBuilder(builder)` | `callSettingsBuilder` | Calls SDK `SessionSettingsBuilder` for layout, controls, audio mode. The component sets the session type on it |
+| `startCall()` | automatic | Joins the session |
+| `setOnError { }` | `onError` | Error callback |
+| `setStyle(style)` | `style` | Container styling |
+| `getViewModel()` | `viewModel` | Access to state and events |
+
+---
+
+## Hang-up and clean-up
+
+Hanging up leaves the Calls SDK session with `CallSession.leave()`; nothing is sent to the Chat SDK. For a 1:1 call (`DEFAULT`) the UI Kit also clears its active-call state and, when the screen was opened with a `Call`, emits `CometChatCallEvent.CallEnded` on `CometChatEvents.callEvents`. A hang-up by the other side clears the same state but emits no global event; observe the component's `CallEnded` instead. For a `MEETING` it only leaves and clears the meeting flag; the conference continues for the others.
+
+`CometChatOngoingCallActivity` leaves the session when it is destroyed, but only its **own** session: after answering a second call over a live one, the Push SDK opens your answer activity again, which hands the new call to `CometChatOngoingCallActivity`. That intent arrives in `onNewIntent` (`SINGLE_TOP`); the activity starts a fresh instance for the new session and finishes itself, and the old screen's clean-up does not touch the new session.
+
+---
+
+## Picture-in-Picture
+
+`CometChatOngoingCallActivity` declares `supportsPictureInPicture` and enters PiP when the user leaves the screen during a call; the Calls SDK switches to its PiP layout (`enablePictureInPictureLayout` / `disablePictureInPictureLayout`). Dismissing the PiP window ends the call. Nothing to configure.
+
+---
+
+## Second call during a call
+
+Handled by the Push SDK: the new call rings over the live one with "Answering ends your current call". Answer leaves the live session, prepares the new one and opens your answer activity, which opens `CometChatOngoingCallActivity` for it; Decline sends `busy` and keeps the current call.
+
+---
+
+## Style
+
+
+
+
+```kotlin lines
+val style = CometChatOngoingCallStyle.Builder(this)
+ .setBackgroundColor(Color.parseColor("#141414"))
+ .setProgressIndicatorColor(Color.parseColor("#6852D6"))
+ .setCornerRadius(0f)
+ .build()
+
+CometChatOngoingCallActivity.launchOngoingCallActivity(
+ context = this,
+ sessionId = sessionId,
+ callType = callType,
+ style = style
+)
+```
+
+
+
+
+```kotlin lines
+CometChatOngoingCall(
+ sessionId = sessionId,
+ callType = callType,
+ style = CometChatOngoingCallStyle.default().copy(
+ backgroundColor = Color(0xFF141414),
+ progressIndicatorColor = Color(0xFF6852D6)
+ )
+)
+```
+
+
+
+
+The style covers the container around the call surface (background, progress indicator while joining, corner radius, stroke). The call UI itself (tiles, buttons, layouts) is the Calls SDK's and is configured through its `SessionSettingsBuilder`; see [Session Settings](/calls/android/session-settings) and [Call Layouts](/calls/android/call-layouts).
+
+See [Component Styling](/ui-kit/android/component-styling) for the full reference.
+
+---
+
+## Next Steps
+
+
+
+ Enable calling and push ringing
+
+
+ Calling and ringing states before the answer
+
+
+ View call history
+
+
+ How incoming calls ring
+
+
diff --git a/ui-kit/android/outgoing-call.mdx b/ui-kit/android/outgoing-call.mdx
index ece476265..cd9ef3cef 100644
--- a/ui-kit/android/outgoing-call.mdx
+++ b/ui-kit/android/outgoing-call.mdx
@@ -1,9 +1,11 @@
---
title: "Outgoing Call"
-description: "Full-screen outgoing call UI with recipient avatar, name, call status, and end-call button."
+description: "Full-screen outgoing call UI with recipient avatar, name, calling and ringing status, end-call button, and the reason when a call is declined, busy or unanswered."
---
-`CometChatOutgoingCall` renders the outgoing call screen and transitions to the ongoing call screen when the receiver accepts. It displays the recipient's avatar, name, call status, and an end-call button.
+`CometChatOutgoingCall` renders the outgoing call screen from the moment a call is placed until the callee joins, declines, is busy or does not answer. It displays the recipient's avatar, name, the call status ("calling…", then "ringing…" once the callee's device is reached) and an end-call button, and transitions to the ongoing call screen when the callee joins.
+
+The screen listens on the Calls SDK session that `initiateCall` prepared, without joining it. The caller joins only when the callee has joined, so no media runs until there is someone to talk to. The Chat SDK is not involved.
@@ -13,7 +15,7 @@ description: "Full-screen outgoing call UI with recipient avatar, name, call sta
## Where It Fits
-`CometChatOutgoingCall` is a call-initiation component. Wire it to `CometChatCallEvents` to handle call-accepted and call-rejected transitions.
+`CometChatOutgoingCall` is a call-initiation component. [Call Buttons](/ui-kit/android/call-buttons), [Call Logs](/ui-kit/android/call-logs) and `CometChatCallActivity.launchOutgoingCallScreen` open it for you with the `Call` returned by `CallInitiationUtils.initiateUserCall`; you only place it yourself when you host the outgoing screen in your own activity.
@@ -85,7 +87,22 @@ fun OutgoingCallScreen() {
-Prerequisites: CometChat SDK initialized with `CometChatUIKit.init()`, a user logged in, and the UI Kit dependency added.
+Prerequisites: `CometChatUIKit.init()` with `setEnableCalling(true)`, a user logged in, and a `Call` obtained from `CallInitiationUtils.initiateUserCall` (the session must already be prepared when the screen opens).
+
+When you host the component yourself, give it a lifecycle owner so it can attach to the prepared session; the XML view uses its hosting activity, and the composable uses `LocalLifecycleOwner`.
+
+---
+
+## Call states
+
+| State | Subtitle | What happened |
+| --- | --- | --- |
+| Calling | "calling …" | `initiateCall` succeeded; the backend is pushing the callee |
+| Ringing | "ringing …" | The callee's device received the push (`onParticipantConnected`) |
+| Accepted | — | The callee joined (`onParticipantJoined`). The End button is disabled and the ongoing call screen opens |
+| Ended | — | The call ended without an answer. `endReason` on the ViewModel says why: `rejected`, `busy`, `unanswered`, `cancelled` or `ended` |
+
+A call nobody answers ends after 45 seconds on both sides: the callee's Push SDK sends `unanswered`, and this screen's own timer ends the call with the same status in case that push is late. Tapping End sends `cancelled` to the callee, whose ring stops.
---
@@ -95,7 +112,7 @@ Prerequisites: CometChat SDK initialized with `CometChatUIKit.init()`, a user lo
#### `onEndCallClick`
-Fires when the end-call button is tapped. Default: cancels the outgoing call via the SDK.
+Fires when the end-call button is tapped. Default: cancels the call with `CometChatCalls.endCall(sessionId, "cancelled")`, closes the screen and emits `CometChatCallEvent.CallRejected`. The button is disabled after the first tap, and a callee answer that arrives after End is not joined.
@@ -127,7 +144,7 @@ Fires on internal errors (network failure, auth issue, SDK exception).
```kotlin lines
-outgoingCall.setOnError { context, exception ->
+outgoingCall.setOnError { exception ->
Log.e("OutgoingCall", "Error: ${exception.message}")
}
```
@@ -138,7 +155,7 @@ outgoingCall.setOnError { context, exception ->
```kotlin lines
CometChatOutgoingCall(
call = call,
- onError = { context, exception ->
+ onError = { exception ->
Log.e("OutgoingCall", "Error: ${exception.message}")
}
)
@@ -147,19 +164,33 @@ CometChatOutgoingCall(
-### Global UI Events (CometChatCallEvents)
+#### `onCallAccepted` (Compose)
+
+Fires when the callee joins. Default: launches `CometChatOngoingCallActivity` and finishes the hosting activity. Provide the callback to host the call screen yourself.
+
+```kotlin lines
+CometChatOutgoingCall(
+ call = call,
+ onCallAccepted = { accepted -> /* open your own call screen for accepted.sessionId */ }
+)
+```
+
+### Global UI Events (`CometChatEvents.callEvents`)
| Event | Fires when | Payload |
| --- | --- | --- |
-| `ccCallAccepted` | Outgoing call accepted by receiver | `Call` |
-| `ccCallRejected` | Outgoing call rejected by receiver | `Call` |
+| `CometChatCallEvent.CallRejected` | End tapped, or this screen's ring timer (45 s) expired. A decline, busy, unanswered or a close from the other side emits no global event; observe the ViewModel's `rejectedCall` / `endReason` for those | `Call` |
### SDK Events (Real-Time, Automatic)
-| SDK Listener | Internal behavior |
+| Calls SDK listener | Internal behavior |
| --- | --- |
-| `onOutgoingCallAccepted` | Transitions to the ongoing call screen |
-| `onOutgoingCallRejected` | Finishes the activity or invokes back-press handler |
+| `ParticipantEventListener.onParticipantConnected` (prepared session) | Subtitle changes to "ringing …" |
+| `ParticipantEventListener.onParticipantJoined` (prepared session) | Accepted: End disabled, ongoing call screen opens and joins |
+| `SessionStatusListener.onConnectionClosed` / `onSessionTimedOut` (prepared session) | Waits 1.5 s for the reason from the terminal push, then closes as `ended` |
+| `IncomingCallListener.onIncomingCallEnded(call, reason)` | Closes with `rejected`, `busy`, `unanswered` or `cancelled` |
+
+The Chat SDK's `CallListener` is superseded and not used.
---
@@ -169,9 +200,37 @@ CometChatOutgoingCall(
| --- | --- | --- |
| `setCall(call)` | `call = call` | Set the Call object (required) |
| `setOnEndCallClickListener { }` | `onEndCallClick = { }` | Override end-call button behavior |
+| — | `onCallAccepted = { }` | Override what happens when the callee joins |
| `setOnError { }` | `onError = { }` | Error callback |
-| `setDisableSoundForCalls(true)` | `disableSoundForCalls = true` | Disable outgoing call ringtone |
-| `setCustomSoundForCalls(R.raw.tone)` | `customSoundForCalls = R.raw.tone` | Custom ringtone |
+| `setOnBackPressListener { }` | `onBackPress = { }` | Back press handling |
+| `setCallSettingsBuilder(builder)` | `callSettingsBuilder` | `SessionSettingsBuilder` handed to the ongoing call screen on accept |
+| `setDisableSoundForCalls(true)` | `disableSoundForCalls = true` | Disable the outgoing ringback tone |
+| `setCustomSoundForCalls(R.raw.tone)` | `customSoundForCalls = R.raw.tone` | Custom ringback tone |
+| `getViewModel()` | `viewModel` | Access to the state flows below |
+
+### ViewModel (`CometChatOutgoingCallViewModel`)
+
+| Member | Description |
+| --- | --- |
+| `call`, `acceptedCall`, `rejectedCall` | `StateFlow`: the current call, the call once the callee joined, the call once it ended without an answer |
+| `endReason` | `StateFlow`: why `rejectedCall` was emitted (`UIKitConstants.CallStatusConstants.REJECTED`, `BUSY`, `UNANSWERED`, `CANCELLED`, `ENDED`); null while ringing or accepted |
+| `calleeRinging` | `StateFlow`: true once the callee's device is reached |
+| `endCallButtonEnabled` | `StateFlow`: false after End is tapped or the call is accepted |
+| `errorEvent` | `SharedFlow` |
+| `ringTimeoutMs` | How long to ring before this side ends the call as unanswered. Default `DEFAULT_RING_TIMEOUT_MS` (45 000); 0 or less disables the timer |
+| `closeGraceMs` | How long to wait after the prepared session closes for the terminal push that carries the reason. Default `DEFAULT_CLOSE_GRACE_MS` (1 500) |
+| `cancelCall()` | Ends the call as `cancelled` |
+| `attachPreparedSession(owner)` | Listens on the prepared session; the component calls it for you |
+
+Showing the reason, for example a "Busy" label before the screen closes:
+
+```kotlin lines
+lifecycleScope.launch {
+ outgoingCall.getViewModel()?.endReason?.collect { reason ->
+ if (reason == UIKitConstants.CallStatusConstants.BUSY) showBusy()
+ }
+}
+```
---
diff --git a/ui-kit/android/sound-manager.mdx b/ui-kit/android/sound-manager.mdx
index 92e7e846c..e34bb36c9 100644
--- a/ui-kit/android/sound-manager.mdx
+++ b/ui-kit/android/sound-manager.mdx
@@ -24,8 +24,8 @@ description: "Play, pause, and customize audio cues for calls and messages using
| Sound | When it plays |
| --- | --- |
-| `Sound.INCOMING_CALL` | Incoming call received (with vibration) |
-| `Sound.OUTGOING_CALL` | Outgoing call initiated |
+| `Sound.INCOMING_CALL` | Only without the Push Notifications SDK calls module. With it, the incoming ringtone is the device ringtone on the Push SDK's `incoming_calls_v1` channel, and the UI Kit plays nothing |
+| `Sound.OUTGOING_CALL` | Outgoing call initiated (ringback on the caller's screen) |
| `Sound.INCOMING_MESSAGE` | Message received in the active conversation |
| `Sound.OUTGOING_MESSAGE` | Message sent |
| `Sound.INCOMING_MESSAGE_FROM_OTHER` | Message received from a different conversation |
@@ -67,7 +67,7 @@ soundManager.release()
## Disable Sounds on Components
-Both UI Kit modules let you disable sounds on individual components.
+Both UI Kit modules let you disable sounds on individual components. For calls, `setDisableSoundForCalls` and `setCustomSoundForCalls` apply to the outgoing ringback tone; the incoming ringtone belongs to the Push Notifications SDK and is changed through the notification channel, not here.
diff --git a/ui-kit/android/troubleshooting.mdx b/ui-kit/android/troubleshooting.mdx
index 1bf4ee2d8..0268b157a 100644
--- a/ui-kit/android/troubleshooting.mdx
+++ b/ui-kit/android/troubleshooting.mdx
@@ -78,15 +78,22 @@ description: "Common failure modes and fixes for the CometChat Android V6 UI Kit
| Symptom | Cause | Fix |
| --- | --- | --- |
-| Call buttons not appearing | `com.cometchat:calls-sdk-android` not installed | Add the calls SDK dependency to your `build.gradle` — the UI Kit auto-detects it. See [Integration](/ui-kit/android/getting-started) |
-| Incoming call screen not showing | `CometChatIncomingCall` not added to the Activity | Render `CometChatIncomingCall` at the root of your Activity layout so it can listen for incoming calls |
-| Incoming calls not detected | Call events not observed | Ensure you collect from `CometChatEvents.onIncomingCall` SharedFlow in your Application class or relevant scope |
-| Call connects but no audio/video | Missing runtime permissions | Request `CAMERA`, `RECORD_AUDIO`, and `INTERNET` permissions at runtime before initiating calls |
-| Call fails immediately | Calls SDK version mismatch | Ensure `com.cometchat:calls-sdk-android` version is compatible with your UI Kit version. Check the [Integration](/ui-kit/android/getting-started) page for version pairing |
-| Sound still plays after disabling | `disableSoundForCalls(true)` called too late | Call `disableSoundForCalls(true)` before the component starts the call. If called after the call is initiated, it may not take effect |
-| Custom call sound not playing | Audio file missing or wrong path | Verify the sound file exists in `res/raw` and pass the correct `@RawRes int` resource ID to `setCustomSoundForCalls` |
-| Call events not received | Not collecting from SharedFlow | Use `CometChatEvents` singleton and collect from the appropriate SharedFlow (e.g., `onCallEnded`, `onIncomingCall`) in a coroutine scope |
-| Call events still firing after screen closed | Coroutine scope not cancelled | Cancel the coroutine scope collecting from `CometChatEvents` SharedFlows in `onDestroy()` or `onDestroyView()` |
+| Call buttons not appearing | `enableCalling` not set | `setEnableCalling(true)` in `UIKitSettings` (or `"enableCalling": true` in `cometchat-settings.json`). See [Calling Integration](/ui-kit/android/calling-integration) |
+| Call logs stay empty, call buttons fail with `CALLS_SDK_MISSING`, the call screen does not start, and logcat says `Calls SDK is not on the classpath` (UI Kits before 6.2.0 crashed with `NoClassDefFoundError` instead) | The Calls SDK is a compile-only dependency of the UI Kit, not bundled | Add `implementation("com.cometchat:calls-sdk-android:5.1.0")` to the app module next to the UI Kit. See [Calling Integration](/ui-kit/android/calling-integration#step-1-dependencies) |
+| Call logs show a silent error after enabling calling | User logged in before calling was enabled, so the Calls SDK never logged in | `CometChatUIKit.logout()` then `CometChatUIKit.login()` once |
+| Incoming calls never ring | Push ringing not configured, or the push not handed to the SDK | `CometChatPushNotifications.init` with `setProviderId` + `setAnswerActivity(...)`, and `handlePushNotification(this, data, sentTime)` for every push in your `FirebaseMessagingService`. See [Push Ringing](/calls/android/voip-calling) |
+| Rings on an emulator or a fresh install, but not on a user's phone | Device settings silence the ring | Run `CometChatPushNotifications.ensureRingReady(activity)` after sign-in and show any result other than `READY` (notifications off, muted channel, full-screen intent revoked, Do Not Disturb) |
+| Rings, then the process dies before Answer | Call push sent at normal FCM priority | Send call pushes at high priority |
+| Answer opens the app but the call does not join | The answer activity does not hand the call to the UI Kit, or the SDK is not initialised on a cold start | In your answer activity, build the call with `CallRingingUtils.buildIncomingCall` and call `CometChatOngoingCallActivity.launchOngoingCallActivity(this, call)` (see [Calling Integration](/ui-kit/android/calling-integration#step-3-configure-push-ringing)), and run `CometChatUIKit.init` in `Application.onCreate` from stored credentials |
+| Caller's screen stays on "calling…" and never shows "ringing…" | Callee's device not reached (push not delivered) | Check the callee's `ensureRingReady`, provider id and host; the caller times out as unanswered after 45 s |
+| Audio call opens the caller's camera | Custom call start without a session type | Place calls with `CallInitiationUtils.initiateUserCall`, or set `setSessionType(SessionType.VOICE)` in the `InitiateCallOptions` session settings |
+| Ring keeps playing after logout, or declines fail after switching users | Logged out with `CometChat.logout` only | Use `CometChatUIKit.logout()` |
+| Two ringtones or two incoming screens | Legacy `CometChat.addCallListener` + `CometChatIncomingCall` overlay still in the app | Remove them; the Push SDK rings. See [Migrating from Legacy Ringing](/calls/android/migrating-from-legacy-ringing) |
+| Call connects but no audio/video | Microphone or camera permission denied at runtime | Nothing to declare: `CAMERA` and `RECORD_AUDIO` merge into your manifest from the Calls SDK. They must be granted at runtime; the Push SDK asks on Answer and the call screen asks when it opens. A denied microphone joins muted. Check the app's permissions in system settings |
+| Sound still plays after disabling | `disableSoundForCalls(true)` called too late, or it is the incoming ringtone | `setDisableSoundForCalls` only controls the outgoing ringback; the incoming ringtone is the Push SDK's notification channel |
+| Custom call sound not playing | Audio file missing or wrong path | Verify the sound file exists in `res/raw` and pass the correct `@RawRes int` resource ID to `setCustomSoundForCalls` (outgoing ringback only) |
+| Call events not received | Listening on the wrong SDK | Listen with `CometChatCalls.addIncomingCallListener` (a call starting or ending), or collect `CometChatEvents.callEvents` (`CometChatCallEvent.OutgoingCall`, `CallRejected`, `CallEnded`; `CallRejected` and `CallEnded` only fire when this device ends the call, see Outgoing Call and Ongoing Call). The Chat SDK `CallListener` is superseded and fires nothing in this flow |
+| Call events still firing after screen closed | Listener or coroutine scope not removed | `removeIncomingCallListener(id)` and cancel the collecting scope in `onDestroy()` or `onDestroyView()` |
---
diff --git a/ui-kit/android/upgrading-from-v5.mdx b/ui-kit/android/upgrading-from-v5.mdx
index 661feb5ef..ff9906de2 100644
--- a/ui-kit/android/upgrading-from-v5.mdx
+++ b/ui-kit/android/upgrading-from-v5.mdx
@@ -368,6 +368,22 @@ import com.cometchat.uikit.kotlin.presentation.callbuttons.CometChatCallButtons
import com.cometchat.uikit.kotlin.presentation.calllogs.ui.CometChatCallLogs
```
+#### Ringing
+
+V6 rings incoming calls through the CometChat Push Notifications SDK instead of the Chat SDK's `CallListener`. What changes in your app:
+
+| V5 | V6 |
+| --- | --- |
+| `CometChat.addCallListener(...)` in the Application class showing `CometChatIncomingCall` | Remove it. The Push SDK rings and draws the incoming-call screen in every app state |
+| Push SDK 1.0 `setVoIPEnabled(true)` + `setOnCallAnsweredHandler` (which called `CometChat.acceptCall`) | `PNConfiguration.Builder(...).setProviderId(...).setAnswerActivity(CallAnswerActivity::class.java)`, where `CallAnswerActivity` opens `CometChatOngoingCallActivity`; see [Calling Integration](/ui-kit/android/calling-integration#step-3-configure-push-ringing) |
+| `FirebaseMessagingService` that skipped call pushes in the foreground and registered tokens manually | `handlePushNotification(this, data, sentTime)` for every push; tokens are automatic |
+| `CometChatIncomingCall` as the primary incoming UI | Deprecated; kept only for `setIncomingCallActivity` |
+| `CometChat.logout` | `CometChatUIKit.logout` (also logs out of the Calls SDK) |
+| Own ringtone, busy check, ConnectionService stack | Delete; the Push SDK does them |
+| `CometChatCallsConstants.*` | `UIKitConstants.CallStatusConstants` / `UIKitConstants.CallType` |
+
+No manifest permissions to add: `CAMERA`, `RECORD_AUDIO` and `POST_NOTIFICATIONS` merge in from the UI Kit, the Calls SDK and the Push SDK; request them at runtime. Add the Calls SDK (`com.cometchat:calls-sdk-android:5.1.0`, not bundled with the UI Kit), the two Push Notifications SDK artifacts and Firebase Messaging to your dependencies. Full walkthrough: [Calling Integration](/ui-kit/android/calling-integration) and [Migrating from Legacy Ringing](/calls/android/migrating-from-legacy-ringing).
+
## Architecture Changes