Ground truth: catalog
android-v6.json+features.android-v6.json(voice-video-calls) +contracts.android-v6.json(voice-video-calls) + the live pagescalling-integration,call-features,components/{call-buttons,incoming-call,outgoing-call,call-logs},guide-call-log-details. Deep Calls-SDK behavior: the/calls/androiddocs tree. Verify symbols against the catalog; fetch params from docs; never trust memory.
Companion skills (read first)
cometchat-android-v6-core— install, credentials (assets/cometchat-settings.json),initFromSettings → login → render, sizing (references/layout.md), the fetch map. Assumed here, never repeated.cometchat-android-v6-{kotlin,compose}-placement— where the incoming-call surface sits in the app shell.
Use this skill when
"add voice/video calling", "show call buttons in the chat", "handle incoming calls", "add a call history screen", "the call buttons aren't appearing", "call log details".
Calls package + version
Add the Calls SDK alongside your cohort's UI Kit artifact (never instead of it):
kotlindependencies { implementation("com.cometchat:chatuikit-compose-android:6.0.5") // or chatuikit-kotlin-android for XML Views implementation("com.cometchat:calls-sdk-android:5.0.4") }
⚠️ REQUIRED whenever
android.enableJetifier=true(whichcoremandates for the Chat SDK's pre-AndroidX transitive) — add a Jetifier ignore-list, or the build breaks the moment you add calling.calls-sdk-android5.0.x bundles a React-Native stack (react-android/hermes/react-native-webrtc), and Jetifier cannot transformreact-android-*.aar— so with Jetifier on, the build dies atcheckDebugAarMetadatawithExecution failed for JetifyTransform: react-android-0.77.2-debug.aar(the app compiled fine BEFORE calling; adding the artifact breaks it — verified 2026-09-10). These AARs are already AndroidX, so exclude them from Jetifier ingradle.properties:propertiesandroid.jetifier.ignorelist=react-android,hermes-android,react-native-webrtc
⚠️ The artifact and the flag are a PAIR — setting either alone breaks the app.
· flag
true, artifact missing → app crashes at launch (initFromSettings→initCometChatCalls→NoClassDefFoundError: CometChatCalls$SessionSettingsBuilder) · artifact present, flagfalse→ calling components crash when used (RuntimeException: Please call the CometChatCalls.init() method …)⚠️ BOTH are required — the artifact alone is not enough.
uiKit.enableCalling: trueinassets/cometchat-settings.jsonis what makesinitFromSettingsinitializeCometChatCalls. Without it,CometChatCallLogsthrowsRuntimeException: Please call the CometChatCalls.init() method …; without the artifact it throwsNoClassDefFoundError. Both compile fine — device-only failures (verified 2026-08-21).
The kit auto-detects the Calls SDK and activates calling. Also set "uiKit": { "enableCalling": true } in app/src/main/assets/cometchat-settings.json so CometChatUIKit.initFromSettings(...) initializes the Calls SDK through the telemetry-aware path (it auto-inits Calls when that flag is on). Dashboard: calling must be enabled for the app.
Header call buttons differ BY COHORT (verified vs installed 6.0.5 source):
- Views: automatic —
CometChatMessageHeader's voice/video button visibility defaults followCallsUtils.isCallingEnabled(), so the buttons render with no extra code once calling is enabled. - Compose: NOT automatic — the
CometChatMessageHeadercomposable defaultshideVoiceCallButton = trueandhideVideoCallButton = true. Pass both explicitly:
And do NOT use the header'skotlinCometChatMessageHeader(user = user, hideVoiceCallButton = false, hideVideoCallButton = false)auxiliaryViewslot for other icons — it REPLACES the default auxiliary area that hosts the call buttons (they vanish). Extra header icons go intrailingView(seecometchat-android-v6-compose-components).
Call-flow ordering (BAKED — invariant)
- Dependency +
enableCalling→ call buttons appear in the message header (Views: nothing to wire; Compose: passhideVoiceCallButton = false, hideVideoCallButton = false— see above). initFromSettingsresolves →loginresolves (as always) — calling is unavailable before that.
⚠️ Enabling calling in an app with EXISTING sessions — force a fresh login.
CometChatUIKit.login()short-circuits when the same uid is already logged in (persisted session): it returnsonSuccessWITHOUT callingloginCometChatCalls, so the Calls SDK never gets an auth token (verified vs 6.0.5CometChatUIKit.ktlogin + on device 2026-08-25). Symptom:CometChatCallLogsshows a silent error state; logcat saysUser auth token cannot be null. When calling is FIRST enabled on an app whose users already have a session, users must re-login —CometChatUIKit.logout(...)thenCometChatUIKit.login(...)(or force a fresh login path once, e.g. on a calling-feature version bump). During development, wireonErroronCometChatCallLogsso this failure is not silent.
- Register a GLOBAL call listener so an incoming call rings from anywhere in the app — the docs recommend an
Applicationsubclass, registering before the UI Kit is used:
kotlinimport com.cometchat.chat.core.Call // NOTE: chat.core, NOT chat.models import com.cometchat.chat.core.CometChat 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) { /* show CometChatIncomingCall over the current screen */ } override fun onOutgoingCallAccepted(call: Call) { /* proceed to the ongoing-call surface */ } override fun onOutgoingCallRejected(call: Call) { /* dismiss the outgoing UI */ } override fun onIncomingCallCancelled(call: Call) { /* dismiss the incoming UI */ } }) } }
Register the Application class in the manifest (android:name=".BaseApplication"). Views: instantiate CometChatIncomingCall(context), set its call, set fitsSystemWindows = true, wire its error handler, and present it (dialog / full-screen overlay). Compose: hold the incoming Call in app-level state and render the incoming-call composable above your nav host — fetch the composable's params from components/incoming-call.md.
4. Permissions — request CAMERA and RECORD_AUDIO at runtime before a call starts (video needs both; audio-only needs RECORD_AUDIO); declare them in the manifest. The kit ships CometChatPermissionHandler / CometChatPermissionActivity — prefer them over a hand-rolled flow.
ℹ️ SoLoader / bundled React-Native native libs (know this before an ACTIVE call).
calls-sdk-android5.0.x bundles a React-Native stack (react-android/hermes/react-native-webrtc), declared atruntimescope in its POM — so they land on the runtime classpath andinitFromSettingscalls-init +CometChatCallLogswork with no extra deps (verified on device: init succeeds, call logs render). An active call surface loads those native.sos, which on RN ≥ 0.76 depend onSoLoader. If an active call fails to open with aSoLoader/UnsatisfiedLinkError/NoClassDefFoundError(React/JSI/ Hermes symbols), add the RN + SoLoader artifacts on the compile classpath and init SoLoader once inApplication.onCreatebefore the first call surface:kotlin// build.gradle.kts (only if an active call throws a SoLoader/native-link error) implementation("com.facebook.react:react-android:0.77.2") // match what calls-sdk-android pulls implementation("com.facebook.soloader:soloader:0.12.1") // Application.onCreate: SoLoader.init(this, OpenSourceMergedSoMapping)Whether 5.0.x needs this for an active call is not yet settled from a single device — it is NOT needed for init or call logs. Verify with a real two-device call; treat this as the fix if (and only if) you hit that native-link error, not as an unconditional step.
Calls component / API map (BAKED closed list)
CometChatCallButtons (auto-rendered in the message header; can also be placed standalone) · CometChatIncomingCall (ringing UI — accept/decline) · CometChatOutgoingCall (dialing UI) · CometChatOngoingCall + CometChatOngoingCallActivity / CometChatCallActivity (the in-call surface) · CometChatCallLogs (call history list) + CometChatCallLogsListItem · call bubbles in the message list: CometChatCallActionBubble, CometChatMeetCallBubble · CometChatCallEvent (call events → cometchat-android-v6-events). SDK-side (build-your-own flows): CometChat.initiateCall / acceptCall / rejectCall / getActiveCall / clearActiveCall / addCallListener / removeCallListener — see core/references/docs-map.md → SDK section.
The minimum for "add calling" (contract voice-video-calls): initiate (buttons) + incoming-call UI + outgoing-call UI + call logs. A call button with no incoming-call handler is a dead end — the callee never sees the ring.
kotlin// Minimum calls surface — four components for voice-video-calls contract // CometChatCallButtons: auto-rendered in the message header; place standalone when needed val callButtons = CometChatCallButtons(context) // CometChatOutgoingCall: shown while the callee hasn't answered yet val outgoingCall = CometChatOutgoingCall(context) outgoingCall.call = pendingCall // CometChatCallActivity / CometChatOngoingCallActivity: the in-call surface, launched // automatically during a session. ⚠️ You do NOT declare these — BOTH are already declared in the // UI-Kit AAR manifest and merge in automatically (verified vs 6.0.x). The cohort-correct classes: // Compose: com.cometchat.uikit.compose.calls.CometChatCallActivity // com.cometchat.uikit.compose.presentation.ongoingcall.ui.CometChatOngoingCallActivity // Views: com.cometchat.uikit.kotlin.calls.CometChatCallActivity // com.cometchat.uikit.kotlin.presentation.ongoingcall.ui.CometChatOngoingCallActivity // (There is NO `com.cometchat.chatuikit.calling.*` class in v6 — that's a v5/v4-era name; do not // add a manual <activity> line for it or the merge/launch breaks.) // CometChatCallLogs: call history tab val callLogs = CometChatCallLogs(context)
Deeper calling features → fetch
| Intent | Page (append .md) |
|---|---|
| calling overview / what's supported | /ui-kit/android/call-features |
| call buttons props | /ui-kit/android/call-buttons |
| incoming / outgoing UI props | /ui-kit/android/incoming-call · /ui-kit/android/outgoing-call |
| call logs + details recipe | /ui-kit/android/call-logs · /ui-kit/android/guide-call-log-details |
| custom in-call UI, layouts, recording, screen share, audio modes | the /calls/android/* pages (call-layouts, custom-control-panel, audio-modes, background-handling, idle-timeout, events, …) |
Common pitfalls
Importing com.cometchat.chat.models.Call (it is com.cometchat.chat.core.Call — the wrong import compiles to an "Unresolved reference 'Call'" cascade) · manually declaring CometChatCallActivity/CometChatOngoingCallActivity in your manifest (they AUTO-MERGE from the UI-Kit AAR — a hand-written <activity> with the wrong/legacy com.cometchat.chatuikit.calling.* name breaks the launch) · Calls SDK missing (buttons never appear) · enableCalling not set in the settings file · Compose header without hideVoiceCallButton = false / hideVideoCallButton = false (buttons never appear — Compose defaults hide them) · Compose auxiliaryView used for a custom header icon (it replaces the call-button host — use trailingView) · calling enabled on an app with a persisted session without a re-login (Calls SDK tokenless — call logs silently error with "User auth token cannot be null") · no global call listener (incoming calls never ring) · listener registered per-Activity only (rings only on that screen) · registering in Application but forgetting android:name in the manifest · missing runtime CAMERA/RECORD_AUDIO permissions (call fails silently) · calling before login resolves · call UI not fitsSystemWindows / not full-bleed (controls under system bars) · leaking the listener (remove it if you register outside Application) · mixing a v4/v5 calls artifact with the v6 kit.
Verify it works
Compile against the pinned kit (./gradlew :app:assembleDebug # in YOUR app (npm run verify:fences:android-v6 is a pack-repo gate)). On device (TWO devices/emulators + two users — calling can't be verified single-ended): call buttons render in the message header; tapping starts an outgoing call; the OTHER device rings with the incoming UI from any screen; accept → ongoing call with audio/video; decline/cancel dismisses cleanly on both sides; the call appears in CometChatCallLogs and as a call bubble in the chat. No ring ⇒ listener not registered globally (or app not logged in). Permission denial ⇒ handle it and tell the user, don't fail silently.

