{"id":"18fb88d7-cd1f-4400-9a43-5ccf5287f688","shortId":"GutEnx","kind":"skill","title":"cometchat-android-v6-push","tagline":"CometChat Android UIKit v6 push notifications — FCM setup, chat/call notification handling, VoIP integration, and deep-linking","description":"> **Companion skills:** cometchat-android-v6-core (init/login), cometchat-android-v6-events (call events), cometchat-android-v6-builder-settings (calling config)\n\n## Purpose\n\nSet up push notifications for CometChat v6 using Firebase Cloud Messaging (FCM), handle chat and call notifications, and integrate VoIP for background call handling.\n\n## Use this skill when\n\n- Adding FCM push notifications for chat messages\n- Handling incoming call notifications (foreground and background)\n- Setting up VoIP with TelecomManager for native call UI\n- Implementing deep-link navigation from notifications\n\n## Do not use this skill when\n\n- Just initializing the SDK (use `cometchat-android-v6-core`)\n- Working with UI components (use `cometchat-*-components`)\n\n## 1. FCM Setup\n\n### 1.1 Gradle Dependencies\n\n```kotlin\n// In project-level build.gradle\nplugins {\n    id(\"com.google.gms.google-services\") version \"4.4.2\" apply false\n}\n\n// In app-level build.gradle.kts\nplugins {\n    id(\"com.google.gms.google-services\")\n}\n\ndependencies {\n    implementation(platform(\"com.google.firebase:firebase-bom:33.x.x\"))\n    implementation(\"com.google.firebase:firebase-messaging\")\n}\n```\n\nAdd `google-services.json` to your app module's root directory.\n\n### 1.2 Initialize Firebase\n\n```kotlin\n// In Application.onCreate()\nFirebaseApp.initializeApp(this)\n```\n\n## 2. FCMService Implementation\n\n> **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 live in the `master-app-jetpack` sample app — 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.\n\nPattern from `master-app-jetpack/fcm/FCMService.kt` (sample-app code, copy into your project):\n\n```kotlin\nclass FCMService : FirebaseMessagingService() {\n\n    companion object {\n        var fcmToken: String? = null\n            private set\n    }\n\n    override fun onNewToken(token: String) {\n        super.onNewToken(token)\n        fcmToken = token\n        // Store token for later use (e.g., SharedPreferences)\n    }\n\n    override fun onMessageReceived(message: RemoteMessage) {\n        super.onMessageReceived(message)\n\n        if (message.data.isEmpty()) return\n\n        val type = message.data[\"type\"]?.lowercase()\n        when (type) {\n            \"chat\" -> handleChatNotification(message)\n            \"call\" -> handleCallNotification(message)\n        }\n    }\n}\n```\n\n### 2.1 Chat Notification Handling\n\n```kotlin\nprivate fun handleChatNotification(message: RemoteMessage) {\n    val fcmMessageDTO = Gson().fromJson(\n        Gson().toJson(message.data),\n        FCMMessageDTO::class.java\n    )\n\n    // Mark as delivered for read receipts (only if SDK is initialized)\n    if (CometChatUIKit.isSDKInitialized()) {\n        CometChat.markAsDelivered(\n            fcmMessageDTO.tag!!.toLong(),\n            fcmMessageDTO.sender!!,\n            fcmMessageDTO.receiverType!!,\n            fcmMessageDTO.receiver!!\n        )\n    }\n\n    // Show notification if user is NOT in the same chat\n    val isUser = fcmMessageDTO.receiverType == CometChatConstants.RECEIVER_TYPE_USER\n    val uid = if (isUser) fcmMessageDTO.sender!! else fcmMessageDTO.receiver!!\n\n    if (uid != currentOpenChatId) {\n        // Build and show notification\n        FCMMessageNotificationUtils.showNotification(\n            this, fcmMessageDTO, intent,\n            NOTIFICATION_KEY_REPLY_ACTION,\n            NotificationCompat.CATEGORY_MESSAGE\n        )\n    }\n}\n```\n\n### 2.2 Call Notification Handling\n\n```kotlin\nprivate fun handleCallNotification(message: RemoteMessage) {\n    val sessionId = message.data[\"sessionId\"]\n    val callAction = message.data[\"callAction\"]\n\n    // CRITICAL: Check if SDK is initialized\n    if (!CometChatUIKit.isSDKInitialized()) {\n        return // Cannot handle call without SDK\n    }\n\n    // Check VoIP permissions\n    if (!CometChatVoIP.hasReadPhoneStatePermission(this) ||\n        !CometChatVoIP.hasManageOwnCallsPermission(this) ||\n        !CometChatVoIP.hasAnswerPhoneCallsPermission(this)) {\n        return\n    }\n\n    // Check phone account is enabled\n    CometChatVoIP.hasEnabledPhoneAccountForVoIP(this, object : VoIPPermissionListener {\n        override fun onPermissionsGranted() {\n            handleCallFlow(message)\n        }\n        override fun onPermissionsDenied(error: CometChatVoIPError?) {\n            // Cannot show VoIP UI\n        }\n    })\n}\n```\n\n## 3. VoIP Integration\n\n### 3.1 Required Permissions\n\n```xml\n<uses-permission android:name=\"android.permission.READ_PHONE_STATE\" />\n<uses-permission android:name=\"android.permission.MANAGE_OWN_CALLS\" />\n<uses-permission android:name=\"android.permission.ANSWER_PHONE_CALLS\" />\n```\n\n### 3.2 VoIP Call Flow\n\n```kotlin\nprivate fun handleCallFlow(message: RemoteMessage) {\n    val callData = Gson().fromJson(\n        Gson().toJson(message.data),\n        FCMCallDto::class.java\n    )\n\n    // Initialize VoIP\n    CometChatVoIP.init(this, applicationInfo.loadLabel(packageManager).toString())\n\n    when (callData.callAction) {\n        \"initiated\" -> {\n            // Show incoming call UI (only if app is in background)\n            if (!isAppInForeground()) {\n                voipIncomingCall(callData)\n            }\n            // If foreground, UIKit's CometChatIncomingCall handles it\n        }\n        \"cancelled\", \"unanswered\" -> {\n            // End the native call UI if session matches\n            if (CometChatVoIPUtils.currentSessionId == callData.sessionId) {\n                CometChatVoIP.telecomManager?.endCall()\n            }\n        }\n    }\n}\n```\n\n### 3.3 Foreground vs Background Routing\n\n| App State | Incoming Call Handler |\n|---|---|\n| Foreground | UIKit's `CometChatIncomingCall` via SDK call listener |\n| Background | VoIP via `TelecomManager` + `CometChatVoIPConnectionService` |\n| Killed | FCM wakes app, but SDK may not be initialized — cannot handle call |\n\n### 3.4 Busy Rejection\n\n```kotlin\nprivate fun rejectCallWithBusyStatus(call: Call) {\n    CometChat.rejectCall(\n        call.sessionId,\n        CometChatConstants.CALL_STATUS_BUSY,\n        object : CometChat.CallbackListener<Call>() {\n            override fun onSuccess(rejectedCall: Call?) {\n                rejectedCall?.let {\n                    CometChatEvents.emitCallEvent(CometChatCallEvent.CallRejected(it))\n                }\n            }\n            override fun onError(e: CometChatException) { }\n        }\n    )\n}\n```\n\n## 4. AndroidManifest Registration\n\n```xml\n<service\n    android:name=\".fcm.FCMService\"\n    android:exported=\"false\">\n    <intent-filter>\n        <action android:name=\"com.google.firebase.MESSAGING_EVENT\" />\n    </intent-filter>\n</service>\n\n<!-- VoIP Connection Service -->\n<service\n    android:name=\".voip.CometChatVoIPConnectionService\"\n    android:permission=\"android.permission.BIND_TELECOM_CONNECTION_SERVICE\"\n    android:exported=\"true\">\n    <intent-filter>\n        <action android:name=\"android.telecom.ConnectionService\" />\n    </intent-filter>\n</service>\n```\n\n## 5. WebSocket Connection Management\n\nFrom `master-app-jetpack/Application.kt` — manage WebSocket connections based on app lifecycle:\n\n```kotlin\n// When app comes to foreground\nCometChat.connect(object : CometChat.CallbackListener<String?>() {\n    override fun onSuccess(s: String?) { /* connected */ }\n    override fun onError(e: CometChatException) { /* failed */ }\n})\n\n// When app goes to background\nCometChat.disconnect(object : CometChat.CallbackListener<String?>() {\n    override fun onSuccess(s: String?) { /* disconnected */ }\n    override fun onError(e: CometChatException) { /* failed */ }\n})\n```\n\n## Hard rules\n\n- ALWAYS check `CometChatUIKit.isSDKInitialized()` before making SDK calls in FCM service — the app may have been killed\n- NEVER show VoIP incoming call UI when app is in foreground — UIKit's `CometChatIncomingCall` handles foreground calls\n- VoIP requires READ_PHONE_STATE, MANAGE_OWN_CALLS, and ANSWER_PHONE_CALLS permissions — check all three before handling calls\n- `CometChat.markAsDelivered()` requires the SDK to be initialized — skip it if not\n- 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}`\n- Always manage WebSocket connections based on app lifecycle to avoid battery drain","tags":["cometchat","android","push","skills","agent-skills","ai-agent","chat","claude-code","cursor","messaging","nextjs","react"],"capabilities":["skill","source-cometchat","skill-cometchat-android-v6-push","topic-agent-skills","topic-ai-agent","topic-chat","topic-claude-code","topic-cometchat","topic-cursor","topic-messaging","topic-nextjs","topic-react","topic-react-native","topic-ui-kit"],"categories":["cometchat-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/cometchat/cometchat-skills/cometchat-android-v6-push","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add cometchat/cometchat-skills","source_repo":"https://github.com/cometchat/cometchat-skills","install_from":"skills.sh"}},"qualityScore":"0.463","qualityRationale":"deterministic score 0.46 from registry signals: · indexed on github topic:agent-skills · 27 github stars · SKILL.md body (8,790 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T19:04:47.357Z","embedding":null,"createdAt":"2026-05-07T13:05:06.627Z","updatedAt":"2026-05-18T19:04:47.357Z","lastSeenAt":"2026-05-18T19:04:47.357Z","tsv":"'/application.kt':629 '/fcm/fcmservice.kt':273 '1':128 '1.1':131 '1.2':180 '2':188 '2.1':333 '2.2':411 '3':477 '3.1':480 '3.2':484 '3.3':549 '3.4':585 '33':164 '4':616 '4.4.2':145 '5':620 '6':220 'account':456 'action':408 'ad':75 'add':171 'alway':682,782 'android':3,7,27,33,40,118,219 'androidmanifest':617 'answer':724 'api':760 'app':150,175,228,231,271,276,519,554,575,627,635,639,660,693,705,770,774,788 'app-level':149 'appli':146 'application.oncreate':185 'applicationinfo.loadlabel':507 'avoid':791 'background':68,88,522,552,567,663 'base':633,786 'batteri':792 'bom':163 'build':397 'build.gradle':139 'build.gradle.kts':152 'builder':42 'busi':586,598 'call':36,44,62,69,84,96,330,412,440,486,515,539,557,565,584,592,593,605,688,702,714,722,726,733 'call.sessionid':595 'callact':426,428 'calldata':495,526 'calldata.callaction':511 'calldata.sessionid':546 'cancel':534 'cannot':438,473,582 'chat':60,80,327,334,380 'chat/call':14 'chatuikit':216,779 'check':430,443,454,683,728 'class':200,283,764 'class.java':351,502 'cloud':56 'code':277 'com.cometchat':215 'com.google.firebase':160,167 'com.google.gms.google':142,155 'come':640 'cometchat':2,6,26,32,39,52,117,126 'cometchat-android-v6-builder-settings':38 'cometchat-android-v6-core':25,116 'cometchat-android-v6-events':31 'cometchat-android-v6-push':1 'cometchat.callbacklistener':600,645,666 'cometchat.connect':643 'cometchat.disconnect':664 'cometchat.markasdelivered':365,734 'cometchat.rejectcall':594 'cometchatcallevent.callrejected':609 'cometchatconstants.call':596 'cometchatconstants.receiver':384 'cometchatevents.emitcallevent':608 'cometchatexcept':615,657,678 'cometchatincomingcal':531,562,711 'cometchatnotifications.registerpushtoken':259,750 'cometchatuikit.issdkinitialized':364,436,684 'cometchatvoip':203,762 'cometchatvoip.hasanswerphonecallspermission':451 'cometchatvoip.hasenabledphoneaccountforvoip':459 'cometchatvoip.hasmanageowncallspermission':449 'cometchatvoip.hasreadphonestatepermission':447 'cometchatvoip.init':505 'cometchatvoip.telecommanager':547 'cometchatvoipconnectionservic':204,571 'cometchatvoiperror':472 'cometchatvoiputil':209 'cometchatvoiputils.currentsessionid':545 'companion':23,286 'compon':124,127 'compos':217,780 'config':45 'connect':622,632,652,785 'control':266 'copi':232,278 'core':29,120 'critic':429 'currentopenchatid':396 'deep':21,100 'deep-link':20,99 'deliv':354 'depend':133,157 'directori':179 'disconnect':673 'drain':793 'e':614,656,677 'e.g':308 'els':392 'enabl':458 'end':536 'endcal':548 'equival':250 'error':471 'event':35,37 'everyth':261 'export':213 'fail':658,679 'fals':147 'fcm':12,58,76,129,573,690,763 'fcmcalldto':206,501 'fcmmessagedto':205,344,350,403 'fcmmessagedto.receiver':370,393 'fcmmessagedto.receivertype':369,383 'fcmmessagedto.sender':368,391 'fcmmessagedto.tag':366 'fcmmessagenotificationutil':210 'fcmmessagenotificationutils.shownotification':401 'fcmservic':189,207,284 'fcmtoken':289,301 'file':236 'firebas':55,162,169,182 'firebase-bom':161 'firebase-messag':168 'firebaseapp.initializeapp':186 'firebasemessagingservic':285 'flow':487 'foreground':86,528,550,559,642,708,713 'fromjson':346,497 'fun':295,311,339,417,464,469,490,590,602,612,648,654,669,675 'glue':264,771 'goe':661 'google-services.json':172 'gradl':132 'gson':345,347,496,498 'guid':245 'handl':16,59,70,82,336,414,439,532,583,712,732 'handlecallflow':466,491 'handlecallnotif':331,418 'handlechatnotif':328,340 'handler':558 'hard':680 'head':192 'heads-up':191 'id':141,154 'implement':98,158,166,190 'incom':83,514,556,701 'init/login':30 'initi':112,181,362,434,503,512,581,740 'integr':18,65,479 'intent':404 'isappinforeground':524 'isus':382,390 'jetpack':229,272,628,775 'key':406 'kill':572,697 'kit':252,755 'kotlin':134,183,218,282,337,415,488,588,637,781 'later':306 'let':607 'level':138,151 'lifecycl':636,789 'link':22,101 'listen':566 'live':223 'lowercas':324 'make':686 'manag':623,630,720,783 'mark':352 'master':227,270,626,773 'master-app-jetpack':226,269,625,772 'match':543 'may':578,694 'messag':57,81,170,313,316,329,332,341,410,419,467,492 'message.data':322,349,423,427,500 'message.data.isempty':318 'modul':176 'nativ':95,538 'navig':102 'never':698 'notif':11,15,50,63,78,85,104,335,372,400,405,413 'notificationcompat.category':409 'null':291 'object':287,461,599,644,665 'onerror':613,655,676 'onmessagereceiv':312 'onnewtoken':296 'onpermissionsdeni':470 'onpermissionsgr':465 'onsuccess':603,649,670 'overrid':294,310,463,468,601,611,647,653,668,674 'packagemanag':508 'part':777 'pattern':198,267 'permiss':445,482,727 'phone':455,718,725 'platform':159 'plugin':140,153 'privat':292,338,416,489,589 'project':137,239,281 'project-level':136 'public':255,758 'purpos':46 'push':5,10,49,77,256,746,759 'read':356,717 'receipt':357 'refer':197 'referenc':201 'registr':618,748 'reject':587 'rejectcallwithbusystatus':591 'rejectedcal':604,606 'relev':234 'remotemessag':314,342,420,493 'repli':407 'requir':481,716,735 'return':319,437,453 'root':178 'rout':553 'rule':681 'sampl':230,275,769 'sample-app':274,768 'sdk':114,360,432,442,564,577,687,737 'servic':143,156,691 'session':542 'sessionid':422,424 'set':43,47,89,293 'setup':13,130 'sharedprefer':309 'show':371,399,474,513,699 'shown':765 'skill':24,73,109 'skill-cometchat-android-v6-push' 'skip':741 'sourc':235 'source-cometchat' 'state':555,719 'status':597 'store':303 'string':290,298,646,651,667,672 'super.onmessagereceived':315 'super.onnewtoken':299 'surfac':257 'telecommanag':93,570 'three':730 'tojson':348,499 'token':297,300,302,304,747 'tolong':367 'topic-agent-skills' 'topic-ai-agent' 'topic-chat' 'topic-claude-code' 'topic-cometchat' 'topic-cursor' 'topic-messaging' 'topic-nextjs' 'topic-react' 'topic-react-native' 'topic-ui-kit' 'tostr':509 'type':321,323,326,385 'ui':97,123,476,516,540,703 'uid':388,395 'uikit':8,529,560,709 'unansw':535 'unregisterpushtoken':260,751 'use':54,71,107,115,125,241,307,749 'user':374,386 'v6':4,9,28,34,41,53,119 'val':320,343,381,387,421,425,494 'var':288 'version':144 'via':563,569 'voip':17,66,91,444,475,478,485,504,568,700,715 'voipincomingcal':525 'voippermissionlisten':208,462 'vs':551 'wake':574 'websocket':621,631,784 'without':441 'work':121 'write':247 'x':221 'x.x':165 'xml':483,619","prices":[{"id":"2c597a52-52ae-45e1-b4fd-b8ae4d319abb","listingId":"18fb88d7-cd1f-4400-9a43-5ccf5287f688","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"cometchat","category":"cometchat-skills","install_from":"skills.sh"},"createdAt":"2026-05-07T13:05:06.627Z"}],"sources":[{"listingId":"18fb88d7-cd1f-4400-9a43-5ccf5287f688","source":"github","sourceId":"cometchat/cometchat-skills/cometchat-android-v6-push","sourceUrl":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-android-v6-push","isPrimary":false,"firstSeenAt":"2026-05-07T13:05:06.627Z","lastSeenAt":"2026-05-18T19:04:47.357Z"}],"details":{"listingId":"18fb88d7-cd1f-4400-9a43-5ccf5287f688","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"cometchat","slug":"cometchat-android-v6-push","github":{"repo":"cometchat/cometchat-skills","stars":27,"topics":["agent-skills","ai-agent","chat","claude-code","cometchat","cursor","messaging","nextjs","react","react-native","ui-kit"],"license":null,"html_url":"https://github.com/cometchat/cometchat-skills","pushed_at":"2026-05-18T05:04:24Z","description":"Add CometChat chat to any React, Next.js, React Native, Angular, Android, iOS, or Flutter project through your AI coding agent. Works with Claude Code, Cursor, Codex, VS Code Copilot, Windsurf, Cline, Kiro, and 50+ more agents.","skill_md_sha":"04a4d9575d8ffc1e8b5e6b5c2f822ecc02f32699","skill_md_path":"skills/cometchat-android-v6-push/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/cometchat/cometchat-skills/tree/main/skills/cometchat-android-v6-push"},"layout":"multi","source":"github","category":"cometchat-skills","frontmatter":{"name":"cometchat-android-v6-push","license":"MIT","description":"CometChat Android UIKit v6 push notifications — FCM setup, chat/call notification handling, VoIP integration, and deep-linking","compatibility":"Android 9.0+ (API 28); Kotlin 1.9+; com.cometchat:chatuikit-compose-android:6.x / com.cometchat:chatuikit-kotlin-android:6.x"},"skills_sh_url":"https://skills.sh/cometchat/cometchat-skills/cometchat-android-v6-push"},"updatedAt":"2026-05-18T19:04:47.357Z"}}