cometchat-android-v6-calls

Add voice and video calling to an Android v6 CometChat app — the Calls SDK dependency, the settings-file calling toggle, call buttons in the message header, a global incoming-call listener, outgoing/o

SKILL.md

GitHub

Ground truth: catalog android-v6.json + features.android-v6.json (voice-video-calls) + contracts.android-v6.json (voice-video-calls) + the live pages calling-integration, call-features, components/{call-buttons,incoming-call,outgoing-call,call-logs}, guide-call-log-details. Deep Calls-SDK behavior: the /calls/android docs 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):

dependencies {
    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 (which core mandates for the Chat SDK's pre-AndroidX transitive) — add a Jetifier ignore-list, or the build breaks the moment you add calling. calls-sdk-android 5.0.x bundles a React-Native stack (react-android / hermes / react-native-webrtc), and Jetifier cannot transform react-android-*.aar — so with Jetifier on, the build dies at checkDebugAarMetadata with Execution 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 in gradle.properties:

android.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, flag false → calling components crash when used (RuntimeException: Please call the CometChatCalls.init() method …)

⚠️ BOTH are required — the artifact alone is not enough. uiKit.enableCalling: true in assets/cometchat-settings.json is what makes initFromSettings initialize CometChatCalls. Without it, CometChatCallLogs throws RuntimeException: Please call the CometChatCalls.init() method …; without the artifact it throws NoClassDefFoundError. 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 follow CallsUtils.isCallingEnabled(), so the buttons render with no extra code once calling is enabled.
  • Compose: NOT automatic — the CometChatMessageHeader composable defaults hideVoiceCallButton = true and hideVideoCallButton = true. Pass both explicitly:
    CometChatMessageHeader(user = user, hideVoiceCallButton = false, hideVideoCallButton = false)
    
    And do NOT use the header's auxiliaryView slot for other icons — it REPLACES the default auxiliary area that hosts the call buttons (they vanish). Extra header icons go in trailingView (see cometchat-android-v6-compose-components).

Call-flow ordering (BAKED — invariant)

  1. Dependency + enableCalling → call buttons appear in the message header (Views: nothing to wire; Compose: pass hideVoiceCallButton = false, hideVideoCallButton = false — see above).
  2. initFromSettings resolves → login resolves (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 returns onSuccess WITHOUT calling loginCometChatCalls, so the Calls SDK never gets an auth token (verified vs 6.0.5 CometChatUIKit.kt login + on device 2026-08-25). Symptom: CometChatCallLogs shows a silent error state; logcat says User 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(...) then CometChatUIKit.login(...) (or force a fresh login path once, e.g. on a calling-feature version bump). During development, wire onError on CometChatCallLogs so this failure is not silent. 3. Register a GLOBAL call listener so an incoming call rings from anywhere in the app — the docs recommend an Application subclass, registering before the UI Kit is used:

import 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-android 5.0.x bundles a React-Native stack (react-android / hermes / react-native-webrtc), declared at runtime scope in its POM — so they land on the runtime classpath and initFromSettings calls-init + CometChatCallLogs work 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 on SoLoader. If an active call fails to open with a SoLoader/UnsatisfiedLinkError/NoClassDefFoundError (React/JSI/ Hermes symbols), add the RN + SoLoader artifacts on the compile classpath and init SoLoader once in Application.onCreate before the first call surface:

// 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.

// 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.