capacitor-messenger-notifications 1.0.0 → 1.0.1

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 (40) hide show
  1. package/MessengerNotifications.podspec +2 -1
  2. package/Package.swift +4 -2
  3. package/README.md +324 -59
  4. package/android/build.gradle +12 -2
  5. package/android/src/main/AndroidManifest.xml +19 -0
  6. package/android/src/main/java/com/codecraft_studio/messenger/notifications/EncryptedMessageNotifier.java +549 -41
  7. package/android/src/main/java/com/codecraft_studio/messenger/notifications/FcmFetchBackgroundService.java +32 -0
  8. package/android/src/main/java/com/codecraft_studio/messenger/notifications/FcmFetchForegroundService.java +182 -0
  9. package/android/src/main/java/com/codecraft_studio/messenger/notifications/FcmFetchManager.java +203 -2
  10. package/android/src/main/java/com/codecraft_studio/messenger/notifications/FcmJobService.java +70 -0
  11. package/android/src/main/java/com/codecraft_studio/messenger/notifications/FcmTokenRegistrar.java +152 -0
  12. package/android/src/main/java/com/codecraft_studio/messenger/notifications/GmsHelper.java +19 -0
  13. package/android/src/main/java/com/codecraft_studio/messenger/notifications/MessengerNotificationsPlugin.java +196 -15
  14. package/android/src/main/java/com/codecraft_studio/messenger/notifications/NativeCrypto.java +82 -29
  15. package/android/src/main/java/com/codecraft_studio/messenger/notifications/NotificationDismissReceiver.java +4 -5
  16. package/android/src/main/java/com/codecraft_studio/messenger/notifications/NotificationHelper.java +348 -127
  17. package/android/src/main/java/com/codecraft_studio/messenger/notifications/PersistentSocketService.java +132 -52
  18. package/android/src/main/java/com/codecraft_studio/messenger/notifications/TemporarySocketSessionManager.java +297 -0
  19. package/android/src/main/java/com/codecraft_studio/messenger/notifications/UnreadMessagesFetcher.java +180 -0
  20. package/android/src/main/res/drawable/ic_notification.png +0 -0
  21. package/android/src/main/res/drawable/ic_transparent.png +0 -0
  22. package/android/src/main/res/values/strings.xml +4 -0
  23. package/dist/esm/definitions.d.ts +6 -0
  24. package/dist/esm/definitions.js.map +1 -1
  25. package/dist/esm/web.d.ts +3 -0
  26. package/dist/esm/web.js +3 -0
  27. package/dist/esm/web.js.map +1 -1
  28. package/dist/plugin.cjs.js +3 -0
  29. package/dist/plugin.cjs.js.map +1 -1
  30. package/dist/plugin.js +3 -0
  31. package/dist/plugin.js.map +1 -1
  32. package/ios/Sources/MessengerNotificationsPlugin/EncryptedMessageNotifier.swift +271 -0
  33. package/ios/Sources/MessengerNotificationsPlugin/FcmTokenRegistrar.swift +94 -0
  34. package/ios/Sources/MessengerNotificationsPlugin/MessengerNotificationsPlugin.swift +41 -9
  35. package/ios/Sources/MessengerNotificationsPlugin/NativeCrypto.swift +70 -12
  36. package/ios/Sources/MessengerNotificationsPlugin/NotificationHelper.swift +313 -41
  37. package/ios/Sources/MessengerNotificationsPlugin/SafeStorageStore.swift +1 -1
  38. package/ios/Sources/MessengerNotificationsPlugin/TemporarySocketSessionManager.swift +292 -36
  39. package/ios/Sources/MessengerNotificationsPlugin/UnreadMessagesFetcher.swift +344 -0
  40. package/package.json +1 -1
@@ -13,6 +13,7 @@ Pod::Spec.new do |s|
13
13
  s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
14
14
  s.ios.deployment_target = '13.0'
15
15
  s.dependency 'Capacitor'
16
- s.dependency 'Socket.io-client-swift', '~> 15.0'
16
+ s.dependency 'Socket.io-client-swift', '~> 16.0'
17
+ s.dependency 'Sodium', '~> 0.9'
17
18
  s.swift_version = '5.1'
18
19
  end
package/Package.swift CHANGED
@@ -11,7 +11,8 @@ let package = Package(
11
11
  ],
12
12
  dependencies: [
13
13
  .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.1.0"),
14
- .package(url: "https://github.com/socketio/socket.io-client-swift.git", from: "16.1.0")
14
+ .package(url: "https://github.com/socketio/socket.io-client-swift.git", from: "16.1.0"),
15
+ .package(url: "https://github.com/jedisct1/swift-sodium.git", from: "0.9.1")
15
16
  ],
16
17
  targets: [
17
18
  .target(
@@ -19,7 +20,8 @@ let package = Package(
19
20
  dependencies: [
20
21
  .product(name: "Capacitor", package: "capacitor-swift-pm"),
21
22
  .product(name: "Cordova", package: "capacitor-swift-pm"),
22
- .product(name: "SocketIO", package: "socket.io-client-swift")
23
+ .product(name: "SocketIO", package: "socket.io-client-swift"),
24
+ .product(name: "Sodium", package: "swift-sodium")
23
25
  ],
24
26
  path: "ios/Sources/MessengerNotificationsPlugin")
25
27
  ]
package/README.md CHANGED
@@ -10,23 +10,41 @@ Capacitor plugin for managing messenger-style notifications with WebSocket suppo
10
10
  - [Requirements](#requirements)
11
11
  - [Install](#install)
12
12
  - [Usage](#usage)
13
- - [JavaScript Examples](#javascript-examples)
14
- - [Native Integration (Android/iOS)](#native-integration-androidios)
15
- - [Configuration](#configuration)
16
- - [Platform Implementation](#platform-implementation)
13
+ - [Permissions](#permissions)
14
+ - [Show a Notification](#show-a-notification)
15
+ - [Clear a Room's Notifications](#clear-a-rooms-notifications)
16
+ - [Cold-Start Navigation](#cold-start-navigation)
17
+ - [Persistent Socket (Android)](#persistent-socket-android)
18
+ - [FCM Token Registration](#fcm-token-registration)
19
+ - [Native Integration](#native-integration)
17
20
  - [Setup by Platform](#setup-by-platform)
21
+ - [Android](#android)
22
+ - [iOS](#ios)
23
+ - [How It Works](#how-it-works)
18
24
  - [API](#api)
19
25
  - [Troubleshooting](#troubleshooting)
20
26
  - [Development](#development)
21
27
  - [Contributing](#contributing)
22
28
  - [License](#license)
23
29
 
30
+ ---
31
+
24
32
  ## Features
25
33
 
26
- - **Native Notification Grouping**: Automatically groups messages by room ID using `MessagingStyle` (Android) and `threadIdentifier` (iOS).
27
- - **Persistent WebSocket (Android)**: Foreground service that stays alive to receive messages even when the app is killed.
28
- - **Background Fetch (iOS)**: Automatically wakes up to fetch unread messages when a silent push is received.
29
- - **End-to-End Encryption Ready**: Designed to handle encrypted payloads and decrypt them on-device before showing notifications.
34
+ - **Native Notification Grouping**: Groups messages per room using `MessagingStyle` (Android) and `threadIdentifier` (iOS), keyed dynamically off the host app's package/bundle ID.
35
+ - **End-to-End Decryption**: Decrypts room names, usernames, and message bodies on-device using Libsodium (Android) and swift-sodium (iOS) before showing any notification.
36
+ - **Persistent WebSocket (Android)**: A foreground service maintains a live Socket.IO connection for devices without Google Play Services (GMS), auto-reconnecting on token or network changes.
37
+ - **FCM Background Fetch (Android)**: On GMS devices, `FcmFetchForegroundService` + `FcmFetchBackgroundService` spin up a short-lived socket session triggered by each FCM push and tear it down cleanly.
38
+ - **Job Scheduler Retry (Android)**: `FcmJobService` schedules a retry via `JobScheduler` if the primary fetch fails, ensuring no message is dropped.
39
+ - **Background Fetch (iOS)**: `TemporarySocketSessionManager` opens a short-lived Socket.IO session inside `didReceiveRemoteNotification` with idle and max-session timeouts.
40
+ - **HTTP Unread Fallback**: Both platforms fall back to fetching from the `/api/rooms/messages/unread` endpoint when a socket session cannot decrypt a message.
41
+ - **Rich Notifications**: Custom SVG avatar rendering (Android via AndroidSVG), conversation shortcuts, and dismissal tracking.
42
+ - **Notification Deduplication**: In-memory + persisted message ID tracking prevents showing the same message twice across socket and FCM paths.
43
+ - **Dynamic Resources**: All icon, app name, and notification group key lookups are resolved from the host app at runtime — no hardcoded app-specific values.
44
+ - **`window.Notification` Polyfill (Android)**: Injects a polyfill into the WebView so the JS app's `new Notification(...)` calls route through the native plugin.
45
+ - **FCM Token Registration**: Registers the device's FCM push token with your backend API automatically after login.
46
+
47
+ ---
30
48
 
31
49
  ## Requirements
32
50
 
@@ -35,6 +53,8 @@ Capacitor plugin for managing messenger-style notifications with WebSocket suppo
35
53
  - **iOS**: 13.0 or higher
36
54
  - **Android**: API level 22 (Android 5.1) or higher
37
55
 
56
+ ---
57
+
38
58
  ## Install
39
59
 
40
60
  ```bash
@@ -42,111 +62,356 @@ npm install capacitor-messenger-notifications
42
62
  npx cap sync
43
63
  ```
44
64
 
65
+ ---
66
+
45
67
  ## Usage
46
68
 
47
- ### JavaScript Examples
69
+ ### Permissions
48
70
 
49
- #### Start Persistent Socket (Android)
71
+ Before showing notifications, check and request permission:
50
72
 
51
73
  ```typescript
52
74
  import { MessengerNotifications } from 'capacitor-messenger-notifications';
53
75
 
54
- // Starts a foreground service on Android to maintain a heartbeat connection
55
- await MessengerNotifications.startPersistentSocket({
56
- url: 'wss://your-chat-server.com',
57
- token: 'YOUR_AUTH_TOKEN'
58
- });
76
+ const status = await MessengerNotifications.checkPermissions();
77
+
78
+ if (status.notifications !== 'granted') {
79
+ const result = await MessengerNotifications.requestPermissions();
80
+ if (result.notifications !== 'granted') {
81
+ console.warn('Notification permission denied');
82
+ }
83
+ }
59
84
  ```
60
85
 
61
- #### Show a Manual Notification
86
+ ### Show a Notification
62
87
 
63
88
  ```typescript
64
89
  await MessengerNotifications.showNotification({
65
- title: 'John Doe',
90
+ title: 'Alice',
66
91
  body: 'Hey, how are you?',
67
92
  roomId: 101,
68
93
  roomName: 'General Chat',
69
94
  messageId: 'uuid-12345',
70
- timestamp: Date.now()
95
+ timestamp: Date.now(),
96
+ senderId: 42,
97
+ avatarSvg: '<svg>...</svg>', // optional SVG string for sender avatar
71
98
  });
72
99
  ```
73
100
 
74
- ### Native Integration (Android/iOS)
75
-
76
- This plugin is often triggered from native background tasks (FCM / Silent Push).
101
+ ### Clear a Room's Notifications
77
102
 
78
- - **Android**: Use `EncryptedMessageNotifier.notifyFromSocketPayload(context, data)` from your background services.
79
- - **iOS**: Use `TemporarySocketSessionManager.shared.fetchAndNotify(...)` inside `didReceiveRemoteNotification`.
103
+ ```typescript
104
+ await MessengerNotifications.clearRoomNotification({ roomId: 101 });
105
+ ```
80
106
 
81
- ## Configuration
107
+ ### Cold-Start Navigation
82
108
 
83
- Most configurations are handled dynamically via the API, but you can define defaults in **`capacitor.config.json`**:
109
+ When the user taps a notification to open the app, retrieve the room to navigate to:
84
110
 
85
- ```json
86
- {
87
- "plugins": {
88
- "MessengerNotifications": {
89
- "defaultSocketUrl": "wss://your-default-server.com",
90
- "notificationChannelId": "chat_messages",
91
- "notificationChannelName": "Messenger Notifications"
92
- }
93
- }
111
+ ```typescript
112
+ const { roomId } = await MessengerNotifications.getPendingRoomId();
113
+ if (roomId !== null) {
114
+ router.push(`/room/${roomId}`);
94
115
  }
95
116
  ```
96
117
 
97
- ## Platform Implementation
118
+ ### Persistent Socket (Android)
98
119
 
99
- | Platform | Implementation |
100
- | --- | --- |
101
- | Android | Foreground Service + NotificationManager with `MessagingStyle`. |
102
- | iOS | `UNNotificationContent` with `threadIdentifier` + SocketIO Task. |
120
+ On non-GMS Android devices the plugin starts a foreground service that keeps a Socket.IO connection alive. Call this after the user logs in:
103
121
 
104
- ## Setup by Platform
122
+ ```typescript
123
+ await MessengerNotifications.startPersistentSocket({
124
+ url: 'wss://your-chat-server.com',
125
+ token: 'YOUR_AUTH_JWT',
126
+ });
127
+
128
+ // Call when the user logs out
129
+ await MessengerNotifications.stopPersistentSocket();
130
+ ```
131
+
132
+ > On GMS devices the socket is only opened for the duration of each incoming FCM push. `startPersistentSocket` stores the credentials but does not start the service.
133
+
134
+ ### FCM Token Registration
135
+
136
+ After the user logs in, trigger FCM token registration with your backend:
137
+
138
+ ```typescript
139
+ await MessengerNotifications.registerFcmToken();
140
+ ```
105
141
 
106
- > For full host‑app steps (Android & iOS), see `HOST_APP_SETUP.md`.
142
+ The plugin reads the FCM token and JWT from `safe_storage` (SharedPreferences / UserDefaults) and POSTs to `{backendBaseUrl}/api/users/fcm-token`. Registration is skipped automatically if it has already succeeded.
143
+
144
+ ### Native Integration
145
+
146
+ The plugin is typically triggered from native background handlers:
147
+
148
+ **Android** — Inside your `FirebaseMessagingService.onMessageReceived`:
149
+
150
+ ```java
151
+ // Kicks off the full fetch → decrypt → notify pipeline
152
+ FcmFetchManager.retrieveMessages(context, remoteMessage.getData());
153
+ ```
154
+
155
+ **iOS** — Inside `application(_:didReceiveRemoteNotification:fetchCompletionHandler:)`:
156
+
157
+ ```swift
158
+ EncryptedMessageNotifier.notifyFromPushData(userInfo as? [String: Any] ?? [:])
159
+ ```
160
+
161
+ ---
162
+
163
+ ## Setup by Platform
107
164
 
108
165
  ### Android
109
166
 
110
- - **AndroidManifest.xml**: Add `FOREGROUND_SERVICE` and `POST_NOTIFICATIONS` permissions.
111
- - **ProGuard**: Add `-keep` rules for the plugin and Socket.IO.
167
+ #### 1. Safe Storage Keys
168
+
169
+ The plugin reads credentials from `SharedPreferences` file `"safe_storage"`. Your JS app should write the following keys (e.g. via a SafeStorage plugin):
170
+
171
+ | Key | Description |
172
+ | --- | --- |
173
+ | `token` / `authToken` | User's JWT for socket auth and API calls |
174
+ | `socketUrl` | WebSocket server URL |
175
+ | `backendBaseUrl` / `serverUrl` / ... | HTTP base URL for the unread messages API |
176
+ | `fcmToken` | FCM registration token (written by Firebase) |
177
+ | `roomDecryptedKeys` | JSON map of room E2EE private keys |
178
+ | `memberDecryptedKeys` | JSON map of member E2EE private keys |
179
+
180
+ #### 2. Required Resources
181
+
182
+ Add the following drawables to your host app's `res/drawable/` so the plugin can find them at runtime (fallback to system icons if absent):
183
+
184
+ | Resource | Purpose |
185
+ | --- | --- |
186
+ | `ic_notification.png` | Small notification icon (monochrome, white on transparent) |
187
+ | `ic_transparent.png` | Transparent icon for the persistent service notification |
188
+
189
+ #### 3. AndroidManifest.xml
190
+
191
+ The plugin's manifest already declares all necessary services and permissions. If you need to override, merge the following into your app's manifest:
192
+
193
+ ```xml
194
+ <uses-permission android:name="android.permission.INTERNET" />
195
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
196
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
197
+ <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
198
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
199
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
200
+ ```
201
+
202
+ #### 4. ProGuard
203
+
204
+ Add to your `proguard-rules.pro`:
205
+
206
+ ```proguard
207
+ -keep class com.codecraft_studio.messenger.notifications.** { *; }
208
+ -keep class io.socket.** { *; }
209
+ -keep class org.java_websocket.** { *; }
210
+ ```
211
+
212
+ ---
112
213
 
113
214
  ### iOS
114
215
 
115
- - **Capabilities**: Enable **Push Notifications** and **Background Modes** (Background fetch, Remote notifications).
216
+ #### 1. Capabilities
116
217
 
117
- ## API
218
+ In Xcode, enable the following for your app target:
219
+
220
+ - **Push Notifications**
221
+ - **Background Modes** → check *Remote notifications* and *Background fetch*
222
+
223
+ #### 2. AppDelegate
224
+
225
+ Forward remote notifications to the plugin:
118
226
 
119
- | Method | Description |
227
+ ```swift
228
+ import UIKit
229
+ import Capacitor
230
+
231
+ @UIApplicationMain
232
+ class AppDelegate: UIResponder, UIApplicationDelegate {
233
+
234
+ func application(
235
+ _ application: UIApplication,
236
+ didReceiveRemoteNotification userInfo: [AnyHashable: Any],
237
+ fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
238
+ ) {
239
+ let data = userInfo as? [String: Any] ?? [:]
240
+ let handled = EncryptedMessageNotifier.notifyFromPushData(data)
241
+ completionHandler(handled ? .newData : .noData)
242
+ }
243
+ }
244
+ ```
245
+
246
+ #### 3. Safe Storage Keys
247
+
248
+ The plugin reads from `UserDefaults` suite `"safe_storage"`. Write the following keys from your JS app:
249
+
250
+ | Key | Description |
120
251
  | --- | --- |
121
- | `startPersistentSocket(options)` | Starts the background socket service (Android only). |
122
- | `stopPersistentSocket()` | Stops the background socket service. |
123
- | `showNotification(options)` | Manually triggers a native grouped notification. |
124
- | `clearRoomNotification(options)` | Clears all notifications for a specific room. |
125
- | `getPendingRoomId()` | Returns the roomId if the app was launched from a notification. |
252
+ | `token` / `authToken` | User's JWT |
253
+ | `socketUrl` | WebSocket server URL |
254
+ | `backendBaseUrl` / `serverUrl` / ... | HTTP base URL |
255
+ | `fcmToken` | APNs/FCM token |
256
+ | `roomDecryptedKeys` | JSON map of room E2EE private keys |
257
+ | `memberDecryptedKeys` | JSON map of member E2EE private keys |
258
+
259
+ ---
260
+
261
+ ## How It Works
262
+
263
+ ### Message Flow (Android)
264
+
265
+ ```text
266
+ FCM push received
267
+
268
+
269
+ FcmFetchManager.retrieveMessages()
270
+
271
+ ├─► FcmFetchForegroundService (keep process alive + wake lock)
272
+
273
+ ├─► TemporarySocketSessionManager.runSession()
274
+ │ └─► socket.emit("sync_messages")
275
+ │ └─► EncryptedMessageNotifier.notifyFromSyncMessagesResponse()
276
+ │ └─► NativeCrypto.decrypt*()
277
+ │ └─► NotificationHelper.showRoomNotification()
278
+
279
+ └─► [fallback] UnreadMessagesFetcher.fetchAndNotify() (HTTP API)
280
+ ```
281
+
282
+ ### Message Flow (iOS)
283
+
284
+ ```text
285
+ Silent push received
286
+
287
+
288
+ EncryptedMessageNotifier.notifyFromPushData()
289
+
290
+ ├─► Direct decrypt from push payload
291
+
292
+ └─► [fallback] TemporarySocketSessionManager.runSession()
293
+ └─► UnreadMessagesFetcher.fetchAndNotify() (HTTP API)
294
+ ```
295
+
296
+ ---
297
+
298
+ ## API
299
+
300
+ ### `showNotification(options)`
301
+
302
+ Shows a native grouped chat notification.
303
+
304
+ | Option | Type | Required | Description |
305
+ | ----------- | -------- | -------- | ---------------------------------------------------------- |
306
+ | `title` | `string` | ✓ | Sender name displayed in the notification |
307
+ | `body` | `string` | ✓ | Message body |
308
+ | `roomId` | `number` | ✓ | Room identifier used for grouping |
309
+ | `messageId` | `string` | | Unique message ID for deduplication |
310
+ | `timestamp` | `number` | | Unix timestamp (ms) for message ordering |
311
+ | `roomName` | `string` | | Room display name shown in notification subtitle |
312
+ | `senderId` | `number` | | Sender's user ID (used for avatar/Person identity) |
313
+ | `avatarSvg` | `string` | | SVG string for the sender's avatar |
314
+
315
+ ---
316
+
317
+ ### `clearRoomNotification(options)`
318
+
319
+ Cancels all active notifications for a room and clears its in-memory history.
320
+
321
+ | Option | Type | Required | Description |
322
+ | -------- | -------- | -------- | -------------------------------------------- |
323
+ | `roomId` | `number` | ✓ | Room whose notifications should be cleared |
324
+
325
+ ---
326
+
327
+ ### `getPendingRoomId()`
328
+
329
+ Returns the `roomId` from the notification that launched the app (cold start), then clears it. Returns `{ roomId: null }` if the app was not opened via a notification.
330
+
331
+ ---
332
+
333
+ ### `startPersistentSocket(options)`
334
+
335
+ Stores socket credentials and starts the persistent foreground service on non-GMS Android devices.
336
+
337
+ | Option | Type | Required | Description |
338
+ | ------- | -------- | -------- | -------------------- |
339
+ | `url` | `string` | ✓ | WebSocket server URL |
340
+ | `token` | `string` | ✓ | JWT auth token |
341
+
342
+ ---
343
+
344
+ ### `stopPersistentSocket()`
345
+
346
+ Stops the persistent socket foreground service.
347
+
348
+ ---
349
+
350
+ ### `checkPermissions()`
351
+
352
+ Returns the current notification permission state.
353
+
354
+ **Returns**: `Promise<{ notifications: 'granted' | 'denied' | 'prompt' }>`
355
+
356
+ ---
357
+
358
+ ### `requestPermissions()`
359
+
360
+ Prompts the user for notification permission.
361
+
362
+ **Returns**: `Promise<{ notifications: 'granted' | 'denied' | 'prompt' }>`
363
+
364
+ ---
365
+
366
+ ### `registerFcmToken()`
367
+
368
+ Triggers registration of the device's FCM/APNs token with the backend server. Reads `fcmToken`, JWT, and backend URL from `safe_storage`. No-ops if already registered or if prerequisites are missing.
126
369
 
127
370
  ---
128
371
 
129
372
  ## Troubleshooting
130
373
 
131
- ### Android: Service is killed
374
+ ### Android: Notifications not appearing after FCM push
375
+
376
+ 1. Confirm `roomDecryptedKeys` and `memberDecryptedKeys` are written to `safe_storage` before the push arrives.
377
+ 2. Check Logcat for `EncryptedMessageNotifier`, `NativeCrypto`, and `NotificationHelper` tags.
378
+ 3. Ensure `ic_notification` drawable exists in the host app (required for the notification icon).
379
+
380
+ ### Android: Persistent service is killed
381
+
382
+ Ensure `android:foregroundServiceType="dataSync"` is declared on `PersistentSocketService` (already set in the plugin manifest). On Android 14+ the system enforces this.
383
+
384
+ ### Android: Duplicate notifications
385
+
386
+ The plugin deduplicates by `messageId`. Ensure each message has a unique, stable `messageId` in the FCM payload.
387
+
388
+ ### iOS: Notifications not grouping
132
389
 
133
- Ensure you have added `android:foregroundServiceType="dataSync"` to the service declaration in `AndroidManifest.xml` as required by Android 14+.
390
+ Confirm the same `roomId` is passed to `showNotification` and that Background Modes are enabled for the app target in Xcode.
134
391
 
135
- ### iOS: Notifications aren't grouping
392
+ ### iOS: Decryption failing
136
393
 
137
- Ensure `threadIdentifier` is correctly set in the push payload or that you are passing the same `roomId` to `showNotification`.
394
+ Ensure `roomDecryptedKeys` and `memberDecryptedKeys` are present in `UserDefaults` suite `"safe_storage"` before the silent push arrives.
395
+
396
+ ---
138
397
 
139
398
  ## Development
140
399
 
141
- - **Build**: `npm run build`
142
- - **Lint**: `npm run lint`
143
- - **Format**: `npm run fmt`
144
- - **Verify**: `npm run verify`
400
+ ```bash
401
+ npm run build # compile TypeScript
402
+ npm run lint # run ESLint
403
+ npm run fmt # run Prettier
404
+ npm run verify # build + lint + native checks
405
+ ```
406
+
407
+ ---
145
408
 
146
409
  ## Contributing
147
410
 
148
411
  Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
149
412
 
413
+ ---
414
+
150
415
  ## License
151
416
 
152
417
  MIT
@@ -43,6 +43,7 @@ android {
43
43
  repositories {
44
44
  google()
45
45
  mavenCentral()
46
+ maven { url 'https://jitpack.io' }
46
47
  }
47
48
 
48
49
  dependencies {
@@ -50,12 +51,21 @@ dependencies {
50
51
  implementation project(':capacitor-android')
51
52
  implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
52
53
  implementation "androidx.core:core:$androidxCoreVersion"
53
-
54
+
54
55
  // Socket.IO client for Android
55
56
  implementation ('io.socket:socket.io-client:2.1.0') {
56
57
  exclude group: 'org.json', module: 'json'
57
58
  }
58
-
59
+
60
+ // Libsodium JNI for E2EE decryption
61
+ implementation 'com.github.joshjdevl.libsodiumjni:libsodium-jni-aar:2.0.2'
62
+
63
+ // Google Play Services for GMS availability check
64
+ implementation 'com.google.android.gms:play-services-base:18.2.0'
65
+
66
+ // AndroidSVG for rendering avatar SVGs in notifications
67
+ implementation 'com.caverock:androidsvg:1.4'
68
+
59
69
  testImplementation 'junit:junit:4.13.2'
60
70
  androidTestImplementation 'androidx.test.ext:junit:1.1.5'
61
71
  androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
@@ -6,6 +6,23 @@
6
6
  android:exported="false"
7
7
  android:foregroundServiceType="dataSync" />
8
8
 
9
+ <service
10
+ android:name="com.codecraft_studio.messenger.notifications.FcmFetchForegroundService"
11
+ android:enabled="true"
12
+ android:exported="false"
13
+ android:foregroundServiceType="dataSync" />
14
+
15
+ <service
16
+ android:name="com.codecraft_studio.messenger.notifications.FcmFetchBackgroundService"
17
+ android:enabled="true"
18
+ android:exported="false" />
19
+
20
+ <service
21
+ android:name="com.codecraft_studio.messenger.notifications.FcmJobService"
22
+ android:enabled="true"
23
+ android:exported="false"
24
+ android:permission="android.permission.BIND_JOB_SERVICE" />
25
+
9
26
  <receiver
10
27
  android:name="com.codecraft_studio.messenger.notifications.NotificationDismissReceiver"
11
28
  android:enabled="true"
@@ -13,7 +30,9 @@
13
30
  </application>
14
31
 
15
32
  <uses-permission android:name="android.permission.INTERNET" />
33
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
16
34
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
17
35
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />
18
36
  <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
37
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
19
38
  </manifest>