appambit-push-notifications 0.3.0 → 1.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 (28) hide show
  1. package/AppAmbitSdkPushNotifications.podspec +14 -4
  2. package/README.md +184 -105
  3. package/android/build.gradle +21 -3
  4. package/android/src/main/AndroidManifest.xml +107 -1
  5. package/android/src/main/java/com/appambitpushnotifications/AppAmbitContextHolder.kt +22 -0
  6. package/android/src/main/java/com/appambitpushnotifications/AppAmbitHeadlessService.kt +177 -0
  7. package/android/src/main/java/com/appambitpushnotifications/AppAmbitInitProvider.kt +73 -0
  8. package/android/src/main/java/com/appambitpushnotifications/AppAmbitMessagingService.kt +12 -0
  9. package/android/src/main/java/com/appambitpushnotifications/AppAmbitNotificationSerializer.kt +88 -0
  10. package/android/src/main/java/com/appambitpushnotifications/AppAmbitPayloadUtils.kt +59 -0
  11. package/android/src/main/java/com/appambitpushnotifications/AppAmbitPushEventEmitter.kt +100 -0
  12. package/android/src/main/java/com/appambitpushnotifications/AppAmbitRNServiceExtension.kt +75 -0
  13. package/android/src/main/java/com/appambitpushnotifications/AppAmbitRemoteMessageStore.kt +26 -0
  14. package/android/src/main/java/com/appambitpushnotifications/AppambitPushNotificationsModule.kt +377 -76
  15. package/ios/AppAmbitNotificationSwizzler.m +290 -0
  16. package/ios/AppAmbitPushWrapper.swift +165 -25
  17. package/ios/AppAmbitRNNotificationService.swift +46 -0
  18. package/ios/AppambitPushNotifications.mm +264 -10
  19. package/lib/module/NativeAppambitPushNotifications.js.map +1 -1
  20. package/lib/module/index.js +46 -10
  21. package/lib/module/index.js.map +1 -1
  22. package/lib/typescript/src/NativeAppambitPushNotifications.d.ts +2 -1
  23. package/lib/typescript/src/NativeAppambitPushNotifications.d.ts.map +1 -1
  24. package/lib/typescript/src/index.d.ts +32 -6
  25. package/lib/typescript/src/index.d.ts.map +1 -1
  26. package/package.json +1 -1
  27. package/src/NativeAppambitPushNotifications.ts +7 -1
  28. package/src/index.tsx +93 -20
@@ -13,10 +13,20 @@ Pod::Spec.new do |s|
13
13
  s.platforms = { :ios => min_ios_version_supported }
14
14
  s.source = { :git => "https://github.com/AppAmbit/appambit-sdk-react-native.git", :tag => "#{s.version}" }
15
15
 
16
- s.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
17
- s.private_header_files = "ios/**/*.h"
16
+ s.default_subspec = 'Core'
18
17
 
19
- s.dependency 'AppAmbitPushNotifications', '~> 0.5.0'
18
+ s.subspec 'Core' do |core|
19
+ core.source_files = "ios/**/*.{h,m,mm,swift,cpp}"
20
+ core.exclude_files = "ios/AppAmbitRNNotificationService.swift"
21
+ core.private_header_files = "ios/**/*.h"
22
+ core.frameworks = 'Network'
23
+ core.dependency 'AppAmbitPushNotifications', '~> 1.0.0'
24
+ core.dependency 'AppAmbitSdk'
25
+ install_modules_dependencies(core)
26
+ end
20
27
 
21
- install_modules_dependencies(s)
28
+ s.subspec 'Extension' do |ext|
29
+ ext.source_files = "ios/AppAmbitRNNotificationService.swift"
30
+ ext.dependency 'AppAmbitPushNotifications', '~> 1.0.0'
31
+ end
22
32
  end
package/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Seamlessly integrate push notifications with your AppAmbit analytics.**
4
4
 
5
- This SDK is an extension of the core AppAmbit Android SDK, providing a simple and powerful way to handle Firebase Cloud Messaging (FCM) notifications.
5
+ This SDK is an extension of the core AppAmbit SDK, providing a simple and powerful way to handle Firebase Cloud Messaging (FCM) notifications on both Android and iOS.
6
6
 
7
7
  ---
8
8
 
@@ -13,203 +13,282 @@ This SDK is an extension of the core AppAmbit Android SDK, providing a simple an
13
13
  * [Install](#install)
14
14
  * [Quickstart](#quickstart)
15
15
  * [Usage](#usage)
16
- * [Customization](#customization)
16
+ * [Native Implementation Setup](#native-implementation-setup)
17
+ * [Android Setup](#android-setup)
18
+ * [iOS Setup](#ios-setup)
19
+ * [iOS Notification Service Extension (Rich Notifications)](#ios-notification-service-extension-rich-notifications)
17
20
 
18
21
  ---
19
22
 
20
23
  ## Features
21
24
 
25
+ * **Zero-Config iOS**: No AppDelegate changes required — the SDK wires itself up automatically via method swizzling.
22
26
  * **Simple Setup**: Integrates in minutes.
23
27
  * **Enable/Disable Notifications**: Easily manage user preferences at both the business and FCM level.
24
- * **Automatic Field Handling**: Automatically uses standard fields from the FCM payload like `color`, `icon`, `channel_id`, and `click_action`.
25
- * **Smart Icon Selection**: Automatically uses your app's icon, with a safe fallback.
26
- * **Advanced Customization**: Provides a powerful hook to modify notifications for advanced use cases.
27
- * **Permission Helper**: Includes a simple utility to request the `POST_NOTIFICATIONS` permission.
28
+ * **Robust Event Listeners**: Separate callbacks for Foreground and Opened (tapped) notifications on both platforms, Background listener is Android-only.
29
+ * **Android Headless JS Support**: Handle background notifications via React Native Headless JS tasks even when the app is completely closed.
30
+ * **Automatic Field Handling**: Automatically uses standard FCM payload fields like `color`, `icon`, `channel_id`, `click_action`, and rich images.
31
+ * **Rich Media Support**: Full iOS Notification Service Extension support for rich payloads, badges, and media attachments.
32
+ * **Permission Helpers**: Utilities to request and check the `POST_NOTIFICATIONS` permission.
28
33
 
29
34
  ---
30
35
 
31
36
  ## Requirements
32
37
 
33
- * **AppAmbit Core SDK**: This SDK is an extension and requires the core `appambit-sdk` to be installed and configured.
34
- * **Firebase Project**: A configured Firebase project and a `google-services.json` file in your application module.
35
- * Android API level 21 (Lollipop) or newer.
38
+ * **AppAmbit Core SDK**: Requires the core `appambit` SDK to be installed and configured.
39
+ * **Firebase Project**: A configured Firebase project with `google-services.json` (Android) and `GoogleService-Info.plist` (iOS) in your application.
40
+ * **OS Versions**: Android API level 21 (Lollipop) or newer / iOS 13.0 or newer.
36
41
 
37
42
  ---
38
43
 
39
44
  ## Install
40
- To install the library from NPM, run the following commands in your project directory:
41
45
 
42
46
  ```bash
43
47
  npm install appambit
44
- &
45
48
  npm install appambit-push-notifications
46
49
  ```
47
50
 
48
- Add the following dependencies to your app's `build.gradle` file. Your app is still responsible for providing the Firebase Bill of Materials (BOM) and Firebase Messaging to ensure version compatibility.
51
+ ### Android Dependencies
49
52
 
50
- **Kotlin DSL**
53
+ Add the following to your Gradle files. Your app is responsible for providing the Firebase BOM and Firebase Messaging to ensure version compatibility.
51
54
 
52
- **`android/app/build.gradle.kts`**
55
+ **`android/app/build.gradle`** (Groovy)
53
56
  ```groovy
54
57
  apply plugin: "com.google.gms.google-services"
55
58
 
56
59
  dependencies {
57
- // The Firebase BOM and Messaging are required to align Firebase library versions.
58
- implementation(platform("com.google.firebase:firebase-bom:33.1.2"))
59
- implementation ("com.google.firebase:firebase-messaging:23.4.0")
60
+ implementation platform('com.google.firebase:firebase-bom:33.1.2')
61
+ implementation 'com.google.firebase:firebase-messaging:23.4.0'
60
62
  }
61
63
  ```
62
- **`android/build.gradle.kts`**
64
+
65
+ **`android/build.gradle`** (Groovy)
63
66
  ```groovy
64
67
  dependencies {
65
68
  classpath("com.google.gms:google-services:4.3.15")
66
69
  }
67
70
  ```
68
71
 
69
- **Groovy**
70
- **`android/app/build.gradle`**
71
- ```groovy
72
- apply plugin: "com.google.gms.google-services"
72
+ <details>
73
+ <summary>Kotlin DSL</summary>
74
+
75
+ **`android/app/build.gradle.kts`**
76
+ ```kotlin
77
+ apply(plugin = "com.google.gms.google-services")
73
78
 
74
79
  dependencies {
75
- // The Firebase BOM and Messaging are required to align Firebase library versions.
76
- implementation platform('com.google.firebase:firebase-bom:33.1.2')
77
- implementation 'com.google.firebase:firebase-messaging:23.4.0'
80
+ implementation(platform("com.google.firebase:firebase-bom:33.1.2"))
81
+ implementation("com.google.firebase:firebase-messaging:23.4.0")
78
82
  }
79
83
  ```
80
84
 
81
- **`android/build.gradle`**
82
- ```groovy
85
+ **`android/build.gradle.kts`**
86
+ ```kotlin
83
87
  dependencies {
84
88
  classpath("com.google.gms:google-services:4.3.15")
85
89
  }
86
90
  ```
91
+ </details>
87
92
 
88
- Also, ensure you have the Google Services plugin configured in your project.
93
+ ### iOS Dependencies
94
+
95
+ After installing the npm package, run:
96
+
97
+ ```bash
98
+ cd ios && pod install
99
+ ```
89
100
 
90
101
  ---
91
102
 
92
103
  ## Quickstart
93
104
 
94
- **Import the SDKs**: In your `App.tsx` file, import both the AppAmbit SDK and the Push Notifications SDK.
105
+ In your `App.tsx` (or application entry point), initialize both SDKs in order:
95
106
 
96
107
  ```javascript
97
- import * as AppAmbit from "appambit";
98
- ```
99
- ```javascript
100
- import * as PushNotifications from "appambit-push-notifications";
108
+ import * as AppAmbit from "appambit";
109
+ import * as PushNotifications from "appambit-push-notifications";
110
+
111
+ AppAmbit.start("<YOUR-APPKEY>");
112
+ PushNotifications.start();
113
+ PushNotifications.requestNotificationPermission();
101
114
  ```
102
115
 
116
+ ---
117
+
118
+ ## Usage
103
119
 
104
- 1. **Initialize the Core SDK**: In your `App.tsx` class, initialize the core AppAmbit SDK with your App Key.
120
+ ### Event Listeners
105
121
 
106
- ```javascript
107
- AppAmbit.start("<YOUR-APPKEY>");
108
- ```
122
+ All listeners hold a single subscription — calling them again silently replaces the previous one. Each returns a cleanup function to call on unmount (e.g. in `useEffect`'s return).
109
123
 
110
- 2. **Initialize the Push SDK**: Immediately after, start the Push Notifications SDK.
124
+ #### Foreground Listener
125
+ Fires when a notification arrives while the app is active and open.
111
126
 
112
- ```javascript
113
- PushNotifications.start();
114
- ```
127
+ ```javascript
128
+ useEffect(() => {
129
+ const unsubscribe = PushNotifications.setForegroundListener((payload) => {
130
+ console.log("Foreground notification:", payload);
131
+ });
132
+ return () => unsubscribe();
133
+ }, []);
134
+ ```
115
135
 
116
- 3. **Request Permissions**: In your main activity, request the required notification permission.
136
+ #### Background Listener
137
+ Fires when a notification arrives while the app is backgrounded or (on Android) killed.
117
138
 
118
- ```javascript
119
- PushNotifications.requestNotificationPermission();
120
- ```
139
+ > **Android only**: To handle notifications when the app is completely killed, you must also register a Headless JS task — see [Android Setup](#android-setup).
121
140
 
122
- **That's it!** Your app is now ready to receive and display push notifications.
141
+ ```javascript
142
+ useEffect(() => {
143
+ const unsubscribe = PushNotifications.Android.setBackgroundListener(async (payload) => {
144
+ console.log("Background notification:", payload);
145
+ // The SDK automatically signals the OS when your Promise resolves
146
+ });
147
+ return () => unsubscribe();
148
+ }, []);
149
+ ```
123
150
 
124
- ---
151
+ #### Opened Listener
152
+ Fires when the user taps a notification. Works regardless of whether the app was in the foreground, background, or killed.
125
153
 
126
- ## Usage
154
+ ```javascript
155
+ useEffect(() => {
156
+ const unsubscribe = PushNotifications.setOpenedListener((payload) => {
157
+ console.log("Notification tapped:", payload);
158
+ });
159
+ return () => unsubscribe();
160
+ }, []);
161
+ ```
127
162
 
128
- ### Enabling and Disabling Notifications
163
+ ### Notification Payload
164
+
165
+ ```typescript
166
+ interface NotificationPayload {
167
+ title: string | null;
168
+ body: string | null;
169
+ imageUrl: string | null;
170
+ data: Record<string, string>;
171
+ android: {
172
+ color: string | null;
173
+ smallIconName: string | null;
174
+ ticker: string | null;
175
+ sticky: boolean | null;
176
+ visibility: string | null;
177
+ channelId: string | null;
178
+ tag: string | null;
179
+ sound: string | null;
180
+ clickAction: string | null;
181
+ } | null;
182
+ ios: {
183
+ badge: number | null;
184
+ sound: string | null;
185
+ category: string | null;
186
+ threadId: string | null;
187
+ } | null;
188
+ }
189
+ ```
129
190
 
130
- By default, notifications are enabled when you first call `start()`. To manage user preferences afterward, use `setNotificationsEnabled`.
191
+ ### Permission Helpers
131
192
 
132
193
  ```javascript
133
- // To disable all future notifications
134
- PushNotifications.setNotificationsEnabled(false)
194
+ // Fire-and-forget shows the system permission dialog
195
+ PushNotifications.requestNotificationPermission();
196
+
197
+ // Returns Promise<boolean> with the user's decision
198
+ const granted = await PushNotifications.requestNotificationPermissionWithResult();
135
199
 
136
- // To re-enable them
137
- PushNotifications.setNotificationsEnabled(true)
200
+ // Check current permission status without prompting the user
201
+ const hasPermission = await PushNotifications.hasNotificationPermission();
138
202
  ```
139
203
 
140
- This method updates the opt-out status on the AppAmbit dashboard and stops the device from receiving FCM messages. You can check the current setting at any time:
204
+ ### Enable / Disable Notifications
141
205
 
142
206
  ```javascript
143
- const isEnabled = PushNotifications.isNotificationsEnabled()
207
+ PushNotifications.setNotificationsEnabled(false); // opt out
208
+ PushNotifications.setNotificationsEnabled(true); // opt back in
209
+
210
+ const isEnabled = await PushNotifications.isNotificationsEnabled();
144
211
  ```
145
212
 
146
- ### Permission Listener (Optional)
213
+ ---
214
+
215
+ ## Native Implementation Setup
147
216
 
148
- To know if the user granted or denied the notification permission, you can provide an optional listener.
217
+ ### Android Setup
218
+
219
+ The SDK's `AndroidManifest.xml` automatically merges the required permissions (`POST_NOTIFICATIONS`, `RECEIVE_BOOT_COMPLETED`), `AppAmbitInitProvider`, and `AppAmbitHeadlessService` into your app — no manifest changes needed.
220
+
221
+ To handle notifications when the app is completely killed, register a Headless JS task in your `index.js`:
149
222
 
150
223
  ```javascript
151
- PushNotifications.requestNotificationPermissionWithResult().then(
152
- (granted: boolean) => {
153
- if(granted) {
154
- console.log("Notification permission granted");
155
- } else {
156
- console.log("Notification permission denied");
157
- }
224
+ import { AppRegistry, Platform } from 'react-native';
225
+ import * as PushNotifications from 'appambit-push-notifications';
226
+ import App from './src/App';
227
+ import { name as appName } from './app.json';
228
+
229
+ if (Platform.OS === 'android') {
230
+ AppRegistry.registerHeadlessTask(
231
+ PushNotifications.BACKGROUND_NOTIFICATION_TASK,
232
+ () => async (payload) => {
233
+ console.log('Background notification (killed state):', payload);
158
234
  }
159
- );
235
+ );
236
+ }
237
+
238
+ AppRegistry.registerComponent(appName, () => App);
160
239
  ```
161
240
 
162
- ---
241
+ > Use `AppRegistry.registerHeadlessTask` with `BACKGROUND_NOTIFICATION_TASK` — not `BackgroundFetch` or any other API.
163
242
 
164
- ## Customization
243
+ ---
165
244
 
166
- The SDK is designed to be highly customizable, automatically adapting to the data you send in your FCM payload, while also offering a powerful hook for advanced modifications.
245
+ ### iOS Setup
167
246
 
168
- ### Automatic Customization
247
+ #### Push Notifications Capability & APNs Entitlement
169
248
 
170
- The SDK automatically configures the notification by reading standard fields from your FCM message. **For most use cases, you won't need to write any custom code.**
249
+ Enable **Push Notifications** in Xcode under your target's *Signing & Capabilities* tab. This automatically injects the `aps-environment` entitlement into your `.entitlements` file.
171
250
 
172
- **`notification` object:**
251
+ If you manage `Runner.entitlements` manually (e.g. via version control or CI), make sure this key is present — without it APNs will reject device registration and no token will ever be delivered:
173
252
 
174
- The SDK uses the standard keys from the FCM `notification` object.
253
+ ```xml
254
+ <!-- ios/Runner/Runner.entitlements -->
255
+ <key>aps-environment</key>
256
+ <string>development</string> <!-- use "production" for App Store builds -->
257
+ ```
175
258
 
176
- - **`title`**: The notification's title.
177
- - **`body`**: The notification's main text.
259
+ ---
178
260
 
179
- **`data` object:**
261
+ ### iOS Notification Service Extension (Rich Notifications)
180
262
 
181
- The `data` object is a free-form container for any custom key-value pairs you wish to send (e.g., `{"your_key": "your_value", "another_key": 123}`). Its sole purpose is to pass custom data to your application, which you can then access using the `NotificationCustomizer` to implement any advanced logic you require.
263
+ To display rich notifications with image attachments, create a Notification Service Extension in Xcode.
182
264
 
183
- ### Advanced Customization with `NotificationCustomizer`
265
+ 1. **Create the extension**: Go to **File > New > Target**, select **Notification Service Extension**, and give it a name (e.g. `NotificationService`).
184
266
 
185
- The `data` payload is a **free-form key-value map**. You are not limited to any specific keys; you can send any data you need and use it to build your custom notification.
267
+ 2. **Add the pod** to your `Podfile` outside the main target:
268
+ ```ruby
269
+ target 'NotificationService' do
270
+ pod 'AppAmbitPushNotificationsExtension', '~> 1.0.0'
271
+ end
272
+ ```
273
+ Then run `pod install` under `ios/`.
186
274
 
187
- **Example: Building a Custom Notification**
275
+ 3. **Subclass `AppAmbitNotificationService`** in `NotificationService.swift`:
188
276
 
189
- The following example shows how to read custom fields from the `data` payload to add a custom action button. This is just one of many possibilities.
277
+ ```swift
278
+ import UserNotifications
279
+ import AppAmbitPushNotificationsExtension
190
280
 
191
- 1. **Send any custom data** you need. The keys and values are completely up to you. For example:
281
+ class NotificationService: AppAmbitNotificationService {
282
+ override func didReceive(
283
+ _ request: UNNotificationRequest,
284
+ withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void
285
+ ) {
286
+ // Base class handles rich media download and attachment automatically.
287
+ super.didReceive(request, withContentHandler: contentHandler)
288
+ }
192
289
 
193
- ```json
194
- {
195
- "title": "New Message",
196
- "body": "You have a new message from a friend.",
197
- "data": {
198
- "key1": "Mark as Read",
199
- "key2": "MARK_AS_READ_ACTION",
200
- "any_other_key": "any_value"
201
- }
202
- }
203
- ```
204
-
205
- 2. **Register the `NotificationCustomizer`** and use your custom keys:
206
-
207
- ```javascript
208
- PushNotifications.setNotificationCustomizer((payload: PushNotifications.NotificationPayload) => {
209
- console.log("Payload:", payload);
210
- console.log("Data:", payload.data);
211
- console.log("Title:", payload.title);
212
- console.log("Body:", payload.body);
213
- });
214
- PushNotifications.start();
215
- ```
290
+ override func serviceExtensionTimeWillExpire() {
291
+ super.serviceExtensionTimeWillExpire()
292
+ }
293
+ }
294
+ ```
@@ -15,10 +15,8 @@ buildscript {
15
15
  }
16
16
  }
17
17
 
18
-
19
18
  apply plugin: "com.android.library"
20
19
  apply plugin: "kotlin-android"
21
-
22
20
  apply plugin: "com.facebook.react"
23
21
 
24
22
  def getExtOrIntegerDefault(name) {
@@ -72,7 +70,27 @@ repositories {
72
70
  def kotlin_version = getExtOrDefault("kotlinVersion")
73
71
 
74
72
  dependencies {
75
- implementation "com.appambit:appambit.push.notifications:0.5.0"
73
+ // ─── Remote Push SDK Dependency ─────────────────────────────────────────────
74
+ // 'api' (not 'implementation') is required so that classes like PushNotifications
75
+ // are visible at compile time to the consuming app (e.g. MainActivity.kt).
76
+ api 'com.appambit:appambit.push.notifications:1.0.0'
77
+
78
+ // ─── Core SDK (compile-time only) ───────────────────────────────────────────
79
+ // The Push SDK depends on the AppAmbit Core SDK. We reference Core's `AppAmbit`
80
+ // class (e.g. AppAmbit.isInitialized()) at compile time, but it is provided at
81
+ // runtime by the consuming app's Core SDK dependency, so use `compileOnly` to
82
+ // avoid bundling a second copy and risking duplicate-class conflicts.
83
+ compileOnly 'com.appambit:appambit:1.0.0'
84
+
85
+ // ─── Firebase (required transitively by the push AAR) ──────────────────────
86
+ implementation platform('com.google.firebase:firebase-bom:33.6.0')
87
+ implementation 'com.google.firebase:firebase-messaging'
88
+
89
+ // ─── AndroidX transitive deps ───────────────────────────────────────────────
90
+ implementation 'androidx.appcompat:appcompat:1.7.0'
91
+ implementation 'androidx.activity:activity:1.9.0'
92
+
93
+ // ─── React Native ─────────────────────────────────────────────────────────────
76
94
  implementation "com.facebook.react:react-android"
77
95
  implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
78
96
  }
@@ -1,2 +1,108 @@
1
- <manifest xmlns:android="http://schemas.android.com/apk/res/android">
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <manifest xmlns:android="http://schemas.android.com/apk/res/android"
3
+ xmlns:tools="http://schemas.android.com/tools">
4
+
5
+ <!--
6
+ AppAmbit Push Notifications — React Native Bridge Manifest
7
+
8
+ This manifest is MERGED into the consuming application's manifest.
9
+ It registers:
10
+ 1. AppAmbitInitProvider — ContentProvider that seeds AppAmbitContextHolder
11
+ before Application.onCreate() runs, enabling the SDK
12
+ to function even when the process is started solely
13
+ to handle an FCM message (killed state).
14
+ 2. AppAmbitMessagingService — Subclasses com.appambit.sdk.MessagingService to
15
+ capture the full RemoteMessage (including all
16
+ RemoteMessage.Notification fields) before the AppAmbit
17
+ SDK strips them into its own AppAmbitNotification model.
18
+ 3. AppAmbitHeadlessService — Starts Headless JS for background/killed notifications.
19
+ 4. NotificationServiceExtension meta-data — tells MessagingService which extension class to load.
20
+
21
+ The consuming app must also declare:
22
+ - INTERNET permission
23
+ - POST_NOTIFICATIONS permission (Android 13+)
24
+ - google-services plugin and google-services.json
25
+ -->
26
+
27
+ <!-- ── Permissions (merged into host app) ───────────────────────────── -->
28
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
29
+ <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
30
+
31
+ <application>
32
+
33
+ <!--
34
+ AppAmbitInitProvider
35
+ Initialises AppAmbitContextHolder at the very first moment of process startup,
36
+ before Application.onCreate(). This is required so that the Headless JS service
37
+ can obtain a valid Context even in killed state (when the React Native bridge
38
+ has not started yet).
39
+
40
+ authorities uses ${applicationId} to avoid clashes between apps.
41
+ initOrder="100" makes it run before other providers with lower values.
42
+ -->
43
+ <provider
44
+ android:name="com.appambitpushnotifications.AppAmbitInitProvider"
45
+ android:authorities="${applicationId}.appambit-init-provider"
46
+ android:exported="false"
47
+ android:initOrder="100" />
48
+
49
+ <!--
50
+ Remove the base AppAmbit SDK's FirebaseMessagingService declaration so our
51
+ subclass (AppAmbitMessagingService) is the only handler for FCM events.
52
+ This library has higher manifest-merge priority than the AppAmbit SDK AAR,
53
+ so tools:node="remove" takes effect before the final manifest is assembled.
54
+ -->
55
+ <service
56
+ android:name="com.appambit.sdk.MessagingService"
57
+ tools:node="remove" />
58
+
59
+ <!--
60
+ AppAmbitMessagingService
61
+ Extends com.appambit.sdk.MessagingService (which extends FirebaseMessagingService).
62
+ Captures the full RemoteMessage in onMessageReceived, stores it in
63
+ AppAmbitRemoteMessageStore, then delegates to the AppAmbit SDK via super.onMessageReceived().
64
+
65
+ AppAmbitNotificationSerializer and AppAmbitHeadlessService read from the store
66
+ to populate notification fields not surfaced by AppAmbitNotification (e.g.
67
+ channelId, sound, tag, ticker, visibility, notificationPriority, vibrateTimings, etc.).
68
+ -->
69
+ <service
70
+ android:name="com.appambitpushnotifications.AppAmbitMessagingService"
71
+ android:exported="false">
72
+ <intent-filter>
73
+ <action android:name="com.google.firebase.MESSAGING_EVENT" />
74
+ </intent-filter>
75
+ </service>
76
+
77
+ <!--
78
+ AppAmbitHeadlessService
79
+ Runs the registered Headless JS task ("AppAmbitBackgroundNotification")
80
+ when a push notification arrives in background / killed state.
81
+
82
+ exported="false" — only started internally, not from other apps.
83
+
84
+ Note: we do NOT declare android:foregroundServiceType because
85
+ HeadlessJsTaskService does not require foreground-service privileges.
86
+ Starting it with startService() is sufficient and avoids the strict
87
+ ForegroundServiceStartNotAllowedException on Android 12+ (API 31+).
88
+ -->
89
+ <service
90
+ android:name="com.appambitpushnotifications.AppAmbitHeadlessService"
91
+ android:exported="false" />
92
+
93
+ <!--
94
+ NotificationServiceExtension meta-data
95
+ Tells MessagingService which class to reflectively load as the
96
+ IAppAmbitNotificationServiceExtension implementation.
97
+
98
+ If the consuming app provides its OWN extension, they must
99
+ override this meta-data entry in their app's AndroidManifest.xml
100
+ with tools:replace="android:value".
101
+ -->
102
+ <meta-data
103
+ android:name="com.appambit.sdk.NotificationServiceExtension"
104
+ android:value="com.appambitpushnotifications.AppAmbitRNServiceExtension" />
105
+
106
+ </application>
107
+
2
108
  </manifest>
@@ -0,0 +1,22 @@
1
+ package com.appambitpushnotifications
2
+
3
+ import android.content.Context
4
+ import com.facebook.react.bridge.ReactApplicationContext
5
+
6
+ /**
7
+ * Holds a reference to the Application context so that the
8
+ * [AppAmbitHeadlessService] can start itself even when no Activity is alive
9
+ * (killed state / background).
10
+ *
11
+ * Set once in [AppambitPushNotificationsModule.initialize] via the
12
+ * ReactApplicationContext, which outlives individual activities.
13
+ */
14
+ internal object AppAmbitContextHolder {
15
+ @Volatile
16
+ var applicationContext: Context? = null
17
+ private set
18
+
19
+ fun set(context: Context) {
20
+ applicationContext = context.applicationContext
21
+ }
22
+ }