capacitor-push-signal 0.0.2 → 0.0.4
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/README.md +155 -9
- package/android/build.gradle +1 -0
- package/android/src/main/AndroidManifest.xml +8 -0
- package/android/src/main/java/com/antonseagull/cap/push/signal/PushModels.kt +24 -0
- package/android/src/main/java/com/antonseagull/cap/push/signal/PushSignalCenter.kt +337 -27
- package/android/src/main/java/com/antonseagull/cap/push/signal/PushSignalPlugin.kt +42 -5
- package/dist/docs.json +140 -8
- package/dist/esm/PushSignalError.d.ts +21 -0
- package/dist/esm/PushSignalError.js +17 -0
- package/dist/esm/PushSignalError.js.map +1 -0
- package/dist/esm/definitions.d.ts +10 -2
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +2 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/pushSignal.d.ts +2 -1
- package/dist/esm/pushSignal.js +65 -5
- package/dist/esm/pushSignal.js.map +1 -1
- package/dist/esm/types.d.ts +20 -1
- package/dist/esm/types.js.map +1 -1
- package/dist/esm/web.d.ts +2 -1
- package/dist/esm/web.js +3 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +86 -5
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +86 -5
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/PushSignalCore/PushSignalCenter.m +12 -1
- package/ios/Sources/PushSignalPlugin/PushSignalPlugin.swift +18 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -111,6 +111,53 @@ Without `initialize(...)` or that file, `getCredentials()` throws.
|
|
|
111
111
|
|
|
112
112
|
If the host app already declares its own `FirebaseMessagingService`, only one service can handle `com.google.firebase.MESSAGING_EVENT`. Prefer this plugin’s service or forward events into it.
|
|
113
113
|
|
|
114
|
+
### Detecting a non-Google device: `getDiagnostics()` and `PushSignalError`
|
|
115
|
+
|
|
116
|
+
When Google Play services cannot serve FCM, the plugin detects it and tells you which service to use instead — both in the error message and as structured data, so the cause is visible in JS logs. Before requesting a token it checks Google Play services and retries up to 5 times with backoff and a 10 s per-attempt timeout.
|
|
117
|
+
|
|
118
|
+
`initialize()` and `getCredentials()` reject with `PushSignalError`:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
import { getCredentials, PushSignalError } from 'capacitor-push-signal';
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
const credentials = await getCredentials();
|
|
125
|
+
} catch (error) {
|
|
126
|
+
if (error instanceof PushSignalError) {
|
|
127
|
+
console.warn(error.code); // E_GMS_MISSING, E_FCM_TOKEN, ...
|
|
128
|
+
console.warn(error.message); // "... Use HMS Push Kit (Huawei Push) instead of the standard Google service."
|
|
129
|
+
console.warn(error.provider); // 'hms'
|
|
130
|
+
console.warn(error.hint); // actionable sentence
|
|
131
|
+
console.warn(error.diagnostics); // full device/provider snapshot
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
You can also inspect diagnostics without requesting a token:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { getDiagnostics } from 'capacitor-push-signal';
|
|
140
|
+
|
|
141
|
+
const diagnostics = await getDiagnostics();
|
|
142
|
+
// { platform, gmsAvailable, gmsStatus, manufacturer, brand, model,
|
|
143
|
+
// provider, providerName, providerInstalled?, hint? }
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Provider suggested when GMS is unavailable:
|
|
147
|
+
|
|
148
|
+
| Manufacturer | `provider` | Service |
|
|
149
|
+
| ----------------------- | ------------ | ------------------------------ |
|
|
150
|
+
| HUAWEI / HONOR | `hms` | HMS Push Kit (Huawei Push) |
|
|
151
|
+
| Xiaomi / Redmi / POCO | `mi_push` | Mi Push (Xiaomi Push) |
|
|
152
|
+
| OPPO / OnePlus / realme | `oppo_push` | OPPO Push (HeyTap) |
|
|
153
|
+
| vivo / iQOO | `vivo_push` | vivo Push |
|
|
154
|
+
| Meizu | `meizu_push` | Meizu Push |
|
|
155
|
+
| anything else | `unknown` | generic “use another provider” |
|
|
156
|
+
|
|
157
|
+
`providerInstalled` is `true` only when the provider service app was positively found on the device (`com.huawei.hwid`, `com.xiaomi.xmsf`); otherwise it is omitted. iOS always reports `provider: 'apns'`. The plugin never logs for you — pass the data wherever you need.
|
|
158
|
+
|
|
159
|
+
Error codes: `E_GMS_MISSING`, `E_GMS_DISABLED`, `E_GMS_UPDATE_REQUIRED`, `E_GMS_INVALID`, `E_GMS_UNAVAILABLE`, `E_FCM_TOKEN`, `E_FIREBASE_CONFIG`, `E_NOT_INITIALIZED`, `E_CREDENTIALS`, `E_INIT`, `E_DIAGNOSTICS`.
|
|
160
|
+
|
|
114
161
|
## Incoming messages vs taps
|
|
115
162
|
|
|
116
163
|
| App state | iOS | Android (notification payload) | Android (data-only) |
|
|
@@ -122,6 +169,66 @@ If the host app already declares its own `FirebaseMessagingService`, only one se
|
|
|
122
169
|
- `onNotificationPress` — the user opened the notification, including a cold start.
|
|
123
170
|
- On iOS, a visible push received in the background or when the app is killed is delivered on tap, not through `onMessage`. That is an OS limit.
|
|
124
171
|
|
|
172
|
+
## Rich / image notifications
|
|
173
|
+
|
|
174
|
+
`PushMessage.imageUrl` is resolved from (first match wins):
|
|
175
|
+
|
|
176
|
+
1. FCM `notification.image` / `RemoteMessage.notification.imageUrl` (Android)
|
|
177
|
+
2. `data.image`
|
|
178
|
+
3. `data.imageUrl`
|
|
179
|
+
4. iOS also: `fcm_options.image` (FCM → APNs)
|
|
180
|
+
|
|
181
|
+
| App state | iOS | Android |
|
|
182
|
+
| --- | --- | --- |
|
|
183
|
+
| Foreground | Needs a host **Notification Service Extension** (NSE) to attach the image; the plugin forwards `imageUrl` to JS | Plugin downloads the image and shows `BigPictureStyle` in the tray |
|
|
184
|
+
| Background / killed | NSE attaches the image when `mutable-content: 1` is set | FCM system tray usually shows `notification.image` without plugin code |
|
|
185
|
+
|
|
186
|
+
### Android payload (FCM HTTP v1)
|
|
187
|
+
|
|
188
|
+
Put the image on both `notification` (background tray) and `data` (foreground path / JS):
|
|
189
|
+
|
|
190
|
+
```json
|
|
191
|
+
{
|
|
192
|
+
"message": {
|
|
193
|
+
"token": "...",
|
|
194
|
+
"notification": {
|
|
195
|
+
"title": "Hello",
|
|
196
|
+
"body": "With image",
|
|
197
|
+
"image": "https://example.com/photo.jpg"
|
|
198
|
+
},
|
|
199
|
+
"data": {
|
|
200
|
+
"image": "https://example.com/photo.jpg"
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### iOS payload (APNs)
|
|
207
|
+
|
|
208
|
+
```json
|
|
209
|
+
{
|
|
210
|
+
"aps": {
|
|
211
|
+
"alert": { "title": "Hello", "body": "With image" },
|
|
212
|
+
"mutable-content": 1,
|
|
213
|
+
"sound": "default"
|
|
214
|
+
},
|
|
215
|
+
"image": "https://example.com/photo.jpg"
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### iOS Notification Service Extension
|
|
220
|
+
|
|
221
|
+
iOS never downloads remote images for you. Add an NSE to the **host** Capacitor app (not installed by `npx cap sync`).
|
|
222
|
+
|
|
223
|
+
1. Xcode → **File → New → Target → Notification Service Extension**.
|
|
224
|
+
2. Copy [`example-app/ios/App/NotificationService/NotificationService.swift`](example-app/ios/App/NotificationService/NotificationService.swift) into the extension (see also that folder’s README).
|
|
225
|
+
3. Keep the extension bundle id under your app id (e.g. `com.example.app.NotificationService`).
|
|
226
|
+
4. Send pushes with `mutable-content: 1` and `image` / `imageUrl`.
|
|
227
|
+
|
|
228
|
+
The example app already wires a **NotificationService** target for reference.
|
|
229
|
+
|
|
230
|
+
If image download fails, both platforms fall back to a normal title/body notification.
|
|
231
|
+
|
|
125
232
|
## Web
|
|
126
233
|
|
|
127
234
|
`initialize()` resolves. `getCredentials()` throws. Listeners (`onMessage`, `onNotificationPress`) are no-ops.
|
|
@@ -132,6 +239,7 @@ If the host app already declares its own `FirebaseMessagingService`, only one se
|
|
|
132
239
|
|
|
133
240
|
* [`initialize(...)`](#initialize)
|
|
134
241
|
* [`getCredentials()`](#getcredentials)
|
|
242
|
+
* [`getDiagnostics()`](#getdiagnostics)
|
|
135
243
|
* [`checkPermissions()`](#checkpermissions)
|
|
136
244
|
* [`requestPermissions()`](#requestpermissions)
|
|
137
245
|
* [`startListening()`](#startlistening)
|
|
@@ -173,6 +281,23 @@ Returns APNs (iOS) or FCM (Android) credentials for your server.
|
|
|
173
281
|
--------------------
|
|
174
282
|
|
|
175
283
|
|
|
284
|
+
### getDiagnostics()
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
getDiagnostics() => Promise<PushDiagnostics>
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
Inspect the device push provider without requesting a token.
|
|
291
|
+
|
|
292
|
+
On Android it reports whether Google Play services can serve FCM and, when
|
|
293
|
+
they cannot, which service to use instead (HMS, Mi Push, OPPO/vivo/Meizu).
|
|
294
|
+
On iOS it always reports APNs.
|
|
295
|
+
|
|
296
|
+
**Returns:** <code>Promise<<a href="#pushdiagnostics">PushDiagnostics</a>></code>
|
|
297
|
+
|
|
298
|
+
--------------------
|
|
299
|
+
|
|
300
|
+
|
|
176
301
|
### checkPermissions()
|
|
177
302
|
|
|
178
303
|
```typescript
|
|
@@ -265,11 +390,26 @@ addListener(eventName: 'onNotificationPress', listenerFunc: (message: PushMessag
|
|
|
265
390
|
|
|
266
391
|
| Prop | Type |
|
|
267
392
|
| ----------------- | ----------------------------------------------------------- |
|
|
268
|
-
| **`platform`** | <code><a href="#pushplatform">PushPlatform</a></code> |
|
|
269
393
|
| **`token`** | <code>string</code> |
|
|
270
394
|
| **`environment`** | <code><a href="#pushenvironment">PushEnvironment</a></code> |
|
|
271
395
|
|
|
272
396
|
|
|
397
|
+
#### PushDiagnostics
|
|
398
|
+
|
|
399
|
+
| Prop | Type | Description |
|
|
400
|
+
| ----------------------- | ----------------------------------------------------- | -------------------------------------------------------------------------------- |
|
|
401
|
+
| **`platform`** | <code><a href="#pushplatform">PushPlatform</a></code> | |
|
|
402
|
+
| **`gmsAvailable`** | <code>boolean</code> | True when Google Play services can serve FCM; false on a ROM without GMS. |
|
|
403
|
+
| **`gmsStatus`** | <code>number</code> | Raw GoogleApiAvailability status code. 0 means SUCCESS. |
|
|
404
|
+
| **`manufacturer`** | <code>string</code> | |
|
|
405
|
+
| **`brand`** | <code>string</code> | |
|
|
406
|
+
| **`model`** | <code>string</code> | |
|
|
407
|
+
| **`provider`** | <code><a href="#pushprovider">PushProvider</a></code> | |
|
|
408
|
+
| **`providerName`** | <code>string</code> | Human-readable provider name, e.g. "HMS Push Kit". |
|
|
409
|
+
| **`providerInstalled`** | <code>boolean</code> | True only when the provider service app was positively found on the device. |
|
|
410
|
+
| **`hint`** | <code>string</code> | Actionable sentence explaining which service to use instead of the standard one. |
|
|
411
|
+
|
|
412
|
+
|
|
273
413
|
#### PermissionStatus
|
|
274
414
|
|
|
275
415
|
| Prop | Type |
|
|
@@ -286,25 +426,31 @@ addListener(eventName: 'onNotificationPress', listenerFunc: (message: PushMessag
|
|
|
286
426
|
|
|
287
427
|
#### PushMessage
|
|
288
428
|
|
|
289
|
-
| Prop
|
|
290
|
-
|
|
|
291
|
-
| **`id`**
|
|
292
|
-
| **`title`**
|
|
293
|
-
| **`body`**
|
|
294
|
-
| **`
|
|
429
|
+
| Prop | Type | Description |
|
|
430
|
+
| -------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------- |
|
|
431
|
+
| **`id`** | <code>string</code> | |
|
|
432
|
+
| **`title`** | <code>string</code> | |
|
|
433
|
+
| **`body`** | <code>string</code> | |
|
|
434
|
+
| **`imageUrl`** | <code>string</code> | Resolved image URL from FCM notification.image / data.image / data.imageUrl. |
|
|
435
|
+
| **`data`** | <code><a href="#record">Record</a><string, string></code> | |
|
|
295
436
|
|
|
296
437
|
|
|
297
438
|
### Type Aliases
|
|
298
439
|
|
|
299
440
|
|
|
441
|
+
#### PushEnvironment
|
|
442
|
+
|
|
443
|
+
<code>'sandbox' | 'production'</code>
|
|
444
|
+
|
|
445
|
+
|
|
300
446
|
#### PushPlatform
|
|
301
447
|
|
|
302
448
|
<code>'ios' | 'android'</code>
|
|
303
449
|
|
|
304
450
|
|
|
305
|
-
####
|
|
451
|
+
#### PushProvider
|
|
306
452
|
|
|
307
|
-
<code>'
|
|
453
|
+
<code>'fcm' | 'hms' | 'mi_push' | 'oppo_push' | 'vivo_push' | 'meizu_push' | 'apns' | 'unknown'</code>
|
|
308
454
|
|
|
309
455
|
|
|
310
456
|
#### PermissionState
|
package/android/build.gradle
CHANGED
|
@@ -59,6 +59,7 @@ dependencies {
|
|
|
59
59
|
implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
|
|
60
60
|
implementation "androidx.activity:activity-ktx:1.10.1"
|
|
61
61
|
implementation "androidx.core:core-ktx:1.16.0"
|
|
62
|
+
implementation "com.google.android.gms:play-services-base:18.5.0"
|
|
62
63
|
implementation "com.google.firebase:firebase-messaging:24.1.2"
|
|
63
64
|
testImplementation "junit:junit:$junitVersion"
|
|
64
65
|
androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
|
|
@@ -1,7 +1,15 @@
|
|
|
1
1
|
<?xml version="1.0" encoding="utf-8"?>
|
|
2
2
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
3
|
+
<uses-permission android:name="android.permission.INTERNET" />
|
|
3
4
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
4
5
|
|
|
6
|
+
<!-- Android 11+ package visibility: lets isPackageInstalled() confirm an
|
|
7
|
+
alternative push provider on the device. -->
|
|
8
|
+
<queries>
|
|
9
|
+
<package android:name="com.huawei.hwid" />
|
|
10
|
+
<package android:name="com.xiaomi.xmsf" />
|
|
11
|
+
</queries>
|
|
12
|
+
|
|
5
13
|
<application>
|
|
6
14
|
<provider
|
|
7
15
|
android:name="com.antonseagull.cap.push.signal.PushSignalInitProvider"
|
|
@@ -5,6 +5,7 @@ data class PushMessage(
|
|
|
5
5
|
val title: String?,
|
|
6
6
|
val body: String?,
|
|
7
7
|
val data: Map<String, String>,
|
|
8
|
+
val imageUrl: String? = null,
|
|
8
9
|
)
|
|
9
10
|
|
|
10
11
|
data class AndroidFirebaseConfig(
|
|
@@ -13,3 +14,26 @@ data class AndroidFirebaseConfig(
|
|
|
13
14
|
val current_key: String?,
|
|
14
15
|
val project_number: String?,
|
|
15
16
|
)
|
|
17
|
+
|
|
18
|
+
data class PushDiagnostics(
|
|
19
|
+
val platform: String,
|
|
20
|
+
val gmsAvailable: Boolean,
|
|
21
|
+
val gmsStatus: Int,
|
|
22
|
+
val manufacturer: String,
|
|
23
|
+
val brand: String,
|
|
24
|
+
val model: String,
|
|
25
|
+
val provider: String,
|
|
26
|
+
val providerName: String,
|
|
27
|
+
val providerInstalled: Boolean?,
|
|
28
|
+
val hint: String?,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Carries a machine-readable [code] to JavaScript, so the host app can branch on
|
|
33
|
+
* the exact reason (missing GMS, disabled GMS, FCM token failure, ...).
|
|
34
|
+
*/
|
|
35
|
+
class PushSignalException(
|
|
36
|
+
val code: String,
|
|
37
|
+
message: String,
|
|
38
|
+
cause: Throwable? = null,
|
|
39
|
+
) : IllegalStateException(message, cause)
|