@cometchat/skills 3.0.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +44 -17
  3. package/bin/install.js +389 -43
  4. package/package.json +19 -9
  5. package/skills/cometchat/SKILL.md +1066 -265
  6. package/skills/cometchat-android-v5/SKILL.md +120 -0
  7. package/skills/cometchat-android-v5-components/SKILL.md +430 -0
  8. package/skills/cometchat-android-v5-core/SKILL.md +468 -0
  9. package/skills/cometchat-android-v5-customization/SKILL.md +305 -0
  10. package/skills/cometchat-android-v5-extensions/SKILL.md +124 -0
  11. package/skills/cometchat-android-v5-features/SKILL.md +130 -0
  12. package/skills/cometchat-android-v5-placement/SKILL.md +276 -0
  13. package/skills/cometchat-android-v5-production/SKILL.md +94 -0
  14. package/skills/cometchat-android-v5-push/SKILL.md +620 -0
  15. package/skills/cometchat-android-v5-testing/SKILL.md +187 -0
  16. package/skills/cometchat-android-v5-theming/SKILL.md +166 -0
  17. package/skills/cometchat-android-v5-troubleshooting/SKILL.md +73 -0
  18. package/skills/cometchat-android-v6/SKILL.md +107 -0
  19. package/skills/cometchat-android-v6-builder-settings/SKILL.md +145 -0
  20. package/skills/cometchat-android-v6-compose-components/SKILL.md +202 -0
  21. package/skills/cometchat-android-v6-compose-customization/SKILL.md +280 -0
  22. package/skills/cometchat-android-v6-compose-placement/SKILL.md +213 -0
  23. package/skills/cometchat-android-v6-compose-theming/SKILL.md +224 -0
  24. package/skills/cometchat-android-v6-core/SKILL.md +354 -0
  25. package/skills/cometchat-android-v6-events/SKILL.md +212 -0
  26. package/skills/cometchat-android-v6-extensions/SKILL.md +191 -0
  27. package/skills/cometchat-android-v6-features/SKILL.md +130 -0
  28. package/skills/cometchat-android-v6-kotlin-components/SKILL.md +197 -0
  29. package/skills/cometchat-android-v6-kotlin-customization/SKILL.md +268 -0
  30. package/skills/cometchat-android-v6-kotlin-placement/SKILL.md +248 -0
  31. package/skills/cometchat-android-v6-kotlin-theming/SKILL.md +202 -0
  32. package/skills/cometchat-android-v6-production/SKILL.md +187 -0
  33. package/skills/cometchat-android-v6-push/SKILL.md +274 -0
  34. package/skills/cometchat-android-v6-testing/SKILL.md +279 -0
  35. package/skills/cometchat-android-v6-troubleshooting/SKILL.md +249 -0
  36. package/skills/cometchat-angular-components/SKILL.md +706 -0
  37. package/skills/cometchat-angular-core/SKILL.md +444 -0
  38. package/skills/cometchat-angular-customization/SKILL.md +424 -0
  39. package/skills/cometchat-angular-features/SKILL.md +447 -0
  40. package/skills/cometchat-angular-patterns/SKILL.md +447 -0
  41. package/skills/cometchat-angular-placement/SKILL.md +685 -0
  42. package/skills/cometchat-angular-production/SKILL.md +415 -0
  43. package/skills/cometchat-angular-theming/SKILL.md +365 -0
  44. package/skills/cometchat-angular-troubleshooting/SKILL.md +329 -0
  45. package/skills/cometchat-astro-patterns/SKILL.md +3 -3
  46. package/skills/cometchat-components/SKILL.md +11 -0
  47. package/skills/cometchat-core/SKILL.md +11 -6
  48. package/skills/cometchat-customization/references/component-catalog.md +1 -1
  49. package/skills/cometchat-features/SKILL.md +176 -108
  50. package/skills/cometchat-flutter-v5/SKILL.md +167 -0
  51. package/skills/cometchat-flutter-v5-calls/SKILL.md +192 -0
  52. package/skills/cometchat-flutter-v5-conversations/SKILL.md +117 -0
  53. package/skills/cometchat-flutter-v5-core/SKILL.md +221 -0
  54. package/skills/cometchat-flutter-v5-customization/SKILL.md +227 -0
  55. package/skills/cometchat-flutter-v5-events/SKILL.md +155 -0
  56. package/skills/cometchat-flutter-v5-messages/SKILL.md +258 -0
  57. package/skills/cometchat-flutter-v5-production/SKILL.md +207 -0
  58. package/skills/cometchat-flutter-v5-push/SKILL.md +284 -0
  59. package/skills/cometchat-flutter-v5-theming/SKILL.md +160 -0
  60. package/skills/cometchat-flutter-v5-troubleshooting/SKILL.md +129 -0
  61. package/skills/cometchat-flutter-v5-users-groups/SKILL.md +151 -0
  62. package/skills/cometchat-flutter-v6/SKILL.md +445 -0
  63. package/skills/cometchat-flutter-v6-calls/SKILL.md +333 -0
  64. package/skills/cometchat-flutter-v6-components/SKILL.md +365 -0
  65. package/skills/cometchat-flutter-v6-conversations/SKILL.md +210 -0
  66. package/skills/cometchat-flutter-v6-core/SKILL.md +305 -0
  67. package/skills/cometchat-flutter-v6-customization/SKILL.md +666 -0
  68. package/skills/cometchat-flutter-v6-events/SKILL.md +216 -0
  69. package/skills/cometchat-flutter-v6-features/SKILL.md +406 -0
  70. package/skills/cometchat-flutter-v6-messages/SKILL.md +333 -0
  71. package/skills/cometchat-flutter-v6-migration/SKILL.md +381 -0
  72. package/skills/cometchat-flutter-v6-placement/SKILL.md +398 -0
  73. package/skills/cometchat-flutter-v6-production/SKILL.md +632 -0
  74. package/skills/cometchat-flutter-v6-theming/SKILL.md +260 -0
  75. package/skills/cometchat-flutter-v6-troubleshooting/SKILL.md +726 -0
  76. package/skills/cometchat-flutter-v6-users-groups/SKILL.md +216 -0
  77. package/skills/cometchat-ios/SKILL.md +323 -0
  78. package/skills/cometchat-ios-components/SKILL.md +651 -0
  79. package/skills/cometchat-ios-core/SKILL.md +822 -0
  80. package/skills/cometchat-ios-customization/SKILL.md +825 -0
  81. package/skills/cometchat-ios-features/SKILL.md +725 -0
  82. package/skills/cometchat-ios-placement/SKILL.md +750 -0
  83. package/skills/cometchat-ios-production/SKILL.md +730 -0
  84. package/skills/cometchat-ios-push/SKILL.md +582 -0
  85. package/skills/cometchat-ios-theming/SKILL.md +631 -0
  86. package/skills/cometchat-ios-troubleshooting/SKILL.md +983 -0
  87. package/skills/cometchat-native-bare-patterns/SKILL.md +470 -0
  88. package/skills/cometchat-native-components/SKILL.md +594 -0
  89. package/skills/cometchat-native-core/SKILL.md +407 -0
  90. package/skills/cometchat-native-customization/SKILL.md +594 -0
  91. package/skills/cometchat-native-expo-patterns/SKILL.md +485 -0
  92. package/skills/cometchat-native-features/SKILL.md +608 -0
  93. package/skills/cometchat-native-placement/SKILL.md +500 -0
  94. package/skills/cometchat-native-production/SKILL.md +694 -0
  95. package/skills/cometchat-native-push/SKILL.md +593 -0
  96. package/skills/cometchat-native-testing/SKILL.md +583 -0
  97. package/skills/cometchat-native-theming/SKILL.md +656 -0
  98. package/skills/cometchat-native-troubleshooting/SKILL.md +433 -0
  99. package/skills/cometchat-nextjs-patterns/SKILL.md +5 -5
  100. package/skills/cometchat-production/SKILL.md +60 -0
  101. package/skills/cometchat-react-router-patterns/SKILL.md +23 -21
@@ -0,0 +1,620 @@
1
+ ---
2
+ name: cometchat-android-v5-push
3
+ description: "Push notifications for CometChat Android — FCM setup, CometChatNotifications API, token registration with PushPlatforms, foreground/background handling, notification channels, reply-from-notification, and tap-to-deep-link."
4
+ license: "MIT"
5
+ compatibility: "Android 7.0+; Java 8+; Kotlin 1.8+; com.cometchat:chat-uikit-android:5.x; com.google.firebase:firebase-messaging"
6
+ allowed-tools: "executeBash, readFile, fileSearch, listDirectory"
7
+ metadata:
8
+ author: "CometChat"
9
+ version: "3.0.0"
10
+ tags: "cometchat android push notifications fcm firebase deep-link CometChatNotifications PushPlatforms"
11
+ ---
12
+
13
+ > **Companion skills:** `cometchat-android-v5-core` covers init and login;
14
+ > `cometchat-android-v5-production` covers production auth and token security.
15
+
16
+ ## Purpose
17
+
18
+ Push notifications are non-negotiable for production chat. Without them, a backgrounded app never wakes when a message arrives. This skill covers end-to-end FCM setup for CometChat Android v5 — using the correct `CometChatNotifications` API with `PushPlatforms.FCM_ANDROID`, notification channels, foreground/background handling, reply-from-notification, and tap-to-deep-link.
19
+
20
+ **Ground truth:** `sample-app-java+push-notification/src/main/java/com/cometchat/sampleapp/java/fcm/` and `sample-app-kotlin+push-notification/src/main/java/com/cometchat/sampleapp/kotlin/fcm/` in the v5 UIKit repository.
21
+
22
+ ---
23
+
24
+ ## Use this skill when
25
+
26
+ - "Set up push notifications"
27
+ - "Messages don't arrive when app is backgrounded"
28
+ - "How do I handle notification taps?"
29
+ - "FCM token registration with CometChat"
30
+ - "How do I register push token?"
31
+ - "CometChatNotifications API"
32
+
33
+ ## Do not use this skill when
34
+
35
+ - Setting up init/login → use `cometchat-android-v5-core`
36
+ - Diagnosing non-push issues → use `cometchat-android-v5-troubleshooting`
37
+ - Setting up VoIP calls → see VoIP section in this skill, but call UI is in `cometchat-android-v5-features`
38
+
39
+ ---
40
+
41
+ ## 1. The moving pieces
42
+
43
+ ```
44
+ FCM (Google) → CometChat Dashboard → CometChat Server → Android Client
45
+ ```
46
+
47
+ When user A sends a message to user B:
48
+ 1. CometChat server receives the message
49
+ 2. Looks up B's registered push token (registered via `CometChatNotifications.registerPushToken()`)
50
+ 3. Sends push via FCM using the dashboard credentials
51
+ 4. B's device receives it via `FirebaseMessagingService.onMessageReceived()`
52
+ 5. App builds and displays a notification; tap → deep-link to conversation
53
+
54
+ All five steps must work. A broken step is almost always silent — no log, no error, just no notification.
55
+
56
+ ---
57
+
58
+ ## 2. FCM setup
59
+
60
+ ### 2a. Firebase project + google-services.json
61
+
62
+ 1. https://console.firebase.google.com → Add project
63
+ 2. Project Overview → Add app → Android → enter your `applicationId`
64
+ 3. Download `google-services.json` → place at `app/google-services.json`
65
+
66
+ ### 2b. Gradle dependencies
67
+
68
+ ```groovy
69
+ // project-level build.gradle
70
+ buildscript {
71
+ dependencies {
72
+ classpath 'com.google.gms:google-services:4.4.2'
73
+ }
74
+ }
75
+
76
+ // app-level build.gradle
77
+ apply plugin: 'com.google.gms.google-services'
78
+
79
+ dependencies {
80
+ implementation 'com.google.firebase:firebase-messaging:24.+'
81
+ }
82
+ ```
83
+
84
+ ### 2c. Service account JSON (for CometChat dashboard)
85
+
86
+ 1. Firebase Console → Project Settings → Service accounts
87
+ 2. Generate new private key → downloads a `.json` file
88
+ 3. You'll upload this to the CometChat dashboard in §3
89
+
90
+ ---
91
+
92
+ ## 3. CometChat dashboard — upload FCM credentials
93
+
94
+ 1. https://app.cometchat.com → your app → **Notifications** → **Push Notifications**
95
+ 2. **Add Provider** → choose **FCM**
96
+ 3. Upload the service account `.json` file from §2c
97
+ 4. Save → note the **Provider ID** (e.g., `"Android-CometChat-Team-Messenger"`)
98
+
99
+ You'll use this Provider ID in your client code when calling `CometChatNotifications.registerPushToken()`.
100
+
101
+ ---
102
+
103
+ ## 4. Token registration API — `CometChatNotifications`
104
+
105
+ The v5 SDK uses `CometChatNotifications.registerPushToken()` — **not** the deprecated `CometChat.registerTokenForPushNotification()`.
106
+
107
+ ### API reference
108
+
109
+ | Method | Signature | Description |
110
+ |---|---|---|
111
+ | `registerPushToken` | `CometChatNotifications.registerPushToken(String token, String platform, String providerId, CallbackListener<String>)` | Register FCM token with CometChat |
112
+ | `unregisterPushToken` | `CometChatNotifications.unregisterPushToken(CallbackListener<String>)` | Unregister token (call before logout) |
113
+
114
+ ### PushPlatforms constants
115
+
116
+ | Constant | Value | Use for |
117
+ |---|---|---|
118
+ | `PushPlatforms.FCM_ANDROID` | `"fcm_android"` | Android FCM push |
119
+
120
+ ### Register token after login
121
+
122
+ **Java:**
123
+ ```java
124
+ public static void registerFCMToken(CometChat.CallbackListener<String> listener) {
125
+ FirebaseMessaging.getInstance().getToken().addOnCompleteListener(task -> {
126
+ if (task.isSuccessful()) {
127
+ String pushToken = task.getResult();
128
+ CometChatNotifications.registerPushToken(
129
+ pushToken,
130
+ PushPlatforms.FCM_ANDROID,
131
+ "YOUR_PROVIDER_ID", // from CometChat dashboard §3
132
+ new CometChat.CallbackListener<String>() {
133
+ @Override
134
+ public void onSuccess(String s) {
135
+ listener.onSuccess(s);
136
+ }
137
+
138
+ @Override
139
+ public void onError(CometChatException e) {
140
+ listener.onError(e);
141
+ }
142
+ }
143
+ );
144
+ } else {
145
+ listener.onError(new CometChatException("ERROR", "Failed to get FCM token"));
146
+ }
147
+ });
148
+ }
149
+ ```
150
+
151
+ **Kotlin:**
152
+ ```kotlin
153
+ fun registerFCMToken(listener: CometChat.CallbackListener<String>) {
154
+ FirebaseMessaging.getInstance().token.addOnCompleteListener { task ->
155
+ if (task.isSuccessful) {
156
+ val pushToken = task.result
157
+ CometChatNotifications.registerPushToken(
158
+ pushToken,
159
+ PushPlatforms.FCM_ANDROID,
160
+ "YOUR_PROVIDER_ID", // from CometChat dashboard §3
161
+ object : CometChat.CallbackListener<String?>() {
162
+ override fun onSuccess(s: String?) {
163
+ listener.onSuccess(s)
164
+ }
165
+
166
+ override fun onError(e: CometChatException) {
167
+ listener.onError(e)
168
+ }
169
+ }
170
+ )
171
+ } else {
172
+ listener.onError(CometChatException("ERROR", "Failed to get FCM token"))
173
+ }
174
+ }
175
+ }
176
+ ```
177
+
178
+ ### Unregister before logout
179
+
180
+ **Java:**
181
+ ```java
182
+ public static void unregisterFCMToken(CometChat.CallbackListener<String> listener) {
183
+ CometChatNotifications.unregisterPushToken(new CometChat.CallbackListener<String>() {
184
+ @Override
185
+ public void onSuccess(String s) {
186
+ listener.onSuccess(s);
187
+ }
188
+
189
+ @Override
190
+ public void onError(CometChatException e) {
191
+ listener.onError(e);
192
+ }
193
+ });
194
+ }
195
+ ```
196
+
197
+ **Kotlin:**
198
+ ```kotlin
199
+ fun unregisterFCMToken(listener: CometChat.CallbackListener<String>) {
200
+ CometChatNotifications.unregisterPushToken(object : CometChat.CallbackListener<String?>() {
201
+ override fun onSuccess(s: String?) {
202
+ listener.onSuccess(s)
203
+ }
204
+
205
+ override fun onError(e: CometChatException) {
206
+ listener.onError(e)
207
+ }
208
+ })
209
+ }
210
+ ```
211
+
212
+ ---
213
+
214
+ ## 5. FirebaseMessagingService implementation
215
+
216
+ ### 5a. Service class
217
+
218
+ **Java:**
219
+ ```java
220
+ public class FCMService extends FirebaseMessagingService {
221
+ private static String fcmToken;
222
+
223
+ @Override
224
+ public void onNewToken(@NonNull String token) {
225
+ super.onNewToken(token);
226
+ fcmToken = token;
227
+ // Re-register if user is already logged in
228
+ // (token rotation can happen at any time)
229
+ }
230
+
231
+ @Override
232
+ public void onMessageReceived(@NonNull RemoteMessage message) {
233
+ super.onMessageReceived(message);
234
+ if (message.getData().isEmpty()) return;
235
+
236
+ String type = message.getData().get("type");
237
+ if ("chat".equalsIgnoreCase(type)) {
238
+ handleChatMessage(message);
239
+ } else if ("call".equalsIgnoreCase(type)) {
240
+ handleCallMessage(message);
241
+ }
242
+ }
243
+
244
+ private void handleChatMessage(RemoteMessage message) {
245
+ FCMMessageDTO dto = new Gson().fromJson(
246
+ new Gson().toJson(message.getData()), FCMMessageDTO.class);
247
+
248
+ // Mark as delivered
249
+ CometChat.markAsDelivered(
250
+ Long.parseLong(dto.getTag()),
251
+ dto.getSender(),
252
+ dto.getReceiverType(),
253
+ dto.getReceiver()
254
+ );
255
+
256
+ // Build and show notification
257
+ Intent clickIntent = new Intent(this, SplashActivity.class);
258
+ FCMMessageNotificationUtils.showNotification(
259
+ this, dto, clickIntent,
260
+ "Reply", NotificationCompat.CATEGORY_MESSAGE
261
+ );
262
+ }
263
+ }
264
+ ```
265
+
266
+ **Kotlin:**
267
+ ```kotlin
268
+ class FCMService : FirebaseMessagingService() {
269
+ companion object {
270
+ var fcmToken: String? = null
271
+ private set
272
+ }
273
+
274
+ override fun onNewToken(token: String) {
275
+ super.onNewToken(token)
276
+ fcmToken = token
277
+ }
278
+
279
+ override fun onMessageReceived(message: RemoteMessage) {
280
+ super.onMessageReceived(message)
281
+ if (message.data.isEmpty()) return
282
+
283
+ when (message.data["type"]?.lowercase()) {
284
+ "chat" -> handleChatMessage(message)
285
+ "call" -> handleCallMessage(message)
286
+ }
287
+ }
288
+
289
+ private fun handleChatMessage(message: RemoteMessage) {
290
+ val dto = Gson().fromJson(
291
+ Gson().toJson(message.data), FCMMessageDTO::class.java)
292
+
293
+ CometChat.markAsDelivered(
294
+ dto.tag!!.toLong(),
295
+ dto.sender!!,
296
+ dto.receiverType!!,
297
+ dto.receiver!!
298
+ )
299
+
300
+ val clickIntent = Intent(this, SplashActivity::class.java)
301
+ FCMMessageNotificationUtils.showNotification(
302
+ this, dto, clickIntent,
303
+ "Reply", NotificationCompat.CATEGORY_MESSAGE
304
+ )
305
+ }
306
+ }
307
+ ```
308
+
309
+ ### 5b. Register in AndroidManifest.xml
310
+
311
+ ```xml
312
+ <service
313
+ android:name=".fcm.FCMService"
314
+ android:exported="false">
315
+ <intent-filter>
316
+ <action android:name="com.google.firebase.MESSAGING_EVENT" />
317
+ </intent-filter>
318
+ </service>
319
+ ```
320
+
321
+ ---
322
+
323
+ ## 6. Push payload schema
324
+
325
+ CometChat sends data-only FCM messages. The payload fields:
326
+
327
+ ### Chat message payload (`type: "chat"`)
328
+
329
+ | Field | Type | Description |
330
+ |---|---|---|
331
+ | `type` | `String` | Always `"chat"` for messages |
332
+ | `sender` | `String` | Sender UID |
333
+ | `senderName` | `String` | Sender display name |
334
+ | `senderAvatar` | `String` | Sender avatar URL |
335
+ | `receiver` | `String` | Receiver UID (user) or GUID (group) |
336
+ | `receiverName` | `String` | Receiver display name |
337
+ | `receiverType` | `String` | `"user"` or `"group"` |
338
+ | `receiverAvatar` | `String` | Receiver avatar URL |
339
+ | `conversationId` | `String` | Conversation ID |
340
+ | `body` | `String` | Message text |
341
+ | `title` | `String` | Notification title |
342
+ | `tag` | `String` | Message ID (as string) |
343
+ | `unreadMessageCount` | `String` | Unread count (as string) |
344
+
345
+ ### Call payload (`type: "call"`)
346
+
347
+ | Field | Type | Description |
348
+ |---|---|---|
349
+ | `type` | `String` | Always `"call"` |
350
+ | `callAction` | `String` | `"initiated"`, `"cancelled"`, `"unanswered"` |
351
+ | `sessionId` | `String` | Call session ID |
352
+ | `callType` | `String` | `"audio"` or `"video"` |
353
+ | `sender` / `receiver` / `senderName` / etc. | `String` | Same as chat payload |
354
+
355
+ ### DTO classes
356
+
357
+ **Java:**
358
+ ```java
359
+ public class FCMMessageDTO {
360
+ @SerializedName("conversationId") private String conversationId;
361
+ @SerializedName("sender") private String sender;
362
+ @SerializedName("receiver") private String receiver;
363
+ @SerializedName("receiverName") private String receiverName;
364
+ @SerializedName("receiverType") private String receiverType;
365
+ @SerializedName("receiverAvatar") private String receiverAvatar;
366
+ @SerializedName("tag") private String tag;
367
+ @SerializedName("body") private String text;
368
+ @SerializedName("type") private String type;
369
+ @SerializedName("title") private String title;
370
+ @SerializedName("senderAvatar") private String senderAvatar;
371
+ @SerializedName("senderName") private String senderName;
372
+ @SerializedName("unreadMessageCount") private String unreadMessageCount;
373
+ // getters and setters
374
+ }
375
+ ```
376
+
377
+ **Kotlin:**
378
+ ```kotlin
379
+ class FCMMessageDTO {
380
+ @SerializedName("conversationId") var conversationId: String? = null
381
+ @SerializedName("sender") var sender: String? = null
382
+ @SerializedName("receiver") var receiver: String? = null
383
+ @SerializedName("receiverName") var receiverName: String? = null
384
+ @SerializedName("receiverType") var receiverType: String? = null
385
+ @SerializedName("receiverAvatar") var receiverAvatar: String? = null
386
+ @SerializedName("tag") var tag: String? = null
387
+ @SerializedName("body") var text: String? = null
388
+ @SerializedName("type") var type: String? = null
389
+ @SerializedName("title") var title: String? = null
390
+ @SerializedName("senderAvatar") var senderAvatar: String? = null
391
+ @SerializedName("senderName") var senderName: String? = null
392
+ @SerializedName("unreadMessageCount") var unreadMessageCount: String? = null
393
+ }
394
+ ```
395
+
396
+ Parse from `RemoteMessage`:
397
+ ```java
398
+ FCMMessageDTO dto = new Gson().fromJson(new Gson().toJson(message.getData()), FCMMessageDTO.class);
399
+ ```
400
+
401
+ ---
402
+
403
+ ## 7. Notification channels (API 26+)
404
+
405
+ Create channels in your `Application.onCreate()` or before showing the first notification:
406
+
407
+ **Java:**
408
+ ```java
409
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
410
+ NotificationManager manager = getSystemService(NotificationManager.class);
411
+
412
+ NotificationChannel messageChannel = new NotificationChannel(
413
+ "Message", // channel ID
414
+ "Message notification", // channel name
415
+ NotificationManager.IMPORTANCE_HIGH
416
+ );
417
+ manager.createNotificationChannel(messageChannel);
418
+
419
+ NotificationChannel callChannel = new NotificationChannel(
420
+ "Call",
421
+ "Call notification",
422
+ NotificationManager.IMPORTANCE_HIGH
423
+ );
424
+ callChannel.setSound(null, null); // calls use their own ringtone
425
+ manager.createNotificationChannel(callChannel);
426
+ }
427
+ ```
428
+
429
+ **Kotlin:**
430
+ ```kotlin
431
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
432
+ val manager = getSystemService(NotificationManager::class.java)
433
+
434
+ val messageChannel = NotificationChannel(
435
+ "Message", "Message notification", NotificationManager.IMPORTANCE_HIGH
436
+ )
437
+ manager.createNotificationChannel(messageChannel)
438
+
439
+ val callChannel = NotificationChannel(
440
+ "Call", "Call notification", NotificationManager.IMPORTANCE_HIGH
441
+ ).apply { setSound(null, null) }
442
+ manager.createNotificationChannel(callChannel)
443
+ }
444
+ ```
445
+
446
+ ---
447
+
448
+ ## 8. Tap-to-deep-link
449
+
450
+ The notification click intent routes through `SplashActivity` → `HomeActivity` → correct fragment/conversation.
451
+
452
+ ### Setting up the click intent
453
+
454
+ **Java:**
455
+ ```java
456
+ Intent clickIntent = new Intent(context, SplashActivity.class);
457
+ clickIntent.putExtra("NOTIFICATION_TYPE", "NOTIFICATION_TYPE_MESSAGE");
458
+ clickIntent.putExtra("NOTIFICATION_DATA", new Gson().toJson(fcmMessageDTO));
459
+ clickIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
460
+
461
+ PendingIntent pendingIntent = PendingIntent.getActivity(
462
+ context, notificationId, clickIntent,
463
+ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
464
+ );
465
+ ```
466
+
467
+ ### Handling in SplashActivity → HomeActivity
468
+
469
+ ```java
470
+ // In HomeActivity.onCreate() or onNewIntent()
471
+ String notificationType = getIntent().getStringExtra("NOTIFICATION_TYPE");
472
+ String notificationPayload = getIntent().getStringExtra("NOTIFICATION_DATA");
473
+
474
+ if ("NOTIFICATION_TYPE_MESSAGE".equals(notificationType) && notificationPayload != null) {
475
+ FCMMessageDTO dto = new Gson().fromJson(notificationPayload, FCMMessageDTO.class);
476
+ if ("chat".equalsIgnoreCase(dto.getType())) {
477
+ // Navigate to the conversation
478
+ boolean isUser = "user".equals(dto.getReceiverType());
479
+ String uid = isUser ? dto.getSender() : dto.getReceiver();
480
+ // Open MessagesActivity with uid/guid
481
+ }
482
+ }
483
+ ```
484
+
485
+ ---
486
+
487
+ ## 9. Reply from notification
488
+
489
+ The sample apps support inline reply from the notification tray using `RemoteInput`:
490
+
491
+ **Java:**
492
+ ```java
493
+ RemoteInput remoteInput = new RemoteInput.Builder("key_text_reply")
494
+ .setLabel("Reply")
495
+ .build();
496
+
497
+ NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
498
+ R.drawable.ic_reply, "Reply", replyPendingIntent)
499
+ .addRemoteInput(remoteInput)
500
+ .build();
501
+
502
+ builder.addAction(replyAction);
503
+ ```
504
+
505
+ The reply is received in a `BroadcastReceiver`:
506
+
507
+ ```java
508
+ public class FCMMessageBroadcastReceiver extends BroadcastReceiver {
509
+ @Override
510
+ public void onReceive(Context context, Intent intent) {
511
+ Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
512
+ if (remoteInput != null) {
513
+ CharSequence replyText = remoteInput.getCharSequence("key_text_reply");
514
+ // Send as TextMessage via CometChat.sendMessage()
515
+ }
516
+ }
517
+ }
518
+ ```
519
+
520
+ ---
521
+
522
+ ## 10. Permissions
523
+
524
+ ```xml
525
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
526
+ <uses-permission android:name="android.permission.INTERNET" />
527
+ ```
528
+
529
+ Request `POST_NOTIFICATIONS` at runtime on Android 13+:
530
+
531
+ **Java:**
532
+ ```java
533
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
534
+ if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
535
+ requestPermissions(new String[]{Manifest.permission.POST_NOTIFICATIONS}, 100);
536
+ }
537
+ }
538
+ ```
539
+
540
+ **Kotlin:**
541
+ ```kotlin
542
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
543
+ if (checkSelfPermission(Manifest.permission.POST_NOTIFICATIONS) != PackageManager.PERMISSION_GRANTED) {
544
+ requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), 100)
545
+ }
546
+ }
547
+ ```
548
+
549
+ ---
550
+
551
+ ## 11. Badge count
552
+
553
+ CometChat sends `unreadMessageCount` in the payload. Apply it:
554
+
555
+ ```java
556
+ String unreadCountStr = message.getData().get("unreadMessageCount");
557
+ if (unreadCountStr != null) {
558
+ int count = Integer.parseInt(unreadCountStr);
559
+ ShortcutBadger.applyCount(getApplicationContext(), count);
560
+ }
561
+ ```
562
+
563
+ Clear on app open:
564
+ ```java
565
+ ShortcutBadger.removeCount(this);
566
+ ```
567
+
568
+ ---
569
+
570
+ ## 12. Testing the push pipeline
571
+
572
+ | Step | How | What it verifies |
573
+ |---|---|---|
574
+ | 1. FCM alone | Firebase Console → Cloud Messaging → Send test message to your FCM token | Firebase + `google-services.json` are correct |
575
+ | 2. CometChat → device | Send a message to the logged-in user from another user (dashboard or another device) | Dashboard provider config + token registration |
576
+ | 3. Tap deep-link | Background the app, send a message, tap the notification | `PendingIntent` routing to correct conversation |
577
+ | 4. Reply from notification | Pull down notification, type reply, send | `BroadcastReceiver` + `CometChat.sendMessage()` |
578
+ | 5. Token rotation | Clear app data, re-open → `onNewToken()` fires | `onNewToken()` re-registers with CometChat |
579
+
580
+ ---
581
+
582
+ ## 13. Troubleshooting — common silent failures
583
+
584
+ | Symptom | Likely cause | Fix |
585
+ |---|---|---|
586
+ | Token prints but no push arrives | Token registered BEFORE login, or wrong Provider ID | Call `registerPushToken()` AFTER `CometChatUIKit.login()` resolves. Verify Provider ID matches dashboard. |
587
+ | Foreground: nothing shows | No `onMessageReceived()` implementation or no notification channel | Implement `FCMService.onMessageReceived()` + create `NotificationChannel` on API 26+ |
588
+ | "Default FirebaseApp is not initialized" | `google-services.json` missing or Gradle plugin not applied | Re-check §2. Clean build: `./gradlew clean` |
589
+ | Notification tap doesn't navigate | `PendingIntent` missing extras or wrong Activity | Pass `NOTIFICATION_TYPE` + `NOTIFICATION_DATA` in Intent extras, handle in target Activity |
590
+ | Works for User A but not User B after logout | `unregisterPushToken` not called on logout | Call `CometChatNotifications.unregisterPushToken()` BEFORE `CometChatUIKit.logout()` |
591
+ | No notifications on Android 13+ | Missing `POST_NOTIFICATIONS` runtime permission | Request at runtime before registering token |
592
+ | Using deprecated `registerTokenForPushNotification()` | Old API from v4 | Use `CometChatNotifications.registerPushToken()` with `PushPlatforms.FCM_ANDROID` |
593
+ | Provider ID mismatch | Client uses different Provider ID than dashboard | Copy exact Provider ID string from CometChat dashboard → Notifications → Push |
594
+
595
+ ---
596
+
597
+ ## 14. VoIP call push (advanced)
598
+
599
+ Call pushes (`type: "call"`) require VoIP permissions and `CometChatVoIP` integration. The flow:
600
+
601
+ 1. `onMessageReceived()` detects `type == "call"`
602
+ 2. Check `callAction`: `"initiated"` → show incoming call, `"cancelled"` / `"unanswered"` → dismiss
603
+ 3. Verify VoIP permissions: `CometChatVoIP.hasReadPhoneStatePermission()`, `hasManageOwnCallsPermission()`, `hasAnswerPhoneCallsPermission()`
604
+ 4. If granted: `CometChatVoIP.addNewIncomingCall()` with call details in a `Bundle`
605
+
606
+ This is an advanced topic — see the `voip/` package in the sample apps for the full implementation.
607
+
608
+ ---
609
+
610
+ ## Hard rules
611
+
612
+ - **Use `CometChatNotifications.registerPushToken()` — NOT the deprecated `CometChat.registerTokenForPushNotification()`.** The v5 API requires `PushPlatforms.FCM_ANDROID` and a Provider ID.
613
+ - **Register AFTER login.** The SDK needs a logged-in user to scope the token.
614
+ - **Unregister BEFORE logout.** Call `CometChatNotifications.unregisterPushToken()` before `CometChatUIKit.logout()`.
615
+ - **Handle `onNewToken()`.** FCM rotates tokens — missing the rotation means push stops working for some users.
616
+ - **Create notification channels on API 26+.** Without a channel, notifications are silently dropped.
617
+ - **Provider ID must match the dashboard.** Copy the exact string from CometChat dashboard → Notifications → Push Notifications.
618
+ - **Call `CometChat.markAsDelivered()` in `onMessageReceived()`.** This updates delivery receipts even when the app is backgrounded.
619
+ - **Test on a real device.** Emulator FCM behavior differs from real devices.
620
+ - **Don't suppress `onMessageReceived()` for foreground messages.** CometChat sends data-only pushes — the OS does NOT auto-display them. You must build the notification yourself.