cometchat-android-v6-push

SkillCommunication

Add push notifications (FCM) to an Android v6 CometChat app — Firebase + Dashboard provider setup, the push dependency, token registration after login, token refresh, unregister on logout, manifest/service wiring, badge count and notification navigation. THIN + docs-first: fetches the notifications

Available today. Use it from your connected AI after setup.

Connect ahel once, and every AI you use reads what you have installed.

Then ask your AI: use the cometchat-android-v6-push skill

What this skill tells your AI

The instructions your AI receives, as published by cometchat/cometchat-skills in skills/cometchat-android-v6-push/SKILL.md and read by ahel’s review.

Ground truth: com.cometchat:chatuikit-{compose,kotlin}-android:6.x (+ calls-sdk-android:5.x) — resolved AAR (javap) + ui-kit/android/v6. Official docs: https://www.cometchat.com/docs/notifications/overview · Docs MCP: claude mcp add --transport http cometchat-docs https://www.cometchat.com/docs/mcp (or fetch the URL directly without MCP). Verify symbols against the installed package/source before relying on them.

Companion skills: cometchat-android-v6-core (init/login), cometchat-android-v6-events (call events), cometchat-android-v6-builder-settings (calling config)

Purpose

Set up push notifications for CometChat v6 using Firebase Cloud Messaging (FCM), handle chat and call notifications, and integrate VoIP for background call handling.

Use this skill when

  • Adding FCM push notifications for chat messages
  • Handling incoming call notifications (foreground and background)
  • Setting up VoIP with TelecomManager for native call UI
  • Implementing deep-link navigation from notifications

Do not use this skill when

  • Just initializing the SDK (use cometchat-android-v6-core)
  • Working with UI components (use cometchat-*-components)

1. FCM Setup

1.1 Gradle Dependencies

// In project-level build.gradle
plugins {
    id("com.google.gms.google-services") version "4.4.2" apply false
}

// In app-level build.gradle.kts
plugins {
    id("com.google.gms.google-services")
}

dependencies {
    implementation(platform("com.google.firebase:firebase-bom:33.x.x"))
    implementation("com.google.firebase:firebase-messaging")
}

Add google-services.json to your app module's root directory.

1.2 Initialize Firebase

// In Application.onCreate()
FirebaseApp.initializeApp(this)

1.3 Register / unregister the push token (the kit's ONLY public push API)

This is the single load-bearing client step. CometChatNotifications.registerPushToken(...) binds the FCM token to the logged-in CometChat user; unregisterPushToken(...) clears it. The Android PushPlatforms constant is FCM_ANDROID (verified in com.cometchat.chat.enums.PushPlatforms — the only Android value). Everything in §2 onward is sample-app glue you control; this is the real API.

import com.cometchat.chat.core.CometChatNotifications
import com.cometchat.chat.enums.PushPlatforms
import com.cometchat.chat.exceptions.CometChatException
import com.google.firebase.messaging.FirebaseMessaging

// Call AFTER CometChatUIKit.login(...) resolves (a token registered before
// login is not bound to a user). Also call from FCMService.onNewToken when a
// session already exists (token rotation).
fun registerPushToken() {
    FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
        if (!task.isSuccessful) return@addOnCompleteListener
        CometChatNotifications.registerPushToken(
            task.result,
            PushPlatforms.FCM_ANDROID,
            "YOUR_PROVIDER_ID",  // FCM Provider ID from the CometChat dashboard (§4 setup)
            object : CometChat.CallbackListener<String>() {
                override fun onSuccess(s: String?) { /* registered */ }
                override fun onError(e: CometChatException) { /* log + retry */ }
            }
        )
    }
}

// Call BEFORE CometChatUIKit.logout() — otherwise the logged-out device keeps
// receiving pushes for the previous user.
fun unregisterPushToken(onDone: () -> Unit) {
    CometChatNotifications.unregisterPushToken(object : CometChat.CallbackListener<String>() {
        override fun onSuccess(s: String?) { onDone() }
        override fun onError(e: CometChatException) { onDone() } // proceed with logout regardless
    })
}

Lifecycle: register after login resolves (and on onNewToken when a session exists), unregister before logout. This mirrors the verified cometchat-android-v5-push pattern — the registration API is identical across v5/v6 (it lives in the Chat SDK, not the UI Kit).

2. FCMService Implementation

Heads-up — this is a reference pattern. The classes referenced below (CometChatVoIP, CometChatVoIPConnectionService, FCMMessageDTO, FCMCallDto, FCMService, VoIPPermissionListener, CometChatVoIPUtils, FCMMessageNotificationUtils) are NOT exported from com.cometchat:chatuikit-{compose,kotlin}-android:6.x. They are sample-app glue (derived from the CometChat Android push sample app — note: not present in the v6 kit clone, so treat them as a copy-in/reference pattern) — copy the relevant source files into your project, or use them as a guide for writing your own equivalents. The kit's only public push surface is CometChatNotifications.registerPushToken(...) / unregisterPushToken(...); everything below is glue you control.

Pattern from master-app-jetpack/fcm/FCMService.kt (sample-app code, copy into your project):

class FCMService : FirebaseMessagingService() {

    companion object {
        var fcmToken: String? = null
            private set
    }

    override fun onNewToken(token: String) {
        super.onNewToken(token)
        fcmToken = token
        // Store token for later use (e.g., SharedPreferences)
    }

    override fun onMessageReceived(message: RemoteMessage) {
        super.onMessageReceived(message)

        if (message.data.isEmpty()) return

        val type = message.data["type"]?.lowercase()
        when (type) {
            "chat" -> handleChatNotification(message)
            "call" -> handleCallNotification(message)
        }
    }
}

2.1 Chat Notification Handling

private fun handleChatNotification(message: RemoteMessage) {
    val fcmMessageDTO = Gson().fromJson(
        Gson().toJson(message.data),
        FCMMessageDTO::class.java
    )

    // Mark as delivered for read receipts (only if SDK is initialized)
    if (CometChatUIKit.isSDKInitialized()) {
        CometChat.markAsDelivered(
            fcmMessageDTO.tag!!.toLong(),
            fcmMessageDTO.sender!!,
            fcmMessageDTO.receiverType!!,
            fcmMessageDTO.receiver!!
        )
    }

    // Show notification if user is NOT in the same chat
    val isUser = fcmMessageDTO.receiverType == CometChatConstants.RECEIVER_TYPE_USER
    val uid = if (isUser) fcmMessageDTO.sender!! else fcmMessageDTO.receiver!!

    if (uid != currentOpenChatId) {
        // Build and show notification
        FCMMessageNotificationUtils.showNotification(
            this, fcmMessageDTO, intent,
            NOTIFICATION_KEY_REPLY_ACTION,
            NotificationCompat.CATEGORY_MESSAGE
        )
    }
}

2.2 Call Notification Handling

private fun handleCallNotification(message: RemoteMessage) {
    val sessionId = message.data["sessionId"]
    val callAction = message.data["callAction"]

    // CRITICAL: Check if SDK is initialized
    if (!CometChatUIKit.isSDKInitialized()) {
        return // Cannot handle call without SDK
    }

    // Check VoIP permissions
    if (!CometChatVoIP.hasReadPhoneStatePermission(this) ||
        !CometChatVoIP.hasManageOwnCallsPermission(this) ||
        !CometChatVoIP.hasAnswerPhoneCallsPermission(this)) {
        return
    }

    // Check phone account is enabled
    CometChatVoIP.hasEnabledPhoneAccountForVoIP(this, object : VoIPPermissionListener {
        override fun onPermissionsGranted() {
            handleCallFlow(message)
        }
        override fun onPermissionsDenied(error: CometChatVoIPError?) {
            // Cannot show VoIP UI
        }
    })
}

3. VoIP Integration

3.1 Required Permissions

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-permission android:name="android.permission.ANSWER_PHONE_CALLS" />

3.2 VoIP Call Flow

private fun handleCallFlow(message: RemoteMessage) {
    val callData = Gson().fromJson(
        Gson().toJson(message.data),
        FCMCallDto::class.java
    )

    // Initialize VoIP
    CometChatVoIP.init(this, applicationInfo.loadLabel(packageManager).toString())

    when (callData.callAction) {
        "initiated" -> {
            // Show incoming call UI (only if app is in background)
            if (!isAppInForeground()) {
                voipIncomingCall(callData)
            }
            // If foreground, UIKit's CometChatIncomingCall handles it
        }
        "cancelled", "unanswered" -> {
            // End the native call UI if session matches
            if (CometChatVoIPUtils.currentSessionId == callData.sessionId) {
                CometChatVoIP.telecomManager?.endCall()
            }
        }
    }
}

3.3 Foreground vs Background Routing

App StateIncoming Call Handler
ForegroundUIKit's CometChatIncomingCall via SDK call listener
BackgroundVoIP via TelecomManager + CometChatVoIPConnectionService
KilledFCM wakes app, but SDK may not be initialized — cannot handle call

3.4 Busy Rejection

private fun rejectCallWithBusyStatus(call: Call) {
    CometChat.rejectCall(
        call.sessionId,
        CometChatConstants.CALL_STATUS_BUSY,
        object : CometChat.CallbackListener<Call>() {
            override fun onSuccess(rejectedCall: Call?) {
                rejectedCall?.let {
                    CometChatEvents.emitCallEvent(CometChatCallEvent.CallRejected(it))
                }
            }
            override fun onError(e: CometChatException) { }
        }
    )
}

4. AndroidManifest Registration

<service
    android:name=".fcm.FCMService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

<!-- VoIP Connection Service -->
<service
    android:name=".voip.CometChatVoIPConnectionService"
    android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
    android:exported="true">
    <intent-filter>
        <action android:name="android.telecom.ConnectionService" />
    </intent-filter>
</service>

5. WebSocket Connection Management

From master-app-jetpack/Application.kt — manage WebSocket connections based on app lifecycle:

// When app comes to foreground
CometChat.connect(object : CometChat.CallbackListener<String?>() {
    override fun onSuccess(s: String?) { /* connected */ }
    override fun onError(e: CometChatException) { /* failed */ }
})

// When app goes to background
CometChat.disconnect(object : CometChat.CallbackListener<String?>() {
    override fun onSuccess(s: String?) { /* disconnected */ }
    override fun onError(e: CometChatException) { /* failed */ }
})

Hard rules

  • ALWAYS check CometChatUIKit.isSDKInitialized() before making SDK calls in FCM service — the app may have been killed
  • NEVER show VoIP incoming call UI when app is in foreground — UIKit's CometChatIncomingCall handles foreground calls
  • VoIP requires READ_PHONE_STATE, MANAGE_OWN_CALLS, and ANSWER_PHONE_CALLS permissions — check all three before handling calls
  • CometChat.markAsDelivered() requires the SDK to be initialized — skip it if not
  • For push token registration, use CometChatNotifications.registerPushToken(...) / unregisterPushToken(...) — that is the kit's only public push API. The CometChatVoIP* / FCM* classes shown above are sample-app glue (master-app-jetpack), not part of chatuikit-{compose,kotlin}
  • Always manage WebSocket connections based on app lifecycle to avoid battery drain

Signals

GitHub stars
105
Forks
2
Last commit
Sep 2026
Advanced
Catalog kind
skill
Gateway key
cometchat-android-v6-push
Source
github.com/cometchat/cometchat-skills