| name | cometchat-android-v6-push |
| description | CometChat Android UIKit v6 push notifications — FCM setup, chat/call notification handling, VoIP integration, and deep-linking |
| license | MIT |
| compatibility | Android 9.0+ (API 28); Kotlin 1.9+; com.cometchat:chatuikit-compose-android:6.x / com.cometchat:chatuikit-kotlin-android:6.x |
| metadata | {"author":"CometChat","version":"3.0.0","tags":"cometchat, android, push, fcm, firebase, voip, notifications, calls"} |
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
plugins {
id("com.google.gms.google-services") version "4.4.2" apply false
}
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
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
fun registerPushToken() {
FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
if (!task.isSuccessful) return@addOnCompleteListener
CometChatNotifications.registerPushToken(
task.result,
PushPlatforms.FCM_ANDROID,
"YOUR_PROVIDER_ID",
object : CometChat.CallbackListener<String>() {
override fun onSuccess(s: String?) { }
override fun onError(e: CometChatException) { }
}
)
}
}
fun unregisterPushToken(onDone: () -> Unit) {
CometChatNotifications.unregisterPushToken(object : CometChat.CallbackListener<String>() {
override { onDone() }
{ onDone() }
})
}
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
}
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
)
if (CometChatUIKit.isSDKInitialized()) {
CometChat.markAsDelivered(
fcmMessageDTO.tag!!.toLong(),
fcmMessageDTO.sender!!,
fcmMessageDTO.receiverType!!,
fcmMessageDTO.receiver!!
)
}
val isUser = fcmMessageDTO.receiverType == CometChatConstants.RECEIVER_TYPE_USER
val uid = if (isUser) fcmMessageDTO.sender!! else fcmMessageDTO.receiver!!
if (uid != currentOpenChatId) {
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"]
if (!CometChatUIKit.isSDKInitialized()) {
return
}
if (!CometChatVoIP.hasReadPhoneStatePermission(this) ||
!CometChatVoIP.hasManageOwnCallsPermission(this) ||
!CometChatVoIP.hasAnswerPhoneCallsPermission(this)) {
return
}
CometChatVoIP.hasEnabledPhoneAccountForVoIP(this, object : VoIPPermissionListener {
override fun onPermissionsGranted() {
handleCallFlow(message)
}
override fun onPermissionsDenied(error: CometChatVoIPError?) {
}
})
}
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
)
CometChatVoIP.init(this, applicationInfo.loadLabel(packageManager).toString())
when (callData.callAction) {
"initiated" -> {
if (!isAppInForeground()) {
voipIncomingCall(callData)
}
}
"cancelled", "unanswered" -> {
if (CometChatVoIPUtils.currentSessionId == callData.sessionId) {
CometChatVoIP.telecomManager?.endCall()
}
}
}
}
3.3 Foreground vs Background Routing
| App State | Incoming Call Handler |
|---|
| Foreground | UIKit's CometChatIncomingCall via SDK call listener |
| Background | VoIP via TelecomManager + CometChatVoIPConnectionService |
| Killed | FCM 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>
<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:
CometChat.connect(object : CometChat.CallbackListener<String?>() {
override fun onSuccess(s: String?) { }
override fun onError(e: CometChatException) { }
})
CometChat.disconnect(object : CometChat.CallbackListener<String?>() {
override fun onSuccess(s: String?) { }
override fun onError(e: CometChatException) { }
})
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