@attentive-mobile/attentive-react-native-sdk 2.0.0-beta.3 → 2.0.0-beta.5
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 +145 -10
- package/android/build.gradle +4 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/kotlin/com/attentivereactnativesdk/AttentivePushHelper.kt +93 -0
- package/android/src/main/kotlin/com/attentivereactnativesdk/AttentiveReactNativeSdkModule.kt +382 -56
- package/android/src/main/kotlin/com/attentivereactnativesdk/debug/NetworkingHelper.kt +220 -0
- package/ios/AttentiveReactNativeSdk.mm +33 -0
- package/ios/AttentiveReactNativeSdk.xcodeproj/project.xcworkspace/contents.xcworkspacedata +7 -0
- package/ios/AttentiveReactNativeSdk.xcodeproj/project.xcworkspace/xcuserdata/zheref.xcuserdatad/UserInterfaceState.xcuserstate +0 -0
- package/ios/AttentiveReactNativeSdk.xcodeproj/xcuserdata/zheref.xcuserdatad/xcschemes/xcschememanagement.plist +14 -0
- package/ios/Bridging/ATTNNativeSDK.swift +47 -6
- package/lib/commonjs/NativeAttentiveReactNativeSdk.js.map +1 -1
- package/lib/commonjs/eventTypes.js.map +1 -1
- package/lib/commonjs/index.js +28 -9
- package/lib/commonjs/index.js.map +1 -1
- package/lib/module/NativeAttentiveReactNativeSdk.js.map +1 -1
- package/lib/module/eventTypes.js.map +1 -1
- package/lib/module/index.js +29 -11
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/NativeAttentiveReactNativeSdk.d.ts +9 -1
- package/lib/typescript/NativeAttentiveReactNativeSdk.d.ts.map +1 -1
- package/lib/typescript/index.d.ts +24 -9
- package/lib/typescript/index.d.ts.map +1 -1
- package/package.json +7 -3
- package/src/NativeAttentiveReactNativeSdk.ts +11 -2
- package/src/index.tsx +29 -10
- package/ios/AttentiveReactNativeSdk.xcworkspace/contents.xcworkspacedata +0 -10
package/README.md
CHANGED
|
@@ -169,11 +169,148 @@ Attentive.identify({phone: '+15556667777'};)
|
|
|
169
169
|
// phone: '+15556667777'
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
-
### Push Notifications (iOS
|
|
172
|
+
### Push Notifications (iOS and Android)
|
|
173
173
|
|
|
174
|
-
The SDK supports push notification integration
|
|
174
|
+
The SDK supports push notification integration on both iOS (APNs) and Android (runtime permission + optional FCM). The following sections cover iOS-specific flows and a full **App events on Android** implementation that mirrors the behavior of the [Bonni](https://github.com/attentive-mobile/attentive-react-native-sdk/tree/main/Bonni) example app.
|
|
175
175
|
|
|
176
|
-
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
### App Events on Android
|
|
179
|
+
|
|
180
|
+
This section describes how to implement Attentive app events on Android so they behave like the iOS flow: **regular app opens** (launch and resume from background) and **notification permission** are handled using the SDK’s native Android APIs. You can add FCM token registration and push open handling when your app uses Firebase Cloud Messaging.
|
|
181
|
+
|
|
182
|
+
| SDK method | Purpose on Android |
|
|
183
|
+
|------------|--------------------|
|
|
184
|
+
| `getPushAuthorizationStatus()` | Returns `authorized`, `denied`, or `notDetermined` (uses `POST_NOTIFICATIONS` on API 33+). Use before `handleRegularOpen` so tracking uses the correct status. |
|
|
185
|
+
| `registerForPushNotifications()` | Requests `POST_NOTIFICATIONS` on Android 13+; no-op on older versions. |
|
|
186
|
+
| `handleRegularOpen(authStatus)` | Tracks a regular app open (launch or return to foreground). Call after `identify()` and pass the result of `getPushAuthorizationStatus()`. |
|
|
187
|
+
| `registerDeviceToken` / `registerDeviceTokenWithCallback` | Optional. Register your FCM token when using Firebase Cloud Messaging. |
|
|
188
|
+
| `handlePushOpen` / `handleForegroundPush` | Optional. Call when the user opens a notification or receives one in the foreground. |
|
|
189
|
+
|
|
190
|
+
#### Overview
|
|
191
|
+
|
|
192
|
+
- **Regular app open** – Call `handleRegularOpen(authorizationStatus)` when the app is opened (launch or returning to foreground). The SDK uses this for tracking and the `/mtctrl` endpoint.
|
|
193
|
+
- **Permission status** – On Android 13+ (API 33+), notification permission is `POST_NOTIFICATIONS`. The SDK exposes `getPushAuthorizationStatus()` so you can pass the correct status into `handleRegularOpen`.
|
|
194
|
+
- **Requesting permission** – Call `registerForPushNotifications()` to trigger the system permission dialog on Android 13+; it is a no-op on older versions.
|
|
195
|
+
- **Order of operations** – Always call `identify()` before any `handleRegularOpen()` so the SDK has user context for network requests.
|
|
196
|
+
|
|
197
|
+
#### Prerequisites
|
|
198
|
+
|
|
199
|
+
1. **AndroidManifest** – Declare the notification permission for Android 13+:
|
|
200
|
+
|
|
201
|
+
```xml
|
|
202
|
+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
|
203
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
204
|
+
<!-- other permissions -->
|
|
205
|
+
</manifest>
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
2. **Initialize and identify first** – In your app entry (e.g. root component `useEffect`), call `initialize(config)` and `identify(identifiers)` before any push or app-event logic.
|
|
209
|
+
|
|
210
|
+
#### 1. On app launch (Android)
|
|
211
|
+
|
|
212
|
+
Right after `identify()`, do the following for the Android path:
|
|
213
|
+
|
|
214
|
+
1. Get the current notification authorization status with `getPushAuthorizationStatus()`.
|
|
215
|
+
2. Call `handleRegularOpen(authStatus)` with that status.
|
|
216
|
+
3. Optionally call `registerForPushNotifications()` to prompt for permission (Android 13+).
|
|
217
|
+
|
|
218
|
+
```typescript
|
|
219
|
+
import { Platform } from 'react-native';
|
|
220
|
+
import {
|
|
221
|
+
initialize,
|
|
222
|
+
identify,
|
|
223
|
+
getPushAuthorizationStatus,
|
|
224
|
+
registerForPushNotifications,
|
|
225
|
+
handleRegularOpen,
|
|
226
|
+
type AttentiveSdkConfiguration,
|
|
227
|
+
type PushAuthorizationStatus,
|
|
228
|
+
} from 'attentive-react-native-sdk';
|
|
229
|
+
|
|
230
|
+
// Inside your root component (e.g. App.tsx useEffect):
|
|
231
|
+
initialize(config);
|
|
232
|
+
identify({ email: 'user@example.com', clientUserId: 'id-123' });
|
|
233
|
+
|
|
234
|
+
if (Platform.OS === 'android') {
|
|
235
|
+
getPushAuthorizationStatus()
|
|
236
|
+
.then((authStatus: PushAuthorizationStatus) => {
|
|
237
|
+
handleRegularOpen(authStatus);
|
|
238
|
+
})
|
|
239
|
+
.catch(() => {
|
|
240
|
+
handleRegularOpen('authorized'); // fallback
|
|
241
|
+
});
|
|
242
|
+
registerForPushNotifications(); // Shows permission dialog on Android 13+
|
|
243
|
+
}
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
#### 2. When app returns to foreground (Android)
|
|
247
|
+
|
|
248
|
+
Subscribe to `AppState` and, when the app becomes `active`, get the current status and call `handleRegularOpen` again:
|
|
249
|
+
|
|
250
|
+
```typescript
|
|
251
|
+
import { AppState } from 'react-native';
|
|
252
|
+
import { getPushAuthorizationStatus, handleRegularOpen } from 'attentive-react-native-sdk';
|
|
253
|
+
import type { PushAuthorizationStatus } from 'attentive-react-native-sdk';
|
|
254
|
+
|
|
255
|
+
const subscription = AppState.addEventListener('change', (nextAppState) => {
|
|
256
|
+
if (nextAppState === 'active' && Platform.OS === 'android') {
|
|
257
|
+
getPushAuthorizationStatus()
|
|
258
|
+
.then((authStatus: PushAuthorizationStatus) => {
|
|
259
|
+
handleRegularOpen(authStatus);
|
|
260
|
+
})
|
|
261
|
+
.catch(() => {
|
|
262
|
+
handleRegularOpen('authorized');
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Cleanup on unmount:
|
|
268
|
+
return () => subscription.remove();
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
#### 3. Optional: Register FCM token (Android)
|
|
272
|
+
|
|
273
|
+
If your app uses Firebase Cloud Messaging and you have an FCM token, register it with the Attentive backend and then call `handleRegularOpen` in the callback (same pattern as iOS):
|
|
274
|
+
|
|
275
|
+
```typescript
|
|
276
|
+
import { registerDeviceTokenWithCallback, handleRegularOpen } from 'attentive-react-native-sdk';
|
|
277
|
+
|
|
278
|
+
// When you receive the FCM token (e.g. from Firebase Messaging):
|
|
279
|
+
getPushAuthorizationStatus().then((authStatus) => {
|
|
280
|
+
registerDeviceTokenWithCallback(
|
|
281
|
+
fcmToken,
|
|
282
|
+
authStatus,
|
|
283
|
+
(data, url, response, error) => {
|
|
284
|
+
if (error) {
|
|
285
|
+
console.error('Attentive token registration failed', error);
|
|
286
|
+
}
|
|
287
|
+
handleRegularOpen(authStatus);
|
|
288
|
+
}
|
|
289
|
+
);
|
|
290
|
+
});
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
#### 4. Optional: Handle notification opens and foreground (Android)
|
|
294
|
+
|
|
295
|
+
If you handle FCM messages (e.g. with `@react-native-firebase/messaging`), you can report notification opens and foreground receives the same way as on iOS:
|
|
296
|
+
|
|
297
|
+
- **User opened notification (background/inactive):** `handlePushOpen(payload, authorizationStatus)`
|
|
298
|
+
- **Notification received while app in foreground:** `handleForegroundPush(payload, authorizationStatus)`
|
|
299
|
+
|
|
300
|
+
Get `authorizationStatus` via `getPushAuthorizationStatus()` when handling the event.
|
|
301
|
+
|
|
302
|
+
#### Complete Android flow (reference)
|
|
303
|
+
|
|
304
|
+
The [Bonni](https://github.com/attentive-mobile/attentive-react-native-sdk/tree/main/Bonni) example app ([App.tsx](https://github.com/attentive-mobile/attentive-react-native-sdk/blob/main/Bonni/App.tsx)) implements the full flow:
|
|
305
|
+
|
|
306
|
+
1. **Launch:** `initialize` → `identify` → (Android) `getPushAuthorizationStatus()` → `handleRegularOpen(authStatus)` → `registerForPushNotifications()`.
|
|
307
|
+
2. **Foreground:** `AppState.addEventListener('change', …)` → when `active` and Android → `getPushAuthorizationStatus()` → `handleRegularOpen(authStatus)`.
|
|
308
|
+
3. **Optional:** When FCM token is available → `registerDeviceTokenWithCallback(token, authStatus, callback)` → in callback call `handleRegularOpen(authStatus)`.
|
|
309
|
+
4. **Optional:** When user opens a notification or receives one in foreground → `handlePushOpen` / `handleForegroundPush` with payload and status from `getPushAuthorizationStatus()`.
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
#### Request Push Permission (iOS)
|
|
177
314
|
|
|
178
315
|
```typescript
|
|
179
316
|
import { registerForPushNotifications } from 'attentive-react-native-sdk';
|
|
@@ -183,9 +320,9 @@ import { registerForPushNotifications } from 'attentive-react-native-sdk';
|
|
|
183
320
|
registerForPushNotifications();
|
|
184
321
|
```
|
|
185
322
|
|
|
186
|
-
#### Register Device Token
|
|
323
|
+
#### Register Device Token (iOS: APNs / Android: FCM)
|
|
187
324
|
|
|
188
|
-
When your app receives a device token
|
|
325
|
+
When your app receives a device token (APNs on iOS, FCM on Android), register it with the Attentive backend:
|
|
189
326
|
|
|
190
327
|
```typescript
|
|
191
328
|
import { registerDeviceToken } from 'attentive-react-native-sdk';
|
|
@@ -202,7 +339,7 @@ The `authorizationStatus` parameter should be one of:
|
|
|
202
339
|
- `'provisional'` - Provisional authorization (quiet notifications)
|
|
203
340
|
- `'ephemeral'` - App Clip notifications
|
|
204
341
|
|
|
205
|
-
#### Handle Push Notification Opens
|
|
342
|
+
#### Handle Push Notification Opens (iOS and Android)
|
|
206
343
|
|
|
207
344
|
When a user taps on a push notification, track the event:
|
|
208
345
|
|
|
@@ -218,7 +355,7 @@ handlePushOpened(
|
|
|
218
355
|
);
|
|
219
356
|
```
|
|
220
357
|
|
|
221
|
-
#### Handle Foreground Notifications
|
|
358
|
+
#### Handle Foreground Notifications (iOS and Android)
|
|
222
359
|
|
|
223
360
|
When a notification arrives while the app is in the foreground:
|
|
224
361
|
|
|
@@ -285,6 +422,4 @@ func application(
|
|
|
285
422
|
- [Push Notifications Setup](./PUSH_NOTIFICATIONS_SETUP.md) - General push notification setup
|
|
286
423
|
- [iOS Native SDK documentation](https://github.com/attentive-mobile/attentive-ios-sdk) - Native SDK reference
|
|
287
424
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
Android push notification support is not yet implemented. The push notification methods will be no-ops on Android. FCM (Firebase Cloud Messaging) integration is planned for a future release.
|
|
425
|
+
For a full Android implementation (app launch, foreground, permission, and optional FCM), see the **[App Events on Android](#app-events-on-android)** section above.
|
package/android/build.gradle
CHANGED
|
@@ -85,6 +85,10 @@ dependencies {
|
|
|
85
85
|
implementation 'com.attentive:attentive-android-sdk:1.0.1'
|
|
86
86
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.9.10"
|
|
87
87
|
implementation(platform("org.jetbrains.kotlin:kotlin-bom:1.9.10"))
|
|
88
|
+
|
|
89
|
+
// OkHttp for networking debugging (optional, only used when debugging is enabled)
|
|
90
|
+
implementation "com.squareup.okhttp3:okhttp:4.12.0"
|
|
91
|
+
implementation "com.squareup.okhttp3:logging-interceptor:4.12.0"
|
|
88
92
|
}
|
|
89
93
|
|
|
90
94
|
if (isNewArchitectureEnabled()) {
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
2
|
package="com.attentivereactnativesdk">
|
|
3
3
|
|
|
4
|
+
<!-- Required for push notification permission prompt on Android 13+ (API 33+) -->
|
|
5
|
+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
|
4
6
|
</manifest>
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
package com.attentivereactnativesdk
|
|
2
|
+
|
|
3
|
+
import android.app.Activity
|
|
4
|
+
import android.content.Context
|
|
5
|
+
import android.content.pm.PackageManager
|
|
6
|
+
import android.os.Build
|
|
7
|
+
import android.util.Log
|
|
8
|
+
import androidx.core.app.ActivityCompat
|
|
9
|
+
import androidx.core.content.ContextCompat
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Android push notification permission helper for the Attentive SDK.
|
|
13
|
+
*
|
|
14
|
+
* On Android 13+ (API 33+), push notifications require the runtime permission
|
|
15
|
+
* [android.permission.POST_NOTIFICATIONS]. On older versions, notifications
|
|
16
|
+
* are allowed by default (no runtime permission).
|
|
17
|
+
*
|
|
18
|
+
* This helper provides:
|
|
19
|
+
* - [getAuthorizationStatus] – current permission status for parity with iOS
|
|
20
|
+
* - [requestPermission] – request POST_NOTIFICATIONS when needed (used by registerForPushNotifications)
|
|
21
|
+
*/
|
|
22
|
+
object AttentivePushHelper {
|
|
23
|
+
|
|
24
|
+
private const val TAG = "AttentivePushHelper"
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Authorization status values aligned with iOS push authorization for use in handleRegularOpen etc.
|
|
28
|
+
* - "authorized" – user has granted notification permission (or API < 33)
|
|
29
|
+
* - "denied" – user denied or permission not granted
|
|
30
|
+
* - "notDetermined" – not yet requested (API 33+ only)
|
|
31
|
+
*/
|
|
32
|
+
const val STATUS_AUTHORIZED = "authorized"
|
|
33
|
+
const val STATUS_DENIED = "denied"
|
|
34
|
+
const val STATUS_NOT_DETERMINED = "notDetermined"
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Returns the current push notification authorization status.
|
|
38
|
+
*
|
|
39
|
+
* On API 33+: uses [android.permission.POST_NOTIFICATIONS].
|
|
40
|
+
* On API < 33: returns [STATUS_AUTHORIZED] (no runtime permission required).
|
|
41
|
+
*
|
|
42
|
+
* @param context Application or Activity context
|
|
43
|
+
* @return One of [STATUS_AUTHORIZED], [STATUS_DENIED], or [STATUS_NOT_DETERMINED]
|
|
44
|
+
*/
|
|
45
|
+
@JvmStatic
|
|
46
|
+
fun getAuthorizationStatus(context: Context): String {
|
|
47
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
|
48
|
+
// API 32 and below: notification permission not required at runtime
|
|
49
|
+
return STATUS_AUTHORIZED
|
|
50
|
+
}
|
|
51
|
+
return when (ContextCompat.checkSelfPermission(context, android.Manifest.permission.POST_NOTIFICATIONS)) {
|
|
52
|
+
PackageManager.PERMISSION_GRANTED -> STATUS_AUTHORIZED
|
|
53
|
+
else -> {
|
|
54
|
+
// Not granted. Without an Activity we cannot distinguish "denied" vs "not yet asked".
|
|
55
|
+
// Return notDetermined so the app can call registerForPushNotifications to request.
|
|
56
|
+
STATUS_NOT_DETERMINED
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Requests the push notification permission (POST_NOTIFICATIONS) if needed.
|
|
63
|
+
* Must be called from an [Activity] (e.g. [reactApplicationContext.currentActivity]).
|
|
64
|
+
*
|
|
65
|
+
* On API < 33 this is a no-op and returns immediately.
|
|
66
|
+
*
|
|
67
|
+
* @param activity Current activity (required for requestPermissions)
|
|
68
|
+
* @param requestCode Request code for [Activity.onRequestPermissionsResult]
|
|
69
|
+
* @return true if the permission request was started or already granted, false if activity is null or permission not applicable
|
|
70
|
+
*/
|
|
71
|
+
@JvmStatic
|
|
72
|
+
fun requestPermissionIfNeeded(activity: Activity?, requestCode: Int): Boolean {
|
|
73
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
|
74
|
+
Log.d(TAG, "requestPermissionIfNeeded: API < 33, no runtime permission needed")
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
if (activity == null) {
|
|
78
|
+
Log.w(TAG, "requestPermissionIfNeeded: activity is null, cannot request permission")
|
|
79
|
+
return false
|
|
80
|
+
}
|
|
81
|
+
if (ContextCompat.checkSelfPermission(activity, android.Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) {
|
|
82
|
+
Log.d(TAG, "requestPermissionIfNeeded: POST_NOTIFICATIONS already granted")
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
Log.i(TAG, "requestPermissionIfNeeded: requesting POST_NOTIFICATIONS")
|
|
86
|
+
ActivityCompat.requestPermissions(
|
|
87
|
+
activity,
|
|
88
|
+
arrayOf(android.Manifest.permission.POST_NOTIFICATIONS),
|
|
89
|
+
requestCode
|
|
90
|
+
)
|
|
91
|
+
return true
|
|
92
|
+
}
|
|
93
|
+
}
|