diff --git a/.travis.yml b/.travis.yml
index 4909f83ca..0fe294a6f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,3 +1,3 @@
language: node_js
node_js:
- - "6"
+ - "7"
diff --git a/README.md b/README.md
index 406166feb..01754518b 100644
--- a/README.md
+++ b/README.md
@@ -4,23 +4,21 @@ Handle all the aspects of push notifications for your app, including remote and
**All the native iOS notifications features are supported!**
+_For information regarding proper integration with [react-native-navigation](https://github.com/wix/react-native-navigation), follow [this wiki](https://github.com/wix/react-native-notifications/wiki/Android:-working-with-RNN)._
+
-## Supported Features
### iOS
-- [Remote notifications](#handling-received-notifications).
-- [Local notifications](#triggering-local-notifications).
-- [Background notifications](#managed-notifications-ios-only).
-- [Managed notifications](#managed-notifications-ios-only) (notifications that can be cleared from the server, like Facebook messenger and Whatsapp web).
-- [PushKit API](#pushkit-api-ios-only) for VoIP and other background messages.
-- [Interactive notifications](#interactive--actionable-notifications-ios-only) that allows you to provide additional functionality to your users outside of your application.
+
-
+- Remote (push) notifications
+- Local notifications
+- Background/Managed notifications (notifications that can be cleared from the server, like Facebook messenger and Whatsapp web)
+- PushKit API (for VoIP and other background messages)
+- Interactive notifications (allows you to provide additional functionality to your users outside of your application such as action buttons)
### Android
->**Please advise that Android support is a work in progress and is subject to breaking changes in the near future**
-
- Receiving notifications in any App state (foreground, background, "dead")
- Built-in notification drawer management
- High degree of code extensibility to allow for advanced custom layouts and any specific notifications behavior as available by [Android's API](https://developer.android.com/training/notify-user/build-notification.html)
@@ -28,593 +26,16 @@ Handle all the aspects of push notifications for your app, including remote and
_Upcoming: local notifications, background-state Rx queue (iOS equivalent)_
-## Installation
-
-```
-$ npm install react-native-notifications --save
-```
-
-### iOS
-
-First, [Manully link](https://facebook.github.io/react-native/docs/linking-libraries-ios.html#manual-linking) the library to your Xcode project.
-
-Then, to enable notifications support add the following line at the top of your `AppDelegate.m`
-
-```objective-c
-#import "RNNotifications.h"
-```
-
-And the following methods to support registration and receiving notifications:
-
-```objective-c
-// Required to register for notifications
-- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
-{
- [RNNotifications didRegisterUserNotificationSettings:notificationSettings];
-}
-
-- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
-{
- [RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
-}
-
-// Required for the notification event.
-- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)notification {
- [RNNotifications didReceiveRemoteNotification:notification];
-}
-
-// Required for the localNotification event.
-- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
-{
- [RNNotifications didReceiveLocalNotification:notification];
-}
-```
-
-### Android
-
-
-Add a reference to the library's native code in your global `settings.gradle`:
-
-```gradle
-include ':reactnativenotifications'
-project(':reactnativenotifications').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-notifications/android')
-```
-
-Declare the library as a dependency in your **app-project's** `build.gradle`:
-
-```gradle
-dependencies {
- // ...
-
- compile project(':reactnativenotifications')
-}
-```
-
-Add the library to your `MainApplication.java`:
-
-```java
-import com.wix.reactnativenotifications.RNNotificationsPackage;
-
-...
-
- @Override
- protected List getPackages() {
- return Arrays.asList(
- new MainReactPackage(),
- ...
- new RNNotificationsPackage(MainApplication.this),
-```
-
-### Receiving push notifications
-
-> This section is only necessary in case you wish to **receive** push notifications in your React-Native app.
-
-Push notifications on Android are managed and dispatched using [Google's GCM service](https://developers.google.com/cloud-messaging/gcm) (now integrated into Firebase). The following installation steps are a TL;DR of [Google's GCM setup guide](https://developers.google.com/cloud-messaging/android/client). You can follow them to get GCM integrated quickly, but we recommend that you will in the very least have a peek at the guide's overview.
-
-#### Step #1: Subscribe to Google's GCM
-
-To set GCM in your app, you must first create a Google API-project and obtain a **Sender ID** and a **Server API Key**. If you have no existing API project yet, the easiest way to go about in creating one is using [this step-by-step installation process](https://developers.google.com/mobile/add); Use [this tutorial](https://code.tutsplus.com/tutorials/how-to-get-started-with-push-notifications-on-android--cms-25870) for insturctions.
-
-Alternatively, follow [Google's complete guide](https://developers.google.com/cloud-messaging/android/client#create-an-api-project).
-
-#### Step #2: Add Sender ID to Manifest File
-
-Once obtained, bundle the Sender ID onto your main `manifest.xml` file:
-
-```gradle
-
-...
-
- ...
- // Replace '1234567890' with your sender ID.
- // IMPORTANT: Leave the trailing \0 intact!!!
-
-
-
-
-```
-
-
----
-
-## Register to Push Notifications
-
-### iOS
-
-In order to handle notifications, you must register before- handle `remoteNotificationsRegistered` event.
-
-In your React Native app:
-
-```javascript
-import NotificationsIOS from 'react-native-notifications';
-
-class App extends Component {
- constructor() {
- NotificationsIOS.addEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
- NotificationsIOS.requestPermissions();
- }
-
- onPushRegistered(deviceToken) {
- console.log("Device Token Received", deviceToken);
- }
-
- componentWillUnmount() {
- // prevent memory leaks!
- NotificationsIOS.removeEventListener('remoteNotificationsRegistered', this.onPushRegistered.bind(this));
- }
-}
-
-```
-
-When you have the device token, POST it to your server and register the device in your notifications provider (Amazon SNS, Azure, etc.).
-
-### Android
-
-The React-Native code equivalent on Android is:
-
-```javascript
-import {NotificationsAndroid} from 'react-native-notifications';
-
-// On Android, we allow for only one (global) listener per each event type.
-NotificationsAndroid.setRegistrationTokenUpdateListener((deviceToken) => {
- console.log('Push-notifications regsitered!', deviceToken)
-});
-
-```
-
-`deviceToken` being the token used to identify the device on the GCM.
-
----
-
-
-## Handling Received Notifications
-
-### iOS
-
-When you receive a notification, the application can be in one of the following states:
-
-1. **Forground**- When the app in running and is used by the user right now. in this case, `notificationReceivedForeground` event will be fired.
-2. **Background**- When the app is running but in background state. in this case, `notificationReceivedBackground` event will be fired.
-3. **Notification Opened**- When you open the notifications from the notification center. in this case, `notificationOpened` event will be fired.
-
-Example:
-
-```javascript
-constructor() {
- NotificationsIOS.addEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this));
- NotificationsIOS.addEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground.bind(this));
- NotificationsIOS.addEventListener('notificationOpened', this.onNotificationOpened.bind(this));
-}
-
-onNotificationReceivedForeground(notification) {
- console.log("Notification Received - Foreground", notification);
-}
-
-onNotificationReceivedBackground(notification) {
- console.log("Notification Received - Background", notification);
-}
-
-onNotificationOpened(notification) {
- console.log("Notification opened by device user", notification);
-}
-
-componentWillUnmount() {
- // Don't forget to remove the event listeners to prevent memory leaks!
- NotificationsIOS.removeEventListener('notificationReceivedForeground', this.onNotificationReceivedForeground.bind(this));
- NotificationsIOS.removeEventListener('notificationReceivedBackground', this.onNotificationReceivedBackground.bind(this));
- NotificationsIOS.removeEventListener('notificationOpened', this.onNotificationOpened.bind(this));
-}
-```
-
-#### Notification Object
-When you receive a push notification, you'll get an instance of `IOSNotification` object, contains the following methods:
-
-- **`getMessage()`**- returns the notification's main message string.
-- **`getSound()`**- returns the sound string from the `aps` object.
-- **`getBadgeCount()`**- returns the badge count number from the `aps` object.
-- **`getCategory()`**- returns the category from the `aps` object (related to interactive notifications).
-- **`getData()`**- returns the data payload (additional info) of the notification.
-- **`getType()`**- returns `managed` for managed notifications, otherwise returns `regular`.
-
-#### Background Queue (Important!)
-When a push notification is opened but the app is not running, the application will be in a **cold launch** state, until the JS engine is up and ready to handle the notification.
-The application will collect the events (notifications, actions, etc.) that happend during the cold launch for you.
-
-When your app is ready (most of the time it's after the call to `requestPermissions()`), just call to `NotificationsIOS.consumeBackgroundQueue();` in order to consume the background queue. For more info see `index.ios.js` in the example app.
-
-### Android
-
-```javascript
-import {NotificationsAndroid} from 'react-native-notifications';
-
-// On Android, we allow for only one (global) listener per each event type.
-NotificationsAndroid.setNotificationReceivedListener((notification) => {
- console.log("Notification received on device", notification.getData());
-});
-NotificationsAndroid.setNotificationOpenedListener((notification) => {
- console.log("Notification opened by device user", notification.getData());
-});
-```
-
-#### Notification Object
-- **`getData()`**- content of the `data` section of the original message (sent to GCM).
-- **`getTitle()`**- Convenience for returning `data.title`.
-- **`getMessage()`**- Convenience for returning `data.body`.
-
----
-
-## Querying initial notification
-
-React-Native's [`PushNotificationsIOS.getInitialNotification()`](https://facebook.github.io/react-native/docs/pushnotificationios.html#getinitialnotification) allows for the async retrieval of the original notification used to open the App on iOS, but it has no equivalent implementation for Android.
-
-We provide a similar implementation on Android using `PendingNotifications.getInitialNotification()` which returns a promise:
-
-```javascript
-import {NotificationsAndroid, PendingNotifications} from 'react-native-notifications';
-
-PendingNotifications.getInitialNotification()
- .then((notification) => {
- console.log("Initial notification was:", (notification ? notification.getData() : 'N/A');
- })
- .catch((err) => console.error("getInitialNotifiation() failed", err));
-
-```
-
-> Notifications are considered 'initial' under the following terms:
-
-> - User tapped on a notification, _AND_ -
-> - App was either not running at all ("dead" state), _OR_ it existed in the background with **no running activities** associated with it.
-
-
-## Triggering Local Notifications
-
-### iOS
-
-You can manually trigger local notifications in your JS code, to be posted immediately or in the future.
-Triggering local notifications is fully compatible with React Native `PushNotificationsIOS` library.
-
-Example:
-
-```javascript
-let localNotification = NotificationsIOS.localNotification({
- alertBody: "Local notificiation!",
- alertTitle: "Local Notification Title",
- alertAction: "Click here to open",
- soundName: "chime.aiff",
- category: "SOME_CATEGORY",
- userInfo: { }
-});
-```
-
-Notification object contains:
-
-- **`fireDate`**- The date and time when the system should deliver the notification (optinal - default is immidiate dispatch).
-- `alertBody`- The message displayed in the notification alert.
-- `alertTitle`- The title of the notification, displayed in the notifications center.
-- `alertAction`- The "action" displayed beneath an actionable notification.
-- `soundName`- The sound played when the notification is fired (optional).
-- `category`- The category of this notification, required for [interactive notifications](#interactive--actionable-notifications-ios-only) (optional).
-- `userInfo`- An optional object containing additional notification data.
-
-### Android
-
-Much like on iOS, notifications can be triggered locally. The API to do so is a simplified version of the iOS equivalent that works more natually with the Android perception of push (remote) notifications:
-
-```javascript
-NotificationsAndroid.localNotification({
- title: "Local notification",
- body: "This notification was generated by the app!",
- extra: "data"
-});
-```
-
-Upon notification opening (tapping by the device user), all data fields will be delivered as-is).
-
-### Cancel Local Notification
-The `NotificationsIOS.localNotification()` and `NotificationsAndroid.localNotification()` methods return unique `notificationId` values, which can be used in order to cancel specific local notifications. You can cancel local notification by calling `NotificationsIOS.cancelLocalNotification(notificationId)` or `NotificationsAndroid.cancelLocalNotification(notificationId)`.
-
-Example (iOS):
-
-```javascript
-let someLocalNotification = NotificationsIOS.localNotification({
- alertBody: "Local notificiation!",
- alertTitle: "Local Notification Title",
- alertAction: "Click here to open",
- soundName: "chime.aiff",
- category: "SOME_CATEGORY",
- userInfo: { }
-});
-
-NotificationsIOS.cancelLocalNotification(someLocalNotification);
-```
-
-### Cancel All Local Notifications (iOS-only!)
-
-```javascript
-NotificationsIOS.cancelAllLocalNotifications();
-```
-
----
-
-## Badge Counts
-
-Setting a badge is possible on iOS and Android. In addition to this retreiving the badge is possible on iOS but _not_ yet supported on Android.
-
-To add `getBadgeCount` support on Android or to read up on what particular devices are supported [check here](https://github.com/leolin310148/ShortcutBadger).
-
-### iOS
-```js
-NotificationsIOS.setBadgeCount(10);
-NotificationsIOS.getBadgeCount(function(count) {
- console.log('Badge count is', count); // 10
-});
-```
-
-### Android
-```js
-NotificationsAndroid.setBadgeCount(10);
-```
-
-
-## Managed Notifications (iOS only)
-
-Managed notifications are notifications that can be cleared by a server request.
-You can find this feature in facebook messenger, when you receive a message in your mobile, but open it in facebook web. More examples are Whatsapp web and gmail app.
-
-In order to handle managed notifications, your app must support background notifications, and the server should send the notifications you'd like to "manage" a bit differently. Let's start.
-
-First, enable the *Remote notifications* checkbox under **capabilities - Background Modes**:
-
-
-Then, add the following lines to `info.plist`:
-
-```xml
-UIBackgroundModes
-
- remote-notification
-
-```
-
-That's it for the client side!
-
-Now the server should push the notification a bit differently- background instead of reguler. You should also provide the action (`CREATE` notification or `CLEAR` notification), and `notificationId` as a unique identifier of the notification.
-
-**Regular** notification payload:
-
-```javascript
-{
- aps: {
- alert: {
- body: "This is regular notification"
- },
- badge: 5,
- sound: "chime.aiff",
- }
-}
-```
-
-**Managed** notification payload:
-
-```javascript
-{
- aps: {
- "content-available": 1
- },
- managedAps: {
- action: "CREATE", // set it to "CLEAR" in order to clear the notification remotely
- notificationId: "1234", // must be unique identifier
- sound: "chime.aiff",
- alert: {
- body: "This is managed notification"
- }
- }
-}
-```
-
----
-
-## PushKit API (iOS only)
-
-The PushKit framework provides the classes for your iOS apps to receive background pushes from remote servers. it has better support for background notifications compared to regular push notifications with `content-available: 1`. More info in [iOS PushKit documentation](https://developer.apple.com/library/ios/documentation/NetworkingInternet/Reference/PushKit_Framework/).
-
-### Register to PushKit
-After [preparing your app to receive VoIP push notifications](https://developer.apple.com/library/ios/documentation/Performance/Conceptual/EnergyGuide-iOS/OptimizeVoIP.html), add the following lines to `appDelegate.m` in order to support PushKit events:
-
-```objective-c
-#import "RNNotifications.h"
-#import
-```
-
-And the following methods:
-
-```objective-c
-// PushKit API Support
-- (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type
-{
- [RNNotifications didUpdatePushCredentials:credentials forType:type];
-}
-
-- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type
-{
- [RNNotifications didReceiveRemoteNotification:payload.dictionaryPayload];
-}
-```
-
-In your ReactNative code, add event handler for `pushKitRegistered` event and call to `registerPushKit()`:
-
-```javascript
-constructor() {
- NotificationsIOS.addEventListener('pushKitRegistered', this.onPushKitRegistered.bind(this));
- NotificationsIOS.registerPushKit();
-}
-
-onPushKitRegistered(deviceToken) {
- console.log("PushKit Token Received: " + deviceToken);
-}
-
-componentWillUnmount() {
- // Don't forget to remove the event listeners to prevent memory leaks!
- NotificationsIOS.removeEventListener('pushKitRegistered', onPushKitRegistered(this));
-}
-```
-
-> 1. Notice that PushKit device token and regular notifications device token are different, so you must handle two different tokens in the server side in order to support this feature.
-> 2. PushKit will not request permissions from the user for push notifications.
-
-
----
-
-## Interactive / Actionable Notifications
-
-> This section provides description for iOS. For notifications customization on Android, refer to [our wiki](https://github.com/wix/react-native-notifications/wiki/Android-Customizations#customizing-notifications-layout).
-
-Interactive notifications allow you to reply to a message right from the notification banner or take action right from the lock screen.
-
-On the Lock screen and within Notification Center, you swipe from right to left
-to reveal actions. Destructive actions, like trashing an email, are color-coded red. Relatively neutral actions, like dismissing an alert or declining an invitation, are color-coded gray.
-
-For banners, you pull down to reveal actions as buttons. For popups, the actions are immediately visible — the buttons are right there.
-
-You can find more info about interactive notifications [here](http://www.imore.com/interactive-notifications-ios-8-explained).
-
-
-
-
-Notification **actions** allow the user to interact with a given notification.
-
-Notification **categories** allow you to group multiple actions together, and to connect the actions with the push notification itself.
-
-In order to support interactive notifications, firstly add the following methods to `appDelegate.m` file:
-
-```objective-c
-// Required for the notification actions.
-- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
-{
- [RNNotifications handleActionWithIdentifier:identifier forLocalNotification:notification withResponseInfo:responseInfo completionHandler:completionHandler];
-}
-
-- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
-{
- [RNNotifications handleActionWithIdentifier:identifier forRemoteNotification:userInfo withResponseInfo:responseInfo completionHandler:completionHandler];
-}
-```
-
-Then, follow the basic workflow of adding interactive notifications to your app:
-
-1. Config the actions.
-2. Group actions together into categories.
-3. Register to push notifications with the configured categories.
-4. Push a notification (or trigger a [local](#triggering-local-notifications) one) with the configured category name.
-
-### Example
-#### Config the Actions
-We will config two actions: upvote and reply.
-
-```javascript
-import NotificationsIOS, { NotificationAction, NotificationCategory } from 'react-native-notifications';
-
-let upvoteAction = new NotificationAction({
- activationMode: "background",
- title: String.fromCodePoint(0x1F44D),
- identifier: "UPVOTE_ACTION"
-}, (action, completed) => {
- console.log("ACTION RECEIVED");
- console.log(JSON.stringify(action));
-
- // You must call to completed(), otherwise the action will not be triggered
- completed();
-});
-
-let replyAction = new NotificationAction({
- activationMode: "background",
- title: "Reply",
- behavior: "textInput",
- authenticationRequired: true,
- identifier: "REPLY_ACTION"
-}, (action, completed) => {
- console.log("ACTION RECEIVED");
- console.log(action);
-
- completed();
-});
-
-```
-
-#### Config the Category
-We will group `upvote` action and `reply` action into a single category: `EXAMPLE_CATEGORY `. If the notification contains `EXAMPLE_CATEGORY ` under `category` field, those actions will appear.
-
-```javascript
-let exampleCategory = new NotificationCategory({
- identifier: "EXAMPLE_CATEGORY",
- actions: [upvoteAction, replyAction],
- context: "default"
-});
-```
-
-#### Register to Push Notifications
-Instead of basic registration like we've done before, we will register the device to push notifications with the category we've just created.
-
-```javascript
-NotificationsIOS.requestPermissions([exampleCategory]);
-```
-
-#### Push an Interactive Notification
-Notification payload should look like this:
-
-```javascript
-{
- aps: {
- // ... (alert, sound, badge, etc)
- category: "EXAMPLE_CATEGORY"
- }
-}
-```
-
-The [example app](https://github.com/wix/react-native-notifications/tree/master/example) contains this interactive notification example, you can follow there.
-
-### `NotificationAction` Payload
-
-- `title` - Action button title.
-- `identifier` - Action identifier (must be unique).
-- `activationMode` - Indicating whether the app should activate to the foreground or background.
- - `foreground` (default) - Activate the app and put it in the foreground.
- - `background` - Activate the app and put it in the background. If the app is already in the foreground, it remains in the foreground.
-- `behavior` - Indicating additional behavior that the action supports.
- - `default` - No additional behavior.
- - `textInput` - When button is tapped, the action opens a text input. the text will be delivered to your action callback.
-- `destructive` - A Boolean value indicating whether the action is destructive. When the value of this property is `true`, the system displays the corresponding button differently to indicate that the action is destructive.
-- `authenticationRequired` - A Boolean value indicating whether the user must unlock the device before the action is performed.
-
-### `NotificationCategory` Payload
+# Table of Content
-- `identifier` - The name of the action group (must be unique).
-- `actions` - An array of `NotificationAction` objects, which related to this category.
-- `context` - Indicating the amount of space available for displaying actions in a notification.
- - `default` (default) - Displayes up to 4 actions (full UI).
- - `minimal` - Displays up tp 2 actions (minimal UI).
+- [Installation and setup](./docs/installation.md) - Setting up the library in your app
+- [Subscription](./docs/subscription.md) - Signing in to push notifications vendors (e.g. GCM)
+- [Notification Events (notfications core)](./docs/notificationsEvents.md) - Handling push notification arrival, notification opening by users
+- [Local notifications](./docs/localNotifications.md) - Manually triggering notifications (i.e. not via push)
+- [Advanced iOS topics](./docs/advancedIos.md) - e.g. managed notifications, PushKit API, Notifications actions
+- [Notifications layout control - Android (wiki page)](https://github.com/wix/react-native-notifications/wiki/Android:-Layout-Customization) - Learn how to fully customize your notifications layout on Android!
-
-## License
+# License
The MIT License.
See [LICENSE](LICENSE)
diff --git a/RNNotifications/RNNotifications.h b/RNNotifications/RNNotifications.h
index 10a2c46a2..0936c00bf 100644
--- a/RNNotifications/RNNotifications.h
+++ b/RNNotifications/RNNotifications.h
@@ -8,11 +8,28 @@
#import
#endif
+#if __has_include()
+#import
+#elif __has_include("React/RCTConvert.h")
+#import "React/RCTConvert.h"
+#else
+#import "RCTConvert.h"
+#endif
+
+@interface RCTConvert (UILocalNotification)
++ (UILocalNotification *)UILocalNotification:(id)json;
+@end
+
+@interface RCTConvert (UIBackgroundFetchResult)
++(UIBackgroundFetchResult *)UIBackgroundFetchResult:(id)json;
+@end
+
@interface RNNotifications : NSObject
typedef void (^RCTRemoteNotificationCallback)(UIBackgroundFetchResult result);
+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;
++ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;
+ (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings;
+ (void)didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type;
diff --git a/RNNotifications/RNNotifications.m b/RNNotifications/RNNotifications.m
index 09880045f..864e92fd4 100644
--- a/RNNotifications/RNNotifications.m
+++ b/RNNotifications/RNNotifications.m
@@ -2,9 +2,12 @@
#import
#import
#import "RNNotifications.h"
-#import "RCTConvert.h"
-#import "RCTUtils.h"
+#import
+#import
#import "RNNotificationsBridgeQueue.h"
+#import
+
+#define SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] != NSOrderedAscending)
#if __has_include("RCTBridge.h")
#import "RCTBridge.h"
@@ -22,6 +25,7 @@
NSString* const RNNotificationClearAction = @"CLEAR";
NSString* const RNNotificationsRegistered = @"RNNotificationsRegistered";
+NSString* const RNNotificationsRegistrationFailed = @"RNNotificationsRegistrationFailed";
NSString* const RNPushKitRegistered = @"RNPushKitRegistered";
NSString* const RNNotificationReceivedForeground = @"RNNotificationReceivedForeground";
NSString* const RNNotificationReceivedBackground = @"RNNotificationReceivedBackground";
@@ -53,17 +57,6 @@ @implementation RCTConvert (UIUserNotificationActionBehavior)
}), UIUserNotificationActionBehaviorDefault, integerValue)
@end
-@implementation RCTConvert (UIBackgroundFetchResult)
-
-RCT_ENUM_CONVERTER(UIBackgroundFetchResult, (@{
- @"UIBackgroundFetchResultNewData": @(UIBackgroundFetchResultNewData),
- @"UIBackgroundFetchResultNoData": @(UIBackgroundFetchResultNoData),
- @"UIBackgroundFetchResultFailed": @(UIBackgroundFetchResultFailed),
- }), UIBackgroundFetchResultNoData, integerValue)
-
-@end
-
-
@implementation RCTConvert (UIMutableUserNotificationAction)
+ (UIMutableUserNotificationAction *)UIMutableUserNotificationAction:(id)json
{
@@ -101,27 +94,64 @@ + (UIMutableUserNotificationCategory *)UIMutableUserNotificationCategory:(id)jso
}
@end
-@implementation RCTConvert (UILocalNotification)
-+ (UILocalNotification *)UILocalNotification:(id)json
+@implementation RCTConvert (UNNotificationRequest)
++ (UNNotificationRequest *)UNNotificationRequest:(id)json withId:(NSString*)notificationId
{
NSDictionary *details = [self NSDictionary:json];
- UILocalNotification* notification = [UILocalNotification new];
- notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]];
- notification.alertBody = [RCTConvert NSString:details[@"alertBody"]];
- // alertTitle is a property on iOS 8.2 and above:
- if ([notification respondsToSelector:@selector(setAlertTitle:)]) {
- notification.alertTitle = [RCTConvert NSString:details[@"alertTitle"]];
+ UNMutableNotificationContent *content = [UNMutableNotificationContent new];
+ content.body = [RCTConvert NSString:details[@"alertBody"]];
+ content.title = [RCTConvert NSString:details[@"alertTitle"]];
+ content.sound = [RCTConvert NSString:details[@"soundName"]]
+ ? [UNNotificationSound soundNamed:[RCTConvert NSString:details[@"soundName"]]]
+ : [UNNotificationSound defaultSound];
+ if ([RCTConvert BOOL:details[@"silent"]]) {
+ content.sound = nil;
+ }
+ content.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{};
+ content.categoryIdentifier = [RCTConvert NSString:details[@"category"]];
+
+ NSDate *triggerDate = [RCTConvert NSDate:details[@"fireDate"]];
+ UNCalendarNotificationTrigger *trigger = nil;
+ if (triggerDate != nil) {
+ NSDateComponents *triggerDateComponents = [[NSCalendar currentCalendar]
+ components:NSCalendarUnitYear +
+ NSCalendarUnitMonth + NSCalendarUnitDay +
+ NSCalendarUnitHour + NSCalendarUnitMinute +
+ NSCalendarUnitSecond + NSCalendarUnitTimeZone
+ fromDate:triggerDate];
+ trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:triggerDateComponents
+ repeats:NO];
}
- notification.alertAction = [RCTConvert NSString:details[@"alertAction"]];
- notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName;
- notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]] ?: @{};
- notification.category = [RCTConvert NSString:details[@"category"]];
- return notification;
+ return [UNNotificationRequest requestWithIdentifier:notificationId
+ content:content trigger:trigger];
}
@end
+static NSDictionary *RCTFormatUNNotification(UNNotification *notification)
+{
+ NSMutableDictionary *formattedNotification = [NSMutableDictionary dictionary];
+ UNNotificationContent *content = notification.request.content;
+
+ formattedNotification[@"identifier"] = notification.request.identifier;
+
+ if (notification.date) {
+ NSDateFormatter *formatter = [NSDateFormatter new];
+ [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"];
+ NSString *dateString = [formatter stringFromDate:notification.date];
+ formattedNotification[@"fireDate"] = dateString;
+ }
+
+ formattedNotification[@"alertTitle"] = RCTNullIfNil(content.title);
+ formattedNotification[@"alertBody"] = RCTNullIfNil(content.body);
+ formattedNotification[@"category"] = RCTNullIfNil(content.categoryIdentifier);
+ formattedNotification[@"thread-id"] = RCTNullIfNil(content.threadIdentifier);
+ formattedNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(content.userInfo));
+
+ return formattedNotification;
+}
+
@interface RNNotifications ()
@property (nonatomic, strong) NSMutableDictionary *remoteNotificationCallbacks;
@end
@@ -146,6 +176,11 @@ - (void)setBridge:(RCTBridge *)bridge
name:RNNotificationsRegistered
object:nil];
+ [[NSNotificationCenter defaultCenter] addObserver:self
+ selector:@selector(handleNotificationsRegistrationFailed:)
+ name:RNNotificationsRegistrationFailed
+ object:nil];
+
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(handlePushKitRegistered:)
name:RNPushKitRegistered
@@ -172,7 +207,8 @@ - (void)setBridge:(RCTBridge *)bridge
object:nil];
[RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
- [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
+ UILocalNotification *localNotification = [_bridge.launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
+ [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = localNotification ? localNotification.userInfo : nil;
}
/*
@@ -185,11 +221,18 @@ + (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notifi
}
}
-+ (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
++ (void)didRegisterForRemoteNotificationsWithDeviceToken:(id)deviceToken
{
+ NSString *tokenRepresentation = [deviceToken isKindOfClass:[NSString class]] ? deviceToken : [self deviceTokenToString:deviceToken];
[[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationsRegistered
object:self
- userInfo:@{@"deviceToken": [self deviceTokenToString:deviceToken]}];
+ userInfo:@{@"deviceToken": tokenRepresentation}];
+}
+
++ (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
+ [[NSNotificationCenter defaultCenter] postNotificationName:RNNotificationsRegistrationFailed
+ object:self
+ userInfo:@{@"code": [NSNumber numberWithInteger:error.code], @"domain": error.domain, @"localizedDescription": error.localizedDescription}];
}
@@ -485,6 +528,11 @@ - (void)handleNotificationsRegistered:(NSNotification *)notification
[_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistered" body:notification.userInfo];
}
+- (void)handleNotificationsRegistrationFailed:(NSNotification *)notification
+{
+ [_bridge.eventDispatcher sendDeviceEventWithName:@"remoteNotificationsRegistrationFailed" body:notification.userInfo];
+}
+
- (void)handlePushKitRegistered:(NSNotification *)notification
{
[_bridge.eventDispatcher sendDeviceEventWithName:@"pushKitRegistered" body:notification.userInfo];
@@ -562,6 +610,17 @@ - (void)handleNotificationActionTriggered:(NSNotification *)notification
[RNNotifications requestPermissionsWithCategories:categories];
}
+RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
+{
+ NSDictionary * notification = nil;
+ notification = [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification ?
+ [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification :
+ [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification;
+ [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = nil;
+ [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = nil;
+ resolve(notification);
+}
+
RCT_EXPORT_METHOD(log:(NSString *)message)
{
NSLog(message);
@@ -582,6 +641,17 @@ - (void)handleNotificationActionTriggered:(NSNotification *)notification
[RNNotifications registerPushKit];
}
+RCT_EXPORT_METHOD(getBadgesCount:(RCTResponseSenderBlock)callback)
+{
+ NSInteger count = [UIApplication sharedApplication].applicationIconBadgeNumber;
+ callback(@[ [NSNumber numberWithInteger:count] ]);
+}
+
+RCT_EXPORT_METHOD(setBadgesCount:(int)count)
+{
+ [[UIApplication sharedApplication] setApplicationIconBadgeNumber:count];
+}
+
RCT_EXPORT_METHOD(backgroundTimeRemaining:(RCTResponseSenderBlock)callback)
{
NSTimeInterval remainingTime = [UIApplication sharedApplication].backgroundTimeRemaining;
@@ -602,43 +672,55 @@ - (void)handleNotificationActionTriggered:(NSNotification *)notification
// Push background notifications to JS
[[RNNotificationsBridgeQueue sharedInstance] consumeNotificationsQueue:^(NSDictionary* notification) {
- [RNNotifications didReceiveRemoteNotification:notification];
+ [RNNotifications didReceiveNotificationOnBackgroundState:notification];
}];
// Push opened local notifications
NSDictionary* openedLocalNotification = [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification;
if (openedLocalNotification) {
+ [RNNotificationsBridgeQueue sharedInstance].openedLocalNotification = nil;
[RNNotifications didNotificationOpen:openedLocalNotification];
}
// Push opened remote notifications
NSDictionary* openedRemoteNotification = [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification;
if (openedRemoteNotification) {
+ [RNNotificationsBridgeQueue sharedInstance].openedRemoteNotification = nil;
[RNNotifications didNotificationOpen:openedRemoteNotification];
}
}
RCT_EXPORT_METHOD(localNotification:(NSDictionary *)notification withId:(NSString *)notificationId)
{
- UILocalNotification* localNotification = [RCTConvert UILocalNotification:notification];
- NSMutableArray* userInfo = localNotification.userInfo.mutableCopy;
- [userInfo setValue:notificationId forKey:@"__id"];
- localNotification.userInfo = userInfo;
-
- if ([notification objectForKey:@"fireDate"] != nil) {
- [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
+ if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) {
+ UNNotificationRequest* localNotification = [RCTConvert UNNotificationRequest:notification withId:notificationId];
+ [[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:localNotification withCompletionHandler:nil];
} else {
- [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
+ UILocalNotification* localNotification = [RCTConvert UILocalNotification:notification];
+ NSMutableArray* userInfo = localNotification.userInfo.mutableCopy;
+ [userInfo setValue:notificationId forKey:@"__id"];
+ localNotification.userInfo = userInfo;
+
+ if ([notification objectForKey:@"fireDate"] != nil) {
+ [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
+ } else {
+ [[UIApplication sharedApplication] presentLocalNotificationNow:localNotification];
+ }
}
}
RCT_EXPORT_METHOD(cancelLocalNotification:(NSString *)notificationId)
{
- for (UILocalNotification* notification in [UIApplication sharedApplication].scheduledLocalNotifications) {
- NSDictionary* notificationInfo = notification.userInfo;
+ if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"10")) {
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
+ [center removePendingNotificationRequestsWithIdentifiers:@[notificationId]];
+ } else {
+ for (UILocalNotification* notification in [UIApplication sharedApplication].scheduledLocalNotifications) {
+ NSDictionary* notificationInfo = notification.userInfo;
- if ([[notificationInfo objectForKey:@"__id"] isEqualToString:notificationId]) {
- [[UIApplication sharedApplication] cancelLocalNotification:notification];
+ if ([[notificationInfo objectForKey:@"__id"] isEqualToString:notificationId]) {
+ [[UIApplication sharedApplication] cancelLocalNotification:notification];
+ }
}
}
}
@@ -648,4 +730,62 @@ - (void)handleNotificationActionTriggered:(NSNotification *)notification
[RCTSharedApplication() cancelAllLocalNotifications];
}
+RCT_EXPORT_METHOD(isRegisteredForRemoteNotifications:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject)
+{
+ BOOL ans;
+
+ if (TARGET_IPHONE_SIMULATOR) {
+ ans = [[[UIApplication sharedApplication] currentUserNotificationSettings] types] != 0;
+ }
+ else {
+ ans = [[UIApplication sharedApplication] isRegisteredForRemoteNotifications];
+ }
+ resolve(@(ans));
+}
+
+RCT_EXPORT_METHOD(checkPermissions:(RCTPromiseResolveBlock) resolve
+ reject:(RCTPromiseRejectBlock) reject) {
+ UIUserNotificationSettings *currentSettings = [[UIApplication sharedApplication] currentUserNotificationSettings];
+ resolve(@{
+ @"badge": @((currentSettings.types & UIUserNotificationTypeBadge) > 0),
+ @"sound": @((currentSettings.types & UIUserNotificationTypeSound) > 0),
+ @"alert": @((currentSettings.types & UIUserNotificationTypeAlert) > 0),
+ });
+}
+
+#if !TARGET_OS_TV
+
+RCT_EXPORT_METHOD(removeAllDeliveredNotifications)
+{
+ if ([UNUserNotificationCenter class]) {
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
+ [center removeAllDeliveredNotifications];
+ }
+}
+
+RCT_EXPORT_METHOD(removeDeliveredNotifications:(NSArray *)identifiers)
+{
+ if ([UNUserNotificationCenter class]) {
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
+ [center removeDeliveredNotificationsWithIdentifiers:identifiers];
+ }
+}
+
+RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback)
+{
+ if ([UNUserNotificationCenter class]) {
+ UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
+ [center getDeliveredNotificationsWithCompletionHandler:^(NSArray * _Nonnull notifications) {
+ NSMutableArray *formattedNotifications = [NSMutableArray new];
+
+ for (UNNotification *notification in notifications) {
+ [formattedNotifications addObject:RCTFormatUNNotification(notification)];
+ }
+ callback(@[formattedNotifications]);
+ }];
+ }
+}
+
+#endif !TARGET_OS_TV
+
@end
diff --git a/android/build.gradle b/android/build.gradle
index 01d52837a..95ca5cdb9 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,12 +1,26 @@
+buildscript {
+ repositories {
+ maven {
+ url "https://maven.google.com"
+ }
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.0.1'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
apply plugin: 'com.android.library'
android {
- compileSdkVersion 26
- buildToolsVersion "26.0.2"
+ compileSdkVersion 27
defaultConfig {
minSdkVersion 16
- targetSdkVersion 26
+ targetSdkVersion 27
versionCode 1
versionName "1.0"
}
@@ -20,13 +34,12 @@ android {
dependencies {
// Google's GCM.
- compile 'com.google.android.gms:play-services-gcm:11.6.0'
-
- compile 'com.facebook.react:react-native:+'
+ api "com.google.firebase:firebase-messaging:17.3.4"
+ implementation 'com.facebook.react:react-native:+'
- compile 'me.leolin:ShortcutBadger:1.1.8@aar'
+ implementation 'me.leolin:ShortcutBadger:1.1.8@aar'
- testCompile 'junit:junit:4.12'
- testCompile 'org.mockito:mockito-core:2.+'
- testCompile 'org.robolectric:robolectric:3.1.4'
+ testImplementation 'junit:junit:4.12'
+ testImplementation 'org.mockito:mockito-core:2.+'
+ testImplementation 'org.robolectric:robolectric:3.1.4'
}
diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml
index 8061e77c1..ffef75f23 100644
--- a/android/src/main/AndroidManifest.xml
+++ b/android/src/main/AndroidManifest.xml
@@ -11,6 +11,9 @@
android:protectionLevel="signature" />
+
+
+
-
-
-
-
-
-
-
-
-
+ android:name=".gcm.FcmInstanceIdListenerService">
-
-
-
-
-
-
-
+
+
+
diff --git a/android/src/main/java/com/wix/reactnativenotifications/Defs.java b/android/src/main/java/com/wix/reactnativenotifications/Defs.java
index fff51742c..26a8b7a03 100644
--- a/android/src/main/java/com/wix/reactnativenotifications/Defs.java
+++ b/android/src/main/java/com/wix/reactnativenotifications/Defs.java
@@ -7,5 +7,6 @@ public interface Defs {
String TOKEN_RECEIVED_EVENT_NAME = "remoteNotificationsRegistered";
String NOTIFICATION_RECEIVED_EVENT_NAME = "notificationReceived";
+ String NOTIFICATION_RECEIVED_FOREGROUND_EVENT_NAME = "notificationReceivedInForeground";
String NOTIFICATION_OPENED_EVENT_NAME = "notificationOpened";
}
diff --git a/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsModule.java b/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsModule.java
index 0b4ef8a98..b3a68d6ff 100644
--- a/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsModule.java
+++ b/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsModule.java
@@ -5,6 +5,7 @@
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
+import android.support.v4.app.NotificationManagerCompat;
import android.util.Log;
import com.facebook.react.bridge.Arguments;
@@ -22,18 +23,18 @@
import com.wix.reactnativenotifications.core.notificationdrawer.IPushNotificationsDrawer;
import com.wix.reactnativenotifications.core.notificationdrawer.PushNotificationsDrawer;
import com.wix.reactnativenotifications.core.ReactAppLifecycleFacade;
-import com.wix.reactnativenotifications.gcm.GcmInstanceIdRefreshHandlerService;
+import com.wix.reactnativenotifications.gcm.FcmInstanceIdRefreshHandlerService;
import com.wix.reactnativenotifications.helpers.ApplicationBadgeHelper;
import static com.wix.reactnativenotifications.Defs.LOGTAG;
public class RNNotificationsModule extends ReactContextBaseJavaModule implements AppLifecycleFacade.AppVisibilityListener, Application.ActivityLifecycleCallbacks {
- public RNNotificationsModule(Application application, ReactApplicationContext reactContext) {
+ public RNNotificationsModule(Application application, RNNotificationsNativeCallback nativeCallback, ReactApplicationContext reactContext) {
super(reactContext);
if (AppLifecycleFacadeHolder.get() instanceof ReactAppLifecycleFacade) {
- ((ReactAppLifecycleFacade) AppLifecycleFacadeHolder.get()).init(reactContext);
+ ((ReactAppLifecycleFacade) AppLifecycleFacadeHolder.get()).init(reactContext, nativeCallback);
}
AppLifecycleFacadeHolder.get().addVisibilityListener(this);
application.registerActivityLifecycleCallbacks(this);
@@ -47,7 +48,7 @@ public String getName() {
@Override
public void initialize() {
Log.d(LOGTAG, "Native module init");
- startGcmIntentService(GcmInstanceIdRefreshHandlerService.EXTRA_IS_APP_INIT);
+ startGcmIntentService(FcmInstanceIdRefreshHandlerService.EXTRA_IS_APP_INIT);
final IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext());
notificationsDrawer.onAppInit();
@@ -56,7 +57,7 @@ public void initialize() {
@ReactMethod
public void refreshToken() {
Log.d(LOGTAG, "Native method invocation: refreshToken()");
- startGcmIntentService(GcmInstanceIdRefreshHandlerService.EXTRA_MANUAL_REFRESH);
+ startGcmIntentService(FcmInstanceIdRefreshHandlerService.EXTRA_MANUAL_REFRESH);
}
@ReactMethod
@@ -99,6 +100,12 @@ public void cancelLocalNotification(int notificationId) {
notificationsDrawer.onNotificationClearRequest(notificationId);
}
+ @ReactMethod
+ public void isRegisteredForRemoteNotifications(Promise promise) {
+ boolean hasPermission = NotificationManagerCompat.from(getReactApplicationContext()).areNotificationsEnabled();
+ promise.resolve(new Boolean(hasPermission));
+ }
+
@Override
public void onAppVisible() {
final IPushNotificationsDrawer notificationsDrawer = PushNotificationsDrawer.get(getReactApplicationContext().getApplicationContext());
@@ -141,7 +148,7 @@ public void onActivityDestroyed(Activity activity) {
protected void startGcmIntentService(String extraFlag) {
final Context appContext = getReactApplicationContext().getApplicationContext();
- final Intent tokenFetchIntent = new Intent(appContext, GcmInstanceIdRefreshHandlerService.class);
+ final Intent tokenFetchIntent = new Intent(appContext, FcmInstanceIdRefreshHandlerService.class);
tokenFetchIntent.putExtra(extraFlag, true);
appContext.startService(tokenFetchIntent);
}
diff --git a/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsNativeCallback.java b/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsNativeCallback.java
new file mode 100644
index 000000000..8aabee048
--- /dev/null
+++ b/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsNativeCallback.java
@@ -0,0 +1,7 @@
+package com.wix.reactnativenotifications;
+
+import com.facebook.react.bridge.WritableMap;
+
+public interface RNNotificationsNativeCallback {
+ void onEventNotSentToJS(String eventName, WritableMap data);
+}
diff --git a/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsPackage.java b/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsPackage.java
index 923f0caf8..7fd65d2f9 100644
--- a/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsPackage.java
+++ b/android/src/main/java/com/wix/reactnativenotifications/RNNotificationsPackage.java
@@ -3,7 +3,6 @@
import android.app.Application;
import com.facebook.react.ReactPackage;
-import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
@@ -14,21 +13,20 @@
public class RNNotificationsPackage implements ReactPackage {
-
- final Application mApplication;
+ private final Application mApplication;
+ private RNNotificationsNativeCallback mNativeCallback;
public RNNotificationsPackage(Application application) {
mApplication = application;
}
- @Override
- public List createNativeModules(ReactApplicationContext reactContext) {
- return Arrays.asList(new RNNotificationsModule(mApplication, reactContext));
+ public void addNativeCallback(RNNotificationsNativeCallback rnNotificationsNativeCallback) {
+ mNativeCallback = rnNotificationsNativeCallback;
}
- // Deprecated RN 0.47
- public List> createJSModules() {
- return Collections.emptyList();
+ @Override
+ public List createNativeModules(ReactApplicationContext reactContext) {
+ return Arrays.asList(new RNNotificationsModule(mApplication, mNativeCallback, reactContext));
}
@Override
diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/AppLifecycleFacade.java b/android/src/main/java/com/wix/reactnativenotifications/core/AppLifecycleFacade.java
index bba1e91ba..508ba6fb9 100644
--- a/android/src/main/java/com/wix/reactnativenotifications/core/AppLifecycleFacade.java
+++ b/android/src/main/java/com/wix/reactnativenotifications/core/AppLifecycleFacade.java
@@ -1,5 +1,6 @@
package com.wix.reactnativenotifications.core;
+import com.wix.reactnativenotifications.RNNotificationsNativeCallback;
import com.facebook.react.bridge.ReactContext;
public interface AppLifecycleFacade {
@@ -11,6 +12,7 @@ interface AppVisibilityListener {
boolean isReactInitialized();
ReactContext getRunningReactContext();
+ RNNotificationsNativeCallback getNativeCallback();
boolean isAppVisible();
void addVisibilityListener(AppVisibilityListener listener);
void removeVisibilityListener(AppVisibilityListener listener);
diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/JsIOHelper.java b/android/src/main/java/com/wix/reactnativenotifications/core/JsIOHelper.java
index 4d8f4d1d5..cc460af6a 100644
--- a/android/src/main/java/com/wix/reactnativenotifications/core/JsIOHelper.java
+++ b/android/src/main/java/com/wix/reactnativenotifications/core/JsIOHelper.java
@@ -1,5 +1,6 @@
package com.wix.reactnativenotifications.core;
+import com.wix.reactnativenotifications.RNNotificationsNativeCallback;
import android.os.Bundle;
import com.facebook.react.bridge.Arguments;
@@ -8,18 +9,18 @@
import com.facebook.react.modules.core.DeviceEventManagerModule;
public class JsIOHelper {
- public boolean sendEventToJS(String eventName, Bundle data, ReactContext reactContext) {
- if (reactContext != null) {
- sendEventToJS(eventName, Arguments.fromBundle(data), reactContext);
- return true;
- }
- return false;
+ public boolean sendEventToJS(String eventName, Bundle data, AppLifecycleFacade appLifecycleFacade) {
+ return sendEventToJS(eventName, Arguments.fromBundle(data), appLifecycleFacade);
}
- public boolean sendEventToJS(String eventName, WritableMap data, ReactContext reactContext) {
- if (reactContext != null) {
+ public boolean sendEventToJS(String eventName, WritableMap data, AppLifecycleFacade appLifecycleFacade) {
+ RNNotificationsNativeCallback nativeCallback = appLifecycleFacade.getNativeCallback();
+ ReactContext reactContext = appLifecycleFacade.getRunningReactContext();
+ if (appLifecycleFacade.isReactInitialized() && reactContext != null) {
reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName, data);
return true;
+ } else if (nativeCallback != null) {
+ nativeCallback.onEventNotSentToJS(eventName, data);
}
return false;
}
diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/NotificationIntentAdapter.java b/android/src/main/java/com/wix/reactnativenotifications/core/NotificationIntentAdapter.java
index 0413e2f86..3a2e65fe3 100644
--- a/android/src/main/java/com/wix/reactnativenotifications/core/NotificationIntentAdapter.java
+++ b/android/src/main/java/com/wix/reactnativenotifications/core/NotificationIntentAdapter.java
@@ -8,12 +8,14 @@
import com.wix.reactnativenotifications.core.notification.PushNotificationProps;
public class NotificationIntentAdapter {
- private static final int PENDING_INTENT_CODE = 0;
private static final String PUSH_NOTIFICATION_EXTRA_NAME = "pushNotification";
public static PendingIntent createPendingNotificationIntent(Context appContext, Intent intent, PushNotificationProps notification) {
intent.putExtra(PUSH_NOTIFICATION_EXTRA_NAME, notification.asBundle());
- return PendingIntent.getService(appContext, PENDING_INTENT_CODE, intent, PendingIntent.FLAG_ONE_SHOT);
+ // a unique action, data, type, class, or category must be set, otherwise the intent matcher
+ // gets confused see https://developer.android.com/reference/android/app/PendingIntent
+ intent.setAction(String.valueOf(System.currentTimeMillis()));
+ return PendingIntent.getService(appContext, (int) System.currentTimeMillis(), intent, PendingIntent.FLAG_ONE_SHOT);
}
public static Bundle extractPendingNotificationDataFromIntent(Intent intent) {
diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/ReactAppLifecycleFacade.java b/android/src/main/java/com/wix/reactnativenotifications/core/ReactAppLifecycleFacade.java
index b185903e6..838c4c19c 100644
--- a/android/src/main/java/com/wix/reactnativenotifications/core/ReactAppLifecycleFacade.java
+++ b/android/src/main/java/com/wix/reactnativenotifications/core/ReactAppLifecycleFacade.java
@@ -1,5 +1,6 @@
package com.wix.reactnativenotifications.core;
+import com.wix.reactnativenotifications.RNNotificationsNativeCallback;
import android.util.Log;
import com.facebook.react.bridge.LifecycleEventListener;
@@ -14,10 +15,12 @@ public class ReactAppLifecycleFacade implements AppLifecycleFacade {
private ReactContext mReactContext;
private boolean mIsVisible;
+ private RNNotificationsNativeCallback mNativeCallback;
private Set mListeners = new CopyOnWriteArraySet<>();
- public void init(ReactContext reactContext) {
+ public void init(ReactContext reactContext, RNNotificationsNativeCallback nativeCallback) {
mReactContext = reactContext;
+ mNativeCallback = nativeCallback;
reactContext.addLifecycleEventListener(new LifecycleEventListener() {
@Override
public void onHostResume() {
@@ -58,6 +61,11 @@ public ReactContext getRunningReactContext() {
return mReactContext;
}
+ @Override
+ public RNNotificationsNativeCallback getNativeCallback() {
+ return mNativeCallback;
+ }
+
@Override
public boolean isAppVisible() {
return mIsVisible;
diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java b/android/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java
index fe82ee1d0..0f136715f 100644
--- a/android/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java
+++ b/android/src/main/java/com/wix/reactnativenotifications/core/notification/PushNotification.java
@@ -28,6 +28,7 @@
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_OPENED_EVENT_NAME;
import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_EVENT_NAME;
+import static com.wix.reactnativenotifications.Defs.NOTIFICATION_RECEIVED_FOREGROUND_EVENT_NAME;
public class PushNotification implements IPushNotification {
@@ -79,12 +80,14 @@ public void onReceivedLocal(Integer notificationId) throws InvalidNotificationEx
postNotification(notificationId);
}
notifyReceivedToJS();
+ if (mAppLifecycleFacade.isAppVisible()) {
+ notifiyReceivedForegroundNotificationToJS();
+ }
}
@Override
public void onOpened() {
digestNotification();
- clearAllNotifications();
}
@Override
@@ -198,15 +201,19 @@ protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
notificationBuilder = new Notification.Builder(mContext);
} else {
+ createNotificationChannel(); // Must happen before notifying system of notification.
notificationBuilder = new Notification.Builder(mContext, mNotificationProps.getChannelId());
}
notificationBuilder.setContentTitle(title)
.setContentText(mNotificationProps.getBody())
+ .setStyle(new Notification.BigTextStyle()
+ .bigText(mNotificationProps.getBody()))
.setPriority(mNotificationProps.getPriority())
.setContentIntent(intent)
.setVibrate(mNotificationProps.getVibrationPattern())
.setSmallIcon(smallIconResId)
+ .setShowWhen(true)
.setAutoCancel(true);
int badge = mNotificationProps.getBadge();
@@ -214,8 +221,6 @@ protected Notification.Builder getNotificationBuilder(PendingIntent intent) {
notificationBuilder.setNumber(badge);
}
- createNotificationChannel(); // Must happen before notifying system of notification.
-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
notificationBuilder.setColor(Color.parseColor("#f65335"));
}
@@ -305,11 +310,15 @@ protected int createNotificationId(Notification notification) {
}
private void notifyReceivedToJS() {
- mJsIOHelper.sendEventToJS(NOTIFICATION_RECEIVED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext());
+ mJsIOHelper.sendEventToJS(NOTIFICATION_RECEIVED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade);
+ }
+
+ private void notifiyReceivedForegroundNotificationToJS() {
+ mJsIOHelper.sendEventToJS(NOTIFICATION_RECEIVED_FOREGROUND_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade);
}
private void notifyOpenedToJS() {
- mJsIOHelper.sendEventToJS(NOTIFICATION_OPENED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade.getRunningReactContext());
+ mJsIOHelper.sendEventToJS(NOTIFICATION_OPENED_EVENT_NAME, mNotificationProps.asBundle(), mAppLifecycleFacade);
}
protected void launchOrResumeApp() {
diff --git a/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawer.java b/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawer.java
index 7b320e16d..acd73c231 100644
--- a/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawer.java
+++ b/android/src/main/java/com/wix/reactnativenotifications/core/notificationdrawer/PushNotificationsDrawer.java
@@ -32,12 +32,16 @@ protected PushNotificationsDrawer(Context context, AppLaunchHelper appLaunchHelp
@Override
public void onAppInit() {
- clearAll();
+ /**
+ * No OP.
+ */
}
@Override
public void onAppVisible() {
- clearAll();
+ /**
+ * No OP.
+ */
}
@Override
@@ -51,7 +55,9 @@ public void onNewActivity(Activity activity) {
@Override
public void onNotificationOpened() {
- clearAll();
+ /**
+ * No OP.
+ */
}
@Override
diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmMessageHandlerService.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/FcmInstanceIdListenerService.java
similarity index 75%
rename from android/src/main/java/com/wix/reactnativenotifications/gcm/GcmMessageHandlerService.java
rename to android/src/main/java/com/wix/reactnativenotifications/gcm/FcmInstanceIdListenerService.java
index 9482bb139..1f91d8715 100644
--- a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmMessageHandlerService.java
+++ b/android/src/main/java/com/wix/reactnativenotifications/gcm/FcmInstanceIdListenerService.java
@@ -4,21 +4,34 @@
import android.util.Log;
import org.json.*;
-import com.google.android.gms.gcm.GcmListenerService;
+import com.facebook.common.logging.FLog;
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
import com.wix.reactnativenotifications.core.notification.IPushNotification;
import com.wix.reactnativenotifications.core.notification.PushNotification;
+import java.util.Map;
+
import static com.wix.reactnativenotifications.Defs.LOGTAG;
-public class GcmMessageHandlerService extends GcmListenerService {
+/**
+ * Instance-ID + token refreshing handling service. Contacts the GCM to fetch the updated token.
+ *
+ * @author amitd
+ */
+public class FcmInstanceIdListenerService extends FirebaseMessagingService {
+
+ private final static String LOG_TAG = "GcmMessageHandlerService";
@Override
- public void onMessageReceived(String s, Bundle bundle) {
+ public void onMessageReceived(RemoteMessage message){
+ Map messageData = message.getData();
+ Bundle bundle = convertMapToBundle(messageData);
Log.d(LOGTAG, "New message from GCM: " + bundle);
- String rawData = bundle.getString("data");
// Hack by Convoy, all of our data is nested in "data" json. We need to bring it up a level.
// we could change this in API but it's backwards incompatible with current app to do so.
- if (rawData.length() > 0) {
+ String rawData = bundle.getString("data");
+ if (rawData != null && rawData.length() > 0) {
try {
JSONObject data = new JSONObject(rawData);
try {
@@ -72,6 +85,8 @@ public void onMessageReceived(String s, Bundle bundle) {
} catch (JSONException ignored) {
Log.d(LOGTAG, "Failed to parse raw data");
}
+ } else {
+ FLog.i(LOG_TAG, "rawData doesn't contain data key or data is empty: " + bundle);
}
try {
@@ -80,6 +95,17 @@ public void onMessageReceived(String s, Bundle bundle) {
} catch (IPushNotification.InvalidNotificationException e) {
// A GCM message, yes - but not the kind we know how to work with.
Log.v(LOGTAG, "GCM message handling aborted", e);
+ FLog.i(LOG_TAG, "GCM message handling aborted: " + bundle);
}
}
+
+ private Bundle convertMapToBundle(Map map) {
+ Bundle bundle = new Bundle();
+ for (Map.Entry entry : map.entrySet()) {
+ bundle.putString(entry.getKey(), entry.getValue());
+ }
+
+ return bundle;
+ }
+
}
diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdRefreshHandlerService.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/FcmInstanceIdRefreshHandlerService.java
similarity index 74%
rename from android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdRefreshHandlerService.java
rename to android/src/main/java/com/wix/reactnativenotifications/gcm/FcmInstanceIdRefreshHandlerService.java
index 3aa7aa9dc..8270ad68e 100644
--- a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdRefreshHandlerService.java
+++ b/android/src/main/java/com/wix/reactnativenotifications/gcm/FcmInstanceIdRefreshHandlerService.java
@@ -3,18 +3,18 @@
import android.app.IntentService;
import android.content.Intent;
-public class GcmInstanceIdRefreshHandlerService extends IntentService {
+public class FcmInstanceIdRefreshHandlerService extends IntentService {
public static String EXTRA_IS_APP_INIT = "isAppInit";
public static String EXTRA_MANUAL_REFRESH = "doManualRefresh";
- public GcmInstanceIdRefreshHandlerService() {
- super(GcmInstanceIdRefreshHandlerService.class.getSimpleName());
+ public FcmInstanceIdRefreshHandlerService() {
+ super(FcmInstanceIdRefreshHandlerService.class.getSimpleName());
}
@Override
protected void onHandleIntent(Intent intent) {
- IGcmToken gcmToken = GcmToken.get(this);
+ IFcmToken gcmToken = FcmToken.get(this);
if (gcmToken == null) {
return;
}
diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/FcmToken.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/FcmToken.java
new file mode 100644
index 000000000..6b8a04fec
--- /dev/null
+++ b/android/src/main/java/com/wix/reactnativenotifications/gcm/FcmToken.java
@@ -0,0 +1,110 @@
+package com.wix.reactnativenotifications.gcm;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.Log;
+
+import com.facebook.react.ReactApplication;
+import com.facebook.react.ReactInstanceManager;
+import com.facebook.react.bridge.ReactContext;
+import com.facebook.react.modules.core.DeviceEventManagerModule;
+import com.google.android.gms.tasks.OnSuccessListener;
+import com.google.firebase.iid.FirebaseInstanceId;
+import com.google.firebase.iid.InstanceIdResult;
+
+import static com.wix.reactnativenotifications.Defs.LOGTAG;
+import static com.wix.reactnativenotifications.Defs.TOKEN_RECEIVED_EVENT_NAME;
+
+public class FcmToken implements IFcmToken {
+
+ final protected Context mAppContext;
+
+ protected static String sToken;
+
+ private final Runnable sendTokenToJsRunnable = new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mAppContext) {
+ final ReactInstanceManager instanceManager = ((ReactApplication) mAppContext).getReactNativeHost().getReactInstanceManager();
+ final ReactContext reactContext = instanceManager.getCurrentReactContext();
+ // Note: Cannot assume react-context exists cause this is an async dispatched service.
+ if (reactContext != null && reactContext.hasActiveCatalystInstance()) {
+ reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(TOKEN_RECEIVED_EVENT_NAME, sToken);
+ }
+ }
+ }
+ };
+
+ private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
+
+ protected FcmToken(Context appContext) {
+ if (!(appContext instanceof ReactApplication)) {
+ throw new IllegalStateException("Application instance isn't a react-application");
+ }
+ mAppContext = appContext;
+ }
+
+ public static IFcmToken get(Context context) {
+ Context appContext = context.getApplicationContext();
+ if (appContext instanceof INotificationsGcmApplication) {
+ return ((INotificationsGcmApplication) appContext).getFcmToken(context);
+ }
+ return new FcmToken(appContext);
+ }
+
+ @Override
+ public void onNewTokenReady() {
+ synchronized (mAppContext) {
+ refreshToken();
+ }
+ }
+
+ @Override
+ public void onManualRefresh() {
+ synchronized (mAppContext) {
+ if (sToken == null) {
+ Log.i(LOGTAG, "Manual token refresh => asking for new token");
+ refreshToken();
+ } else {
+ Log.i(LOGTAG, "Manual token refresh => publishing existing token ("+sToken+")");
+ sendTokenToJS();
+ }
+ }
+ }
+
+ @Override
+ public void onAppReady() {
+ synchronized (mAppContext) {
+ if (sToken == null) {
+ Log.i(LOGTAG, "App initialized => asking for new token");
+ refreshToken();
+ } else {
+ // Except for first run, this should be the case.
+ Log.i(LOGTAG, "App initialized => publishing existing token ("+sToken+")");
+ sendTokenToJS();
+ }
+ }
+ }
+
+ protected void refreshToken() {
+ FirebaseInstanceId.getInstance().getInstanceId().addOnSuccessListener( new OnSuccessListener() {
+ @Override
+ public void onSuccess(InstanceIdResult instanceIdResult) {
+ sToken = instanceIdResult.getToken();
+ Log.i(LOGTAG, "FCM has a new token" + "=" + sToken);
+ sendTokenToJS();
+ }
+ });
+ }
+
+ /**
+ * This method can be called from a background thread. The call to getReactInstanceManager()
+ * can end up calling createReactInstanceManager() which must be called from the UI thread.
+ *
+ * Because of this restriction we make a point to always post this runnable to a main thread.
+ */
+ protected void sendTokenToJS() {
+ mainThreadHandler.post(sendTokenToJsRunnable);
+ }
+}
diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdListenerService.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdListenerService.java
deleted file mode 100644
index 933415f5c..000000000
--- a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmInstanceIdListenerService.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package com.wix.reactnativenotifications.gcm;
-
-import android.content.Intent;
-
-import com.google.android.gms.iid.InstanceIDListenerService;
-
-/**
- * Instance-ID + token refreshing handling service. Contacts the GCM to fetch the updated token.
- *
- * @author amitd
- */
-public class GcmInstanceIdListenerService extends InstanceIDListenerService {
-
- @Override
- public void onTokenRefresh() {
- // Fetch updated Instance ID token and notify our app's server of any changes (if applicable).
- // Google recommends running this from an intent service.
- Intent intent = new Intent(this, GcmInstanceIdRefreshHandlerService.class);
- startService(intent);
- }
-}
diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmToken.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmToken.java
deleted file mode 100644
index b11a6b5f4..000000000
--- a/android/src/main/java/com/wix/reactnativenotifications/gcm/GcmToken.java
+++ /dev/null
@@ -1,132 +0,0 @@
-package com.wix.reactnativenotifications.gcm;
-
-import android.content.Context;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.support.annotation.NonNull;
-import android.util.Log;
-
-import com.facebook.react.ReactApplication;
-import com.facebook.react.ReactInstanceManager;
-import com.facebook.react.bridge.ReactContext;
-import com.facebook.react.modules.core.DeviceEventManagerModule;
-import com.google.android.gms.gcm.GoogleCloudMessaging;
-import com.google.android.gms.iid.InstanceID;
-
-import static com.wix.reactnativenotifications.Defs.GCM_SENDER_ID_ATTR_NAME;
-import static com.wix.reactnativenotifications.Defs.LOGTAG;
-import static com.wix.reactnativenotifications.Defs.TOKEN_RECEIVED_EVENT_NAME;
-
-public class GcmToken implements IGcmToken {
-
- final protected Context mAppContext;
-
- protected static String sToken;
-
- protected GcmToken(Context appContext) {
- if (!(appContext instanceof ReactApplication)) {
- throw new IllegalStateException("Application instance isn't a react-application");
- }
- mAppContext = appContext;
- }
-
- public static IGcmToken get(Context context) {
- Context appContext = context.getApplicationContext();
- if (appContext instanceof INotificationsGcmApplication) {
- return ((INotificationsGcmApplication) appContext).getGcmToken(context);
- }
- return new GcmToken(appContext);
- }
-
- @Override
- public void onNewTokenReady() {
- synchronized (mAppContext) {
- refreshToken();
- }
- }
-
- @Override
- public void onManualRefresh() {
- synchronized (mAppContext) {
- if (sToken == null) {
- Log.i(LOGTAG, "Manual token refresh => asking for new token");
- refreshToken();
- } else {
- Log.i(LOGTAG, "Manual token refresh => publishing existing token ("+sToken+")");
- sendTokenToJS();
- }
- }
- }
-
- @Override
- public void onAppReady() {
- synchronized (mAppContext) {
- if (sToken == null) {
- Log.i(LOGTAG, "App initialized => asking for new token");
- refreshToken();
- } else {
- // Except for first run, this should be the case.
- Log.i(LOGTAG, "App initialized => publishing existing token ("+sToken+")");
- sendTokenToJS();
- }
- }
- }
-
- protected void refreshToken() {
- try {
- sToken = getNewToken();
- } catch (Exception e) {
- Log.e(LOGTAG, "Failed to retrieve new token", e);
- return;
- }
-
- sendTokenToJS();
- }
-
- @NonNull
- protected String getNewToken() throws Exception {
- final InstanceID instanceId = InstanceID.getInstance(mAppContext);
- Log.d(LOGTAG, "GCM is refreshing token... instanceId=" + instanceId.getId());
-
- // TODO why is this needed?
- GoogleCloudMessaging.getInstance(mAppContext).close();
-
- try {
- final String registrationToken = instanceId.getToken(getSenderId(), GoogleCloudMessaging.INSTANCE_ID_SCOPE);
- Log.i(LOGTAG, "GCM has a new token: instanceId=" + instanceId.getId() + ", token=" + registrationToken);
- return registrationToken;
- } catch (Exception e) {
- throw new Exception("FATAL: Failed to fetch a fresh new token, instanceId=" + instanceId.getId(), e);
- }
- }
-
- protected String getSenderId() {
- final String senderId = getSenderIdFromManifest();
- if (senderId == null) {
- throw new IllegalStateException("Sender ID not found in manifest. Did you forget to add it as the value of a '"+GCM_SENDER_ID_ATTR_NAME+"' meta-data field?");
- }
- return senderId;
- }
-
- protected String getSenderIdFromManifest() {
- final ApplicationInfo appInfo;
- try {
- appInfo = mAppContext.getPackageManager().getApplicationInfo(mAppContext.getPackageName(), PackageManager.GET_META_DATA);
- return appInfo.metaData.getString(GCM_SENDER_ID_ATTR_NAME);
- } catch (PackageManager.NameNotFoundException e) {
- // Should REALLY never happen cause we're querying for our own package.
- Log.e(LOGTAG, "Failed to resolve sender ID from manifest", e);
- return null;
- }
- }
-
- protected void sendTokenToJS() {
- final ReactInstanceManager instanceManager = ((ReactApplication) mAppContext).getReactNativeHost().getReactInstanceManager();
- final ReactContext reactContext = instanceManager.getCurrentReactContext();
-
- // Note: Cannot assume react-context exists cause this is an async dispatched service.
- if (reactContext != null && reactContext.hasActiveCatalystInstance()) {
- reactContext.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(TOKEN_RECEIVED_EVENT_NAME, sToken);
- }
- }
-}
diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/IGcmToken.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/IFcmToken.java
similarity index 95%
rename from android/src/main/java/com/wix/reactnativenotifications/gcm/IGcmToken.java
rename to android/src/main/java/com/wix/reactnativenotifications/gcm/IFcmToken.java
index f324a591f..9e75d3901 100644
--- a/android/src/main/java/com/wix/reactnativenotifications/gcm/IGcmToken.java
+++ b/android/src/main/java/com/wix/reactnativenotifications/gcm/IFcmToken.java
@@ -1,6 +1,6 @@
package com.wix.reactnativenotifications.gcm;
-public interface IGcmToken {
+public interface IFcmToken {
/**
* Handle an event where we've been notified of a that a fresh token is now available from Google.
diff --git a/android/src/main/java/com/wix/reactnativenotifications/gcm/INotificationsGcmApplication.java b/android/src/main/java/com/wix/reactnativenotifications/gcm/INotificationsGcmApplication.java
index 36f59f71c..d318ecc4a 100644
--- a/android/src/main/java/com/wix/reactnativenotifications/gcm/INotificationsGcmApplication.java
+++ b/android/src/main/java/com/wix/reactnativenotifications/gcm/INotificationsGcmApplication.java
@@ -3,5 +3,5 @@
import android.content.Context;
public interface INotificationsGcmApplication {
- IGcmToken getGcmToken(Context context);
+ IFcmToken getFcmToken(Context context);
}
diff --git a/docs/advancedIos.md b/docs/advancedIos.md
new file mode 100644
index 000000000..9e44bde67
--- /dev/null
+++ b/docs/advancedIos.md
@@ -0,0 +1,277 @@
+# Advanced API -
iOS
+
+## Managed Notifications
+
+Managed notifications are notifications that can be cleared by a server request.
+You can find this feature in facebook messenger, when you receive a message in your mobile, but open it in facebook web. More examples are Whatsapp web and gmail app.
+
+In order to handle managed notifications, your app must support background notifications, and the server should send the notifications you'd like to "manage" a bit differently. Let's start.
+
+First, enable the *Remote notifications* checkbox under **capabilities - Background Modes**:
+
+
+Then, add the following lines to `info.plist`:
+
+```xml
+UIBackgroundModes
+
+ remote-notification
+
+```
+
+That's it for the client side!
+
+Now the server should push the notification a bit differently- background instead of reguler. You should also provide the action (`CREATE` notification or `CLEAR` notification), and `notificationId` as a unique identifier of the notification.
+
+**Regular** notification payload:
+
+```javascript
+{
+ aps: {
+ alert: {
+ body: "This is regular notification"
+ },
+ badge: 5,
+ sound: "chime.aiff",
+ }
+}
+```
+
+**Managed** notification payload:
+
+```javascript
+{
+ aps: {
+ "content-available": 1
+ },
+ managedAps: {
+ action: "CREATE", // set it to "CLEAR" in order to clear the notification remotely
+ notificationId: "1234", // must be unique identifier
+ sound: "chime.aiff",
+ alert: {
+ body: "This is managed notification"
+ }
+ }
+}
+```
+
+---
+
+## Remove notifications
+
+### getDeliveredNotifications
+
+`PushNotification.getDeliveredNotifications(callback: (notifications: Array