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.
- package/MessengerNotifications.podspec +2 -1
- package/Package.swift +4 -2
- package/README.md +324 -59
- package/android/build.gradle +12 -2
- package/android/src/main/AndroidManifest.xml +19 -0
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/EncryptedMessageNotifier.java +549 -41
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/FcmFetchBackgroundService.java +32 -0
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/FcmFetchForegroundService.java +182 -0
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/FcmFetchManager.java +203 -2
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/FcmJobService.java +70 -0
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/FcmTokenRegistrar.java +152 -0
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/GmsHelper.java +19 -0
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/MessengerNotificationsPlugin.java +196 -15
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/NativeCrypto.java +82 -29
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/NotificationDismissReceiver.java +4 -5
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/NotificationHelper.java +348 -127
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/PersistentSocketService.java +132 -52
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/TemporarySocketSessionManager.java +297 -0
- package/android/src/main/java/com/codecraft_studio/messenger/notifications/UnreadMessagesFetcher.java +180 -0
- package/android/src/main/res/drawable/ic_notification.png +0 -0
- package/android/src/main/res/drawable/ic_transparent.png +0 -0
- package/android/src/main/res/values/strings.xml +4 -0
- package/dist/esm/definitions.d.ts +6 -0
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/web.d.ts +3 -0
- package/dist/esm/web.js +3 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +3 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +3 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/MessengerNotificationsPlugin/EncryptedMessageNotifier.swift +271 -0
- package/ios/Sources/MessengerNotificationsPlugin/FcmTokenRegistrar.swift +94 -0
- package/ios/Sources/MessengerNotificationsPlugin/MessengerNotificationsPlugin.swift +41 -9
- package/ios/Sources/MessengerNotificationsPlugin/NativeCrypto.swift +70 -12
- package/ios/Sources/MessengerNotificationsPlugin/NotificationHelper.swift +313 -41
- package/ios/Sources/MessengerNotificationsPlugin/SafeStorageStore.swift +1 -1
- package/ios/Sources/MessengerNotificationsPlugin/TemporarySocketSessionManager.swift +292 -36
- package/ios/Sources/MessengerNotificationsPlugin/UnreadMessagesFetcher.swift +344 -0
- 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', '~>
|
|
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
|
-
- [
|
|
14
|
-
- [
|
|
15
|
-
- [
|
|
16
|
-
- [
|
|
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**:
|
|
27
|
-
- **
|
|
28
|
-
- **
|
|
29
|
-
- **
|
|
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
|
-
###
|
|
69
|
+
### Permissions
|
|
48
70
|
|
|
49
|
-
|
|
71
|
+
Before showing notifications, check and request permission:
|
|
50
72
|
|
|
51
73
|
```typescript
|
|
52
74
|
import { MessengerNotifications } from 'capacitor-messenger-notifications';
|
|
53
75
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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
|
-
|
|
86
|
+
### Show a Notification
|
|
62
87
|
|
|
63
88
|
```typescript
|
|
64
89
|
await MessengerNotifications.showNotification({
|
|
65
|
-
title: '
|
|
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
|
-
###
|
|
75
|
-
|
|
76
|
-
This plugin is often triggered from native background tasks (FCM / Silent Push).
|
|
101
|
+
### Clear a Room's Notifications
|
|
77
102
|
|
|
78
|
-
|
|
79
|
-
|
|
103
|
+
```typescript
|
|
104
|
+
await MessengerNotifications.clearRoomNotification({ roomId: 101 });
|
|
105
|
+
```
|
|
80
106
|
|
|
81
|
-
|
|
107
|
+
### Cold-Start Navigation
|
|
82
108
|
|
|
83
|
-
|
|
109
|
+
When the user taps a notification to open the app, retrieve the room to navigate to:
|
|
84
110
|
|
|
85
|
-
```
|
|
86
|
-
{
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
118
|
+
### Persistent Socket (Android)
|
|
98
119
|
|
|
99
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
111
|
-
|
|
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
|
-
|
|
216
|
+
#### 1. Capabilities
|
|
116
217
|
|
|
117
|
-
|
|
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
|
-
|
|
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
|
-
| `
|
|
122
|
-
| `
|
|
123
|
-
| `
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
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:
|
|
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
|
-
|
|
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:
|
|
392
|
+
### iOS: Decryption failing
|
|
136
393
|
|
|
137
|
-
Ensure `
|
|
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
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
package/android/build.gradle
CHANGED
|
@@ -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>
|