cometchat-android-v6-push — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited cometchat-android-v6-push (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
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)
Set up push notifications for CometChat v6 using Firebase Cloud Messaging (FCM), handle chat and call notifications, and integrate VoIP for background call handling.
cometchat-android-v6-core)cometchat-*-components)// 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.
// In Application.onCreate()
FirebaseApp.initializeApp(this)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).
Heads-up — this is a reference pattern. The classes referenced below (CometChatVoIP,CometChatVoIPConnectionService,FCMMessageDTO,FCMCallDto,FCMService,VoIPPermissionListener,CometChatVoIPUtils,FCMMessageNotificationUtils) are NOT exported fromcom.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 isCometChatNotifications.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)
}
}
}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
)
}
}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
}
})
}<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" />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()
}
}
}
}| 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 |
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) { }
}
)
}<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>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 */ }
})CometChatUIKit.isSDKInitialized() before making SDK calls in FCM service — the app may have been killedCometChatIncomingCall handles foreground callsCometChat.markAsDelivered() requires the SDK to be initialized — skip it if notCometChatNotifications.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}~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.