diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..105898580
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,2 @@
+github: [Dallas62]
+custom: ["https://www.buymeacoffee.com/Dallas62"]
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
index 0a912dff1..df62fddde 100644
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -6,6 +6,8 @@ about: Report a reproducible bug or regression in this library.
# Bug
diff --git a/.gitignore b/.gitignore
index adec98609..0a2cc57b7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -46,3 +46,5 @@ android/src/main/gen
#Debug only
google-services.json
+
+.vscode/
diff --git a/.npmignore b/.npmignore
index 8c497b0dd..84ba28601 100644
--- a/.npmignore
+++ b/.npmignore
@@ -62,4 +62,13 @@ local.properties
example
# Git
-.git
\ No newline at end of file
+.git
+
+# GitHub
+.github/*
+
+# Docs
+submitting-a-pull-request.md
+
+# Vscode
+.vscode/*
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 42a1287cc..a7b3c0c64 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,15 +2,255 @@
All notable changes to this project will be documented in this file.
-The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
-and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
+This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
-## [Unreleased]
+## Unreleased
+
+### Breaking changes
### Features
### Fixed
+## [7.2.3] 2021-03-18
+
+### Fixed
+
+- (Android) Fix: Notification drawer doesn't close after click on action that navigates you to app [#1914](https://github.com/zo0r/react-native-push-notification/issues/1914)
+- (iOS) Fix: foreground notification property [#1916](https://github.com/zo0r/react-native-push-notification/pull/1916)
+
+## [7.2.2] 2021-03-04
+
+### Fixed
+
+- (Android) Fix: Could not invoke RNPushNotification.getDeliveredNotifications. [#1878](https://github.com/zo0r/react-native-push-notification/issues/1878)
+- (fix) deep clone details and notifications. [#1793](https://github.com/zo0r/react-native-push-notification/issues/1793)
+
+## [7.2.1] 2021-02-11
+
+### Fixed
+
+- (iOS) Fix `playSound` options on local notifications. [#1858](https://github.com/zo0r/react-native-push-notification/issues/1858#issuecomment-775714298)
+
+## [7.2.0] 2021-01-24
+
+### Features
+
+- (Android) Handle localization for notification title and body [#1837](https://github.com/zo0r/react-native-push-notification/pull/1837)
+
+## [7.1.1] 2021-01-20
+
+### Fixed
+
+- (Android) unsubscribeFromTopic function fix [#1831](https://github.com/zo0r/react-native-push-notification/pull/1831)
+
+## [7.1.0] 2021-01-16
+
+# Features
+
+- (Android) Add hooks to intent handling and bundle parsing [#1819](https://github.com/zo0r/react-native-push-notification/pull/1819)
+
+## [7.0.0] 2020-12-23
+
+### Breaking changes
+
+- (iOS) Replace deprecated local notification methods on iOS [#1751](https://github.com/zo0r/react-native-push-notification/pull/1751)
+- (Android) Rename the Android package from `RNPushNotification` to `ReactNativePushNotification` resolve [#893](https://github.com/zo0r/react-native-push-notification/issues/893)
+- (Android) Allow `userInfo` to be stored in scheduled notification as in iOS (mapped as `data` on press or list scheduled notifications).
+
+### Features
+
+- (Android) silent channel using playSound flag
+- (Android) implement 'bigLargeIcon' for Android notifications (must be combined with BigPicture) [#1730](https://github.com/zo0r/react-native-push-notification/pull/1730)
+- (Android) notification with inline reply [#612](https://github.com/zo0r/react-native-push-notification/pull/612)
+- (Android) Support using drawable as Android small icon [#1787](https://github.com/zo0r/react-native-push-notification/pull/1787)
+
+## [6.1.3] 2020-11-09
+
+### Fixed
+
+- (Android) Null pointer exception when trying to create channel [#1734](https://github.com/zo0r/react-native-push-notification/issues/1734)
+
+## [6.1.2] 2020-10-29
+
+### Fixed
+
+- (Android) Fix for vibration on notifs for Android API >= 26 [#1686](https://github.com/zo0r/react-native-push-notification/pull/1686)
+
+## [6.1.1] 2020-09-29
+
+### Fixed
+
+- (Android) Fix a crash when the application is in background [#1676](https://github.com/zo0r/react-native-push-notification/issues/1676)
+
+## [6.1.0] 2020-09-28
+
+### Features
+
+- (Android) Allow a default channel in the `AndroidManifest`:
+ ```xml
+
+ ```
+ If not defined, fallback to the Firebase value of:
+ ```xml
+
+ ```
+ If not defined, fallback to the default Firebase channel id `fcm_fallback_notification_channel`
+
+## [6.0.0] 2020-09-26
+
+### Breaking changes
+
+- (Android) Channel Management: In order to limit the scope of responsability of this library, developers are now responsible of the creation of the channels. You can find the documentation at https://github.com/zo0r/react-native-push-notification#channel-management-android. These changes are also made to allow improvements in the future of the library. Here the list of impacts:
+ - You must create your channels before triggering a notification.
+ - These entries in `AndroidManifest` are deprecated:
+ ```xml
+
+
+
+ ```
+ - Followings options changed on Android in `localNotification` and `localNotificationSchedule`:
+ - `channelId` becomes mandatory (warning if not provided)
+ - `channelName` is deprecated
+ - `channelDescription` is deprecated
+ - `importance` is deprecated
+ - These changes help to avoid an issue [#1649](https://github.com/zo0r/react-native-push-notification/issues/1649)
+- (Android) Remove check for the intent `BOOT_COMPLETED`, this should allow more intent action such as `QUICKBOOT_POWERON`. It's recommended to update `AndroidManifest`, the `RNPushNotificationBootEventReceiver` to:
+ ```xml
+
+
+
+
+
+
+
+ ```
+- `@react-native-community/push-notification-ios` is now a `peerDependency`, please make sure that you installed this library with NPM or YARN.
+- (Android) Fix a bug where notification data are not inside `data` property after been pressed by user. When sending notification + data and app in background.
+- (Android) Add more fields from the firebase notification part. (Thanks to @fattomhk with this PR [#1626](https://github.com/zo0r/react-native-push-notification/pull/1626))
+ - `notificationPriority`
+ - `image`
+ - `tag`
+ - `visibility`
+- (Android) `data.twi_body` is no more used to trigger a notification in notification-center. Revert of [#744](https://github.com/zo0r/react-native-push-notification/pull/744)
+
+### Fixed
+
+- (iOS) upgrade `@react-native-community/push-notification-ios`, fixe the value of `userInteraction` [@react-native-community/push-notification-ios#122](https://github.com/react-native-community/push-notification-ios/pull/122).
+
+## [5.1.1] 2020-09-15
+
+### Fixed
+
+- (Android) Fatal Exception: java.lang.NullPointerException [#1641](https://github.com/zo0r/react-native-push-notification/issues/1641)
+
+## [5.1.0] 2020-08-31
+
+### Features
+
+- (Android) Add support for specifying a delegate FirebaseMessagingService [#1589](https://github.com/zo0r/react-native-push-notification/pull/1589)
+- (Android) Add support of `when`, `usesChronometer` and `timeoutAfter`.
+
+### Fixed
+
+- (Android) Fix a bug where `userInteraction` is not set, notification when app in background pressed by user.
+
+
+## [5.0.1] 2020-08-04
+
+### Fixed
+
+- (Android) Fix change that make gradle build fail [#1578](https://github.com/zo0r/react-native-push-notification/pull/1578).
+
+## [5.0.0] 2020-08-03
+
+### Breaking changes
+
+- (Android/iOS) Unify returned values between iOS and Android [#1516](https://github.com/zo0r/react-native-push-notification/pull/1516).
+- (Android/iOS) `.popInitialNotification(callback)` now return the same format as `onNotification()`.
+- (Android) `popInitialNotification` in `configure()` now trigger only once on app startup, same as iOS.
+- (Android) `notification.foreground` now return the good value, before the value was `false` most of the time.
+
+### Features
+
+- (Android) Add function `createChannel` for custom Android channel support [#1509](https://github.com/zo0r/react-native-push-notification/pull/1509)
+- (Android) Add Android `messageId` to enable integration with `react-native-firebase/messaging` [#1510](https://github.com/zo0r/react-native-push-notification/pull/1510)
+- (Android) Add support for `onlyAlertOnce` property [#1519](https://github.com/zo0r/react-native-push-notification/pull/1519)
+- (Android) Allow to change default notification channel name after it's creation [#1549](https://github.com/zo0r/react-native-push-notification/pull/1549)
+
+### Fixed
+
+- (Android) `popInitialNotification` in `configure()` now trigger only once and do not trigger twice `onNotification()` when user press the notification, more details: [#1516](https://github.com/zo0r/react-native-push-notification/pull/1516).
+- (Android) `notification.foreground` now return the good value, before the value was `false` most of the time.
+
+## [4.0.0] 2020-07-06
+
+### Breaking changes
+
+- `RNPushNotificationRegistrationService` has been removed, old reference in AndroidManifest must be removed.
+- `Notifications.registerNotificationActions()` has been removed and is not required for `actions`.
+- `DeviceEventEmitter.addListener('notificationActionReceived', callback)` is replaced by `onAction`.
+- Extra receiver must be added to manage actions.
+ ```xml
+
+ ```
+- (iOS) `userInfo` is now populated with id by default to allow operation based on `id`.
+
+### Features
+
+- (Android) `actions` accept an array of strings.
+- (Android) `invokeApp` allow you to handle actions in background without invoking the application.
+- (Android) `onAction` has been added to `.configure()` to handle action in background.
+- (Android) `PushNotification.invokeApp(notification)` allow you to invoke the application when in background (notification for initial notification).
+- (Android) `PushNotification.getChannels(callback)` allow you to get the list of channels.
+- (Android) `PushNotification.channelExists(channel_id, callback)` allow you to check of a channel exists.
+- (Android) `PushNotification.channelBlocked(channel_id, callback)` allow you to check of a channel is blocked. Based on [#1249](https://github.com/zo0r/react-native-push-notification/pull/1249)
+- (Android) `PushNotification.deleteChannel(channel_id)` allow you to delete a channel.
+- (Android) Add `largeIconUrl` to load a largeIcon based on Url. Based on [#1444](https://github.com/zo0r/react-native-push-notification/pull/1444)
+- (Android) Add `bigPictureUrl` to load a picture based on Url. Based on [#1444](https://github.com/zo0r/react-native-push-notification/pull/1444)
+- (Android) Add `shortcutId` for better badges management.
+- (Android) Add `showWhen` to display "when" it was published, default: true.
+- (Android) Add `groupSummary` to allow grouping notifications. Based on [#1253](https://github.com/zo0r/react-native-push-notification/pull/1253)
+- (Android) Add `channelId`, custom channel_id in android. Based on [#1159](https://github.com/zo0r/react-native-push-notification/pull/1159)
+- (Android) Add `channelName`, custom channel_name in android.
+- (Android) Add `channelDescription`, custom channel_description in android.
+- (iOS) Add fire date in notification response, NOTE: `push-notification-ios` in version `> 1.2.0` [#1345](https://github.com/zo0r/react-native-push-notification/pull/1345)
+- (iOS) `onRegistrationError` has been added to `.configure()` to handle `registrationError` events.
+- (Android/iOS) Add method getScheduledLocalNotifications()[#1466](https://github.com/zo0r/react-native-push-notification/pull/1466)
+
+### Fixed
+
+- (Android) Replace java.util.Random with java.security.SecureRandom [#1497](https://github.com/zo0r/react-native-push-notification/pull/1497)
+- (Android) WAKE_LOCK permission removed from documentation. [#1494](https://github.com/zo0r/react-native-push-notification/issues/1494)
+- (Android) Some options were ignored on scheduled/repeating notifications (allowWhileIdle, ignoreInForeground).
+- (Android/iOS) popInitialInotification might be ignored in `.configure()`
+
+## [3.5.2] - 2020-05-25
+
+### Fixed
+
+- (Android) Sounds are playing even in Do Not Disturb [#1432](https://github.com/zo0r/react-native-push-notification/issues/1432#issuecomment-633367111)
+- (Android) onNotification fires every time when the app goes from background to foreground [#1455](https://github.com/zo0r/react-native-push-notification/issues/1455)
+- (Android) java.lang.NullPointerException: Attempt to invoke virtual method 'void com.dieam.reactnativepushnotification.modules.d.c(android.os.Bundle)' on a null object reference [#1431](https://github.com/zo0r/react-native-push-notification/issues/1431#issuecomment-633315150)
+
+## [3.5.1] - 2020-05-20
+
+### Fixed
+
+- (Android) When updating 3.4 to 3.5, unable to compile Android [#1449](https://github.com/zo0r/react-native-push-notification/pull/1449)
+
+## [3.5.0] - 2020-05-20
+
+### Features
+
+- (Android) Enables the ability to support multiple push providers [#1445](https://github.com/zo0r/react-native-push-notification/pull/1445)
+
+### Fixed
+
+- (Android) No sound on notifications [#1432](https://github.com/zo0r/react-native-push-notification/issues/1432)
+- (Android) onNotification is not calling when app is in background [#1446](https://github.com/zo0r/react-native-push-notification/pull/1446)
+- (Android) `number` and `id` do not crash if NaN is passed in Android.
+
## [3.4.0] - 2020-05-08
### Features
diff --git a/README.md b/README.md
index 7b718613b..d0b755360 100644
--- a/README.md
+++ b/README.md
@@ -5,6 +5,19 @@
React Native Local and Remote Notifications for iOS and Android
+
+## 🎉 Version 7.x is live ! 🎉
+
+Check out for changes and migration in the CHANGELOG:
+
+[Changelog](https://github.com/zo0r/react-native-push-notification/blob/master/CHANGELOG.md)
+
+# Supporting the project
+
+Maintaining this project takes time. To help allocate time, you can Buy Me a Coffee :wink:
+
+
+
## Supported React Native Versions
| Component Version | RN Versions | README |
@@ -29,6 +42,8 @@ Changelog is available from version 3.1.3 here: [Changelog](https://github.com/z
`yarn add react-native-push-notification`
+**NOTE: If you target iOS you also need to follow the [installation instructions for PushNotificationIOS](https://github.com/react-native-community/react-native-push-notification-ios) since this package depends on it.**
+
**NOTE: For Android, you will still have to manually update the AndroidManifest.xml (as below) in order to use Scheduled Notifications.**
## Issues
@@ -41,9 +56,7 @@ Having a problem? Read the [troubleshooting](./trouble-shooting.md) guide before
## iOS manual Installation
-The component uses PushNotificationIOS for the iOS part.
-
-[Please see: PushNotificationIOS](https://github.com/react-native-community/react-native-push-notification-ios)
+The component uses PushNotificationIOS for the iOS part. You should follow their [installation instructions](https://github.com/react-native-community/react-native-push-notification-ios).
## Android manual Installation
@@ -70,28 +83,24 @@ In your `android/app/src/main/AndroidManifest.xml`
```xml
.....
-
-
-
-
-
-
+
+
+
+
@@ -114,6 +123,17 @@ In `android/app/src/main/res/values/colors.xml` (Create the file if it doesn't e
```
+If your app has an @Override on onNewIntent in `MainActivity.java` ensure that function includes a super call on onNewIntent (if your `MainActivity.java` does not have an @Override for onNewIntent skip this):
+
+```java
+ @Override
+ public void onNewIntent(Intent intent) {
+ ...
+ super.onNewIntent(intent);
+ ...
+ }
+```
+
### If you use remote notifications
Make sure you have installed setup Firebase correctly.
@@ -189,10 +209,10 @@ public class MainApplication extends Application implements ReactApplication {
@Override
protected List getPackages() {
- return Arrays.asList(
- new MainReactPackage(),
- new ReactNativePushNotificationPackage() // <---- Add the Package
- );
+ return Arrays.asList(
+ new MainReactPackage(),
+ new ReactNativePushNotificationPackage() // <---- Add the Package
+ );
}
};
@@ -202,10 +222,15 @@ public class MainApplication extends Application implements ReactApplication {
## Usage
+**DO NOT USE `.configure()` INSIDE A COMPONENT, EVEN `App`**
+> If you do, notification handlers will not fire, because they are not loaded. Instead, use `.configure()` in the app's first file, usually `index.js`.
+
+
```javascript
import PushNotificationIOS from "@react-native-community/push-notification-ios";
-var PushNotification = require("react-native-push-notification");
+import PushNotification from "react-native-push-notification";
+// Must be outside of any component LifeCycle (such as `componentDidMount`).
PushNotification.configure({
// (optional) Called when Token is generated (iOS and Android)
onRegister: function (token) {
@@ -222,6 +247,19 @@ PushNotification.configure({
notification.finish(PushNotificationIOS.FetchResult.NoData);
},
+ // (optional) Called when Registered Action is pressed and invokeApp is false, if true onNotification will be called (Android)
+ onAction: function (notification) {
+ console.log("ACTION:", notification.action);
+ console.log("NOTIFICATION:", notification);
+
+ // process the action
+ },
+
+ // (optional) Called when the user fails to register for remote notifications. Typically occurs when APNS is having issues, or the device is a simulator. (iOS)
+ onRegistrationError: function(err) {
+ console.error(err.message, err);
+ },
+
// IOS ONLY (optional): default: all - Permissions to register.
permissions: {
alert: true,
@@ -261,7 +299,7 @@ Notification object example:
foreground: false, // BOOLEAN: If the notification was received in foreground or not
userInteraction: false, // BOOLEAN: If the notification was opened by the user from the notification area or not
message: 'My Notification Message', // STRING: The notification message
- data: {}, // OBJECT: The push data
+ data: {}, // OBJECT: The push data or the defined userInfo in local notifications
}
```
@@ -274,38 +312,52 @@ EXAMPLE:
```javascript
PushNotification.localNotification({
/* Android Only Properties */
- id: 0, // (optional) Valid unique 32 bit integer specified as string. default: Autogenerated Unique ID
+ channelId: "your-channel-id", // (required) channelId, if the channel doesn't exist, notification will not trigger.
ticker: "My Notification Ticker", // (optional)
+ showWhen: true, // (optional) default: true
autoCancel: true, // (optional) default: true
- largeIcon: "ic_launcher", // (optional) default: "ic_launcher"
- smallIcon: "ic_notification", // (optional) default: "ic_notification" with fallback for "ic_launcher"
+ largeIcon: "ic_launcher", // (optional) default: "ic_launcher". Use "" for no large icon.
+ largeIconUrl: "https://www.example.tld/picture.jpg", // (optional) default: undefined
+ smallIcon: "ic_notification", // (optional) default: "ic_notification" with fallback for "ic_launcher". Use "" for default small icon.
bigText: "My big text that will be shown when notification is expanded", // (optional) default: "message" prop
subText: "This is a subText", // (optional) default: none
+ bigPictureUrl: "https://www.example.tld/picture.jpg", // (optional) default: undefined
+ bigLargeIcon: "ic_launcher", // (optional) default: undefined
+ bigLargeIconUrl: "https://www.example.tld/bigicon.jpg", // (optional) default: undefined
color: "red", // (optional) default: system default
vibrate: true, // (optional) default: true
vibration: 300, // vibration length in milliseconds, ignored if vibrate=false, default: 1000
tag: "some_tag", // (optional) add tag to message
group: "group", // (optional) add group to message
+ groupSummary: false, // (optional) set this notification to be the group summary for a group of notifications, default: false
ongoing: false, // (optional) set whether this is an "ongoing" notification
priority: "high", // (optional) set notification priority, default: high
visibility: "private", // (optional) set notification visibility, default: private
- importance: "high", // (optional) set notification importance, default: high
- allowWhileIdle: false, // (optional) set notification to work while on doze, default: false
- ignoreInForeground: false, // (optional) if true, the notification will not be visible when the app is in the foreground (useful for parity with how iOS notifications appear)
+ ignoreInForeground: false, // (optional) if true, the notification will not be visible when the app is in the foreground (useful for parity with how iOS notifications appear). should be used in combine with `com.dieam.reactnativepushnotification.notification_foreground` setting
+ shortcutId: "shortcut-id", // (optional) If this notification is duplicative of a Launcher shortcut, sets the id of the shortcut, in case the Launcher wants to hide the shortcut, default undefined
+ onlyAlertOnce: false, // (optional) alert will open only once with sound and notify, default: false
+
+ when: null, // (optional) Add a timestamp (Unix timestamp value in milliseconds) pertaining to the notification (usually the time the event occurred). For apps targeting Build.VERSION_CODES.N and above, this time is not shown anymore by default and must be opted into by using `showWhen`, default: null.
+ usesChronometer: false, // (optional) Show the `when` field as a stopwatch. Instead of presenting `when` as a timestamp, the notification will show an automatically updating display of the minutes and seconds since when. Useful when showing an elapsed time (like an ongoing phone call), default: false.
+ timeoutAfter: null, // (optional) Specifies a duration in milliseconds after which this notification should be canceled, if it is not already canceled, default: null
+
+ messageId: "google:message_id", // (optional) added as `message_id` to intent extras so opening push notification can find data stored by @react-native-firebase/messaging module.
+
+ actions: ["Yes", "No"], // (Android only) See the doc for notification actions to know more
+ invokeApp: true, // (optional) This enable click on actions to bring back the application to foreground or stay in background, default: true
/* iOS only properties */
- alertAction: "view", // (optional) default: view
category: "", // (optional) default: empty string
- userInfo: {}, // (optional) default: {} (using null throws a JSON value '' error)
/* iOS and Android properties */
+ id: 0, // (optional) Valid unique 32 bit integer specified as string. default: Autogenerated Unique ID
title: "My Notification Title", // (optional)
message: "My Notification Message", // (required)
+ userInfo: {}, // (optional) default: {} (using null throws a JSON value '' error)
playSound: false, // (optional) default: true
soundName: "default", // (optional) Sound to play when the notification is shown. Value of 'default' plays the default sound. It can be set to a custom sound such as 'android.resource://com.xyz/raw/my_sound'. It will look for the 'my_sound' audio file in 'res/raw' directory and play it. default: 'default' (default sound is played)
number: 10, // (optional) Valid 32 bit integer specified as string. default: none (Cannot be zero)
repeatType: "day", // (optional) Repeating interval. Check 'Repeating Notifications' section for more info.
- actions: '["Yes", "No"]', // (Android only) See the doc for notification actions to know more
});
```
@@ -320,6 +372,19 @@ PushNotification.localNotificationSchedule({
//... You can use all the options from localNotifications
message: "My Notification Message", // (required)
date: new Date(Date.now() + 60 * 1000), // in 60 secs
+ allowWhileIdle: false, // (optional) set notification to work while on doze, default: false
+});
+```
+
+## Get the initial notification
+
+`PushNotification.popInitialNotification(callback)`
+
+EXAMPLE:
+
+```javascript
+PushNotification.popInitialNotification((notification) => {
+ console.log('Initial Notification', notification);
});
```
@@ -333,50 +398,120 @@ In the location notification json specify the full file name:
soundName: 'my_sound.mp3'
-## Cancelling notifications
+## Channel Management (Android)
-### 1) cancelLocalNotifications
+To use channels, create them at startup and pass the matching `channelId` through to `PushNotification.localNotification` or `PushNotification.localNotificationSchedule`.
-#### Android
+```javascript
+ PushNotification.createChannel(
+ {
+ channelId: "channel-id", // (required)
+ channelName: "My channel", // (required)
+ channelDescription: "A channel to categorise your notifications", // (optional) default: undefined.
+ playSound: false, // (optional) default: true
+ soundName: "default", // (optional) See `soundName` parameter of `localNotification` function
+ importance: 4, // (optional) default: 4. Int value of the Android notification importance
+ vibrate: true, // (optional) default: true. Creates the default vibration patten if true.
+ },
+ (created) => console.log(`createChannel returned '${created}'`) // (optional) callback returns whether the channel was created, false means it already existed.
+ );
+```
-The `id` parameter for `PushNotification.localNotification` is required for this operation. The id supplied will then be used for the cancel operation.
+**NOTE: Without channel, notifications don't work**
-```javascript
-// Android
-PushNotification.localNotification({
- ...
- id: '123'
- ...
+In the notifications options, you must provide a channel id with `channelId: "your-channel-id"`, if the channel doesn't exist the notification might not e triggered. Once the channel is created, the channel cannot be update. Make sure your `channelId` is different if you change these options. If you have created a channel in another way, it will apply options of the channel.
+
+If you want to use a different default channel for remote notification, refer to the documentation of Firebase:
+
+[Set up a Firebase Cloud Messaging client app on Android](https://firebase.google.com/docs/cloud-messaging/android/client?hl=fr)
+
+```xml
+
+```
+
+For local notifications, the same kind of option is available:
+
+- you can use:
+ ```xml
+
+ ```
+- If not defined, fallback to the Firebase value defined in the `AndroidManifest`:
+ ```xml
+
+ ```
+- If not defined, fallback to the default Firebase channel id `fcm_fallback_notification_channel`
+
+### List channels
+
+You can list available channels with:
+
+```js
+PushNotification.getChannels(function (channel_ids) {
+ console.log(channel_ids); // ['channel_id_1']
});
-PushNotification.cancelLocalNotifications({id: '123'});
```
-#### IOS
+### Channel exists
-The `userInfo` parameter for `PushNotification.localNotification` is required for this operation and must contain an `id` parameter. The id supplied will then be used for the cancel operation.
+You can check if a channel exists with:
+
+```js
+PushNotification.channelExists(channel_id, function (exists) {
+ console.log(exists); // true/false
+});
+```
+
+### Channel blocked
+
+You can check if a channel blocked with:
+
+```js
+PushNotification.channelBlocked(channel_id, function (blocked) {
+ console.log(blocked); // true/false
+});
+```
+
+### Delete channel
+
+You can delete a channel with:
+
+```js
+PushNotification.deleteChannel(channel_id);
+```
+
+## Cancelling notifications
+
+### 1) cancelLocalNotifications
+
+The `id` parameter for `PushNotification.localNotification` is required for this operation. The id supplied will then be used for the cancel operation.
```javascript
-// IOS
PushNotification.localNotification({
...
- userInfo: { id: '123' }
+ id: '123'
...
});
PushNotification.cancelLocalNotifications({id: '123'});
```
+**iOS: `userInfo` is populated `id` if not defined this allow the previous method**
+
### 2) cancelAllLocalNotifications
`PushNotification.cancelAllLocalNotifications()`
Cancels all scheduled notifications AND clears the notifications alerts that are in the notification centre.
-_NOTE: there is currently no api for removing specific notification alerts from the notification centre._
-
### 3) removeAllDeliveredNotifications
```javascript
-PushNotificationIOS.removeAllDeliveredNotifications();
+PushNotification.removeAllDeliveredNotifications();
```
Remove all delivered notifications from Notification Center
@@ -384,7 +519,7 @@ Remove all delivered notifications from Notification Center
### 4) getDeliveredNotifications
```javascript
-PushNotificationIOS.getDeliveredNotifications(callback);
+PushNotification.getDeliveredNotifications(callback);
```
Provides you with a list of the app’s notifications that are still displayed in Notification Center
@@ -407,7 +542,7 @@ A delivered notification is an object containing:
### 5) removeDeliveredNotifications
```javascript
-PushNotificationIOS.removeDeliveredNotifications(identifiers);
+PushNotification.removeDeliveredNotifications(identifiers);
```
Removes the specified notifications from Notification Center
@@ -418,6 +553,32 @@ Removes the specified notifications from Notification Center
| ----------- | ----- | -------- | ---------------------------------- |
| identifiers | array | Yes | Array of notification identifiers. |
+### 6) getScheduledLocalNotifications
+
+```javascript
+PushNotification.getScheduledLocalNotifications(callback);
+```
+
+Provides you with a list of the app’s scheduled local notifications that are yet to be displayed
+
+**Parameters:**
+
+| Name | Type | Required | Description |
+| -------- | -------- | -------- | ----------------------------------------------------------- |
+| callback | function | Yes | Function which receive an array of delivered notifications. |
+
+Returns an array of local scheduled notification objects containing:
+
+| Name | Type | Description |
+| -------------- | ------ | -------------------------------------------------------- |
+| id | number | The identifier of this notification. |
+| date | Date | The fire date of this notification. |
+| title | string | The title of this notification. |
+| message | string | The message body of this notification. |
+| soundName | string | The sound name of this notification. |
+| repeatInterval | number | (Android only) The repeat interval of this notification. |
+| number | number | App notification badge count number. |
+| data | any | The user info of this notification. |
## Abandon Permissions
@@ -465,6 +626,12 @@ Available options:
More information: https://developer.android.com/reference/android/app/NotificationManager#IMPORTANCE_DEFAULT
+## Show notifications while the app is in foreground
+
+If you want a consistent results in Android & iOS with the most flexibility, it is best to handle it manually by prompting a local notification when `onNotification` is triggered by a remote push notification on foreground (check `notification.foreground` prop).
+
+Watch out for an infinite loop triggering `onNotification` - remote & local notification will trigger it. You can overcome this by marking local notifications' data.
+
## Notification while idle
(optional) Specify `allowWhileIdle` to set if the notification should be allowed to execute even when the system is on low-power idle modes.
@@ -478,48 +645,64 @@ https://developer.android.com/training/monitoring-device-state/doze-standby
## Repeating Notifications
-(optional) Specify `repeatType` and optionally `repeatTime` while scheduling the local notification. Check the local notification example above.
+(optional) Specify `repeatType` and optionally `repeatTime` (Android-only) while scheduling the local notification. Check the local notification example above.
+
+### iOS
+Property `repeatType` can only be `day`.
+### Android
Property `repeatType` could be one of `month`, `week`, `day`, `hour`, `minute`, `time`. If specified as time, it should be accompanied by one more parameter `repeatTime` which should the number of milliseconds between each interval.
## Notification Actions
-(Android only) [Refer](https://github.com/zo0r/react-native-push-notification/issues/151) to this issue to see an example of a notification action.
+(Android Only)
-Two things are required to setup notification actions.
+This is done by specifying an `actions` parameters while configuring the local notification. This is an array of strings where each string is a notification action that will be presented with the notification.
-### 1) Specify notification actions for a notification
+For e.g. `actions: ['Accept', 'Reject']`
-This is done by specifying an `actions` parameters while configuring the local notification. This is an array of strings where each string is a notification action that will be presented with the notification.
+When you handle actions in background (`invokeApp: false`), you can open the application and pass the initial notification by using use `PushNotification.invokeApp(notification)`.
+
+Make sure you have the receiver in `AndroidManifest.xml`:
+
+```xml
+
+```
-For e.g. `actions: '["Accept", "Reject"]' // Must be in string format`
+Notifications with inline reply:
-The array itself is specified in string format to circumvent some problems because of the way JSON arrays are handled by react-native android bridge.
+You must register an action as "ReplyInput", this will show in the notifications an input to write in.
-### 2) Specify handlers for the notification actions
+EXAMPLE:
+```javascript
+PushNotification.localNotificationSchedule({
+ message: "My Notification Message", // (required)
+ date: new Date(Date.now() + (60 * 1000)), // in 60 secs
+ actions: ["ReplyInput"],
+ reply_placeholder_text: "Write your response...", // (required)
+ reply_button_text: "Reply" // (required)
+});
+```
-For each action specified in the `actions` field, we need to add a handler that is called when the user clicks on the action. This can be done in the `componentWillMount` of your main app file or in a separate file which is imported in your main app file. Notification actions handlers can be configured as below:
+To get the text from the notification:
+```javascript
+...
+if(notification.action === "ReplyInput"){
+ console.log("texto", notification.reply_text)// this will contain the inline reply text.
+}
+...
```
-import PushNotificationAndroid from 'react-native-push-notification'
-
-(function() {
- // Register all the valid actions for notifications here and add the action handler for each action
- PushNotificationAndroid.registerNotificationActions(['Accept','Reject','Yes','No']);
- DeviceEventEmitter.addListener('notificationActionReceived', function(action){
- console.log ('Notification action received: ' + action);
- const info = JSON.parse(action.dataJSON);
- if (info.action == 'Accept') {
- // Do work pertaining to Accept action here
- } else if (info.action == 'Reject') {
- // Do work pertaining to Reject action here
- }
- // Add all the required actions handlers
- });
-})();
+
+For iOS, you can use:
+
+```javascript
+PushNotification.setNotificationCategories(categories);
```
-For iOS, you can use this [package](https://github.com/holmesal/react-native-ios-notification-actions) to add notification actions.
+And use the `category` field in the notification.
+
+Documentation [here](https://github.com/react-native-push-notification-ios/push-notification-ios#how-to-perform-different-action-based-on-user-selected-action) to add notification actions.
## Set application badge icon
@@ -529,16 +712,55 @@ Works natively in iOS.
Uses the [ShortcutBadger](https://github.com/leolin310148/ShortcutBadger) on Android, and as such will not work on all Android devices.
-## Sending Notification Data From Server
-
-Same parameters as `PushNotification.localNotification()`
-
## Android Only Methods
`PushNotification.subscribeToTopic(topic: string)` Subscribe to a topic (works only with Firebase)
`PushNotification.unsubscribeFromTopic(topic: string)` Unsubscribe from a topic (works only with Firebase)
+## Android Custom Notification Handling
+
+Unlike iOS, Android apps handle the creation of their own notifications. React Native Push Notifications does a "best guess" to create and handle incoming notifications. However, when using 3rd party notification platforms and tools, the initial notification creation process may need to be customized.
+
+### Customizing Notification Creation
+
+If your notification service uses a custom data payload format, React Native Push Notifications will not be able to parse the data correctly to create an initial notification.
+
+For these cases, you should:
+
+1. Remove the intent handler configuration for React Native Push Notifications from your `android/app/src/main/AndroidManifest.xml`.
+2. Implement initial notification creation as per the instructions from your Provider.
+
+### Handling Custom Payloads
+
+Data payloads of notifications from 3rd party services may not match the format expected by React Native Push Notification. When tapped, these notifications will not pass the details and data to the `onNotification()` event handler. Custom `IntentHandlers` allow you to fix this so that correct `notification` objects are sent to your `onNotification()` method.
+
+Custom handlers are added in Application init or `MainActivity.onCreate()` methods:
+
+```
+RNPushNotification.IntentHandlers.add(new RNPushNotification.RNIntentHandler() {
+ @Override
+ public void onNewIntent(Intent intent) {
+ // If your provider requires some parsing on the intent before the data can be
+ // used, add that code here. Otherwise leave empty.
+ }
+
+ @Nullable
+ @Override
+ public Bundle getBundleFromIntent(Intent intent) {
+ // This should return the bundle data that will be serialized to the `notification.data`
+ // property sent to the `onNotification()` handler. Return `null` if there is no data
+ // or this is not an intent from your provider.
+
+ // Example:
+ if (intent.hasExtra("MY_NOTIFICATION_PROVIDER_DATA_KEY")) {
+ return intent.getBundleExtra("MY_NOTIFICATION_PROVIDER_DATA_KEY");
+ }
+ return null;
+ }
+});
+```
+
## Checking Notification Permissions
`PushNotification.checkPermissions(callback: Function)` Check permissions
diff --git a/android/.classpath b/android/.classpath
new file mode 100644
index 000000000..eb19361b5
--- /dev/null
+++ b/android/.classpath
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/android/.project b/android/.project
index 3964dd3f5..3865e0fa4 100644
--- a/android/.project
+++ b/android/.project
@@ -5,6 +5,11 @@
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
org.eclipse.buildship.core.gradleprojectbuilder
@@ -12,6 +17,7 @@
+ org.eclipse.jdt.core.javanature
org.eclipse.buildship.core.gradleprojectnature
diff --git a/android/.settings/org.eclipse.buildship.core.prefs b/android/.settings/org.eclipse.buildship.core.prefs
index c25633f0a..342e81ef5 100644
--- a/android/.settings/org.eclipse.buildship.core.prefs
+++ b/android/.settings/org.eclipse.buildship.core.prefs
@@ -1,7 +1,7 @@
arguments=
auto.sync=false
build.scans.enabled=false
-connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(6.3))
+connection.gradle.distribution=GRADLE_DISTRIBUTION(WRAPPER)
connection.project.dir=
eclipse.preferences.version=1
gradle.user.home=
diff --git a/android/build.gradle b/android/build.gradle
index 141fec0b2..040255816 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,5 +1,6 @@
buildscript {
repositories {
+ mavenCentral()
google()
jcenter()
}
@@ -10,6 +11,7 @@ buildscript {
allprojects {
repositories {
+ mavenCentral()
google()
jcenter()
}
diff --git a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotification.java b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotification.java
index 98fa1b185..0e940916e 100644
--- a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotification.java
+++ b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotification.java
@@ -8,8 +8,8 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
-
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.core.app.NotificationManagerCompat;
import com.dieam.reactnativepushnotification.helpers.ApplicationBadgeHelper;
@@ -22,13 +22,15 @@
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.ReadableArray;
+import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableMap;
import java.io.IOException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
-import java.util.Random;
import android.util.Log;
@@ -40,9 +42,19 @@
public class RNPushNotification extends ReactContextBaseJavaModule implements ActivityEventListener {
public static final String LOG_TAG = "RNPushNotification";// all logging should use this tag
+ public static final String KEY_TEXT_REPLY = "key_text_reply";
+
+ public interface RNIntentHandler {
+ void onNewIntent(Intent intent);
+
+ @Nullable
+ Bundle getBundleFromIntent(Intent intent);
+ }
+
+ public static ArrayList IntentHandlers = new ArrayList();
private RNPushNotificationHelper mRNPushNotificationHelper;
- private final Random mRandomNumberGenerator = new Random(System.currentTimeMillis());
+ private final SecureRandom mRandomNumberGenerator = new SecureRandom();
private RNPushNotificationJsDelivery mJsDelivery;
public RNPushNotification(ReactApplicationContext reactContext) {
@@ -56,13 +68,11 @@ public RNPushNotification(ReactApplicationContext reactContext) {
mRNPushNotificationHelper = new RNPushNotificationHelper(applicationContext);
// This is used to delivery callbacks to JS
mJsDelivery = new RNPushNotificationJsDelivery(reactContext);
-
- mRNPushNotificationHelper.checkOrCreateDefaultChannel();
}
@Override
public String getName() {
- return "RNPushNotification";
+ return "ReactNativePushNotification";
}
@Override
@@ -77,41 +87,45 @@ private Bundle getBundleFromIntent(Intent intent) {
if (intent.hasExtra("notification")) {
bundle = intent.getBundleExtra("notification");
} else if (intent.hasExtra("google.message_id")) {
- bundle = intent.getExtras();
+ bundle = new Bundle();
+
+ bundle.putBundle("data", intent.getExtras());
+ }
+
+ if (bundle == null) {
+ for (RNIntentHandler handler : IntentHandlers) {
+ bundle = handler.getBundleFromIntent(intent);
+ }
}
+
+ if(null != bundle && !bundle.getBoolean("foreground", false) && !bundle.containsKey("userInteraction")) {
+ bundle.putBoolean("userInteraction", true);
+ }
+
return bundle;
}
+
+ @Override
public void onNewIntent(Intent intent) {
+ for (RNIntentHandler handler : IntentHandlers) {
+ handler.onNewIntent(intent);
+ }
+
Bundle bundle = this.getBundleFromIntent(intent);
if (bundle != null) {
- bundle.putBoolean("foreground", false);
- intent.putExtra("notification", bundle);
mJsDelivery.notifyNotification(bundle);
}
}
- private void registerNotificationsReceiveNotificationActions(ReadableArray actions) {
- IntentFilter intentFilter = new IntentFilter();
- // Add filter for each actions.
- for (int i = 0; i < actions.size(); i++) {
- String action = actions.getString(i);
- intentFilter.addAction(getReactApplicationContext().getPackageName() + "." + action);
+ @ReactMethod
+ public void invokeApp(ReadableMap data) {
+ Bundle bundle = null;
+
+ if (data != null) {
+ bundle = Arguments.toBundle(data);
}
-
- getReactApplicationContext().registerReceiver(new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- Bundle bundle = intent.getBundleExtra("notification");
-
- // Notify the action.
- mJsDelivery.notifyNotificationAction(bundle);
-
- // Dismiss the notification popup.
- NotificationManager manager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
- int notificationID = Integer.parseInt(bundle.getString("id"));
- manager.cancel(notificationID);
- }
- }, intentFilter);
+
+ mRNPushNotificationHelper.invokeApp(bundle);
}
@ReactMethod
@@ -225,13 +239,8 @@ public void cancelLocalNotifications(ReadableMap userInfo) {
/**
* Clear notification from the notification centre.
*/
- public void clearLocalNotification(int notificationID) {
- mRNPushNotificationHelper.clearNotification(notificationID);
- }
-
- @ReactMethod
- public void registerNotificationActions(ReadableArray actions) {
- registerNotificationsReceiveNotificationActions(actions);
+ public void clearLocalNotification(String tag, int notificationID) {
+ mRNPushNotificationHelper.clearNotification(tag, notificationID);
}
@ReactMethod
@@ -248,7 +257,15 @@ public void removeAllDeliveredNotifications() {
* Returns a list of all notifications currently in the Notification Center
*/
public void getDeliveredNotifications(Callback callback) {
- callback.invoke(mRNPushNotificationHelper.getDeliveredNotifications());
+ callback.invoke(mRNPushNotificationHelper.getDeliveredNotifications());
+ }
+
+ @ReactMethod
+ /**
+ * Returns a list of all currently scheduled notifications
+ */
+ public void getScheduledLocalNotifications(Callback callback) {
+ callback.invoke(mRNPushNotificationHelper.getScheduledLocalNotifications());
}
@ReactMethod
@@ -277,4 +294,60 @@ public void run() {
}
}).start();
}
+
+ @ReactMethod
+ /**
+ * List all channels id
+ */
+ public void getChannels(Callback callback) {
+ WritableArray array = Arguments.fromList(mRNPushNotificationHelper.listChannels());
+
+ if(callback != null) {
+ callback.invoke(array);
+ }
+ }
+
+ @ReactMethod
+ /**
+ * Check if channel exists with a given id
+ */
+ public void channelExists(String channel_id, Callback callback) {
+ boolean exists = mRNPushNotificationHelper.channelExists(channel_id);
+
+ if(callback != null) {
+ callback.invoke(exists);
+ }
+ }
+
+ @ReactMethod
+ /**
+ * Creates a channel if it does not already exist. Returns whether the channel was created.
+ */
+ public void createChannel(ReadableMap channelInfo, Callback callback) {
+ boolean created = mRNPushNotificationHelper.createChannel(channelInfo);
+
+ if(callback != null) {
+ callback.invoke(created);
+ }
+ }
+
+ @ReactMethod
+ /**
+ * Check if channel is blocked with a given id
+ */
+ public void channelBlocked(String channel_id, Callback callback) {
+ boolean blocked = mRNPushNotificationHelper.channelBlocked(channel_id);
+
+ if(callback != null) {
+ callback.invoke(blocked);
+ }
+ }
+
+ @ReactMethod
+ /**
+ * Delete channel with a given id
+ */
+ public void deleteChannel(String channel_id) {
+ mRNPushNotificationHelper.deleteChannel(channel_id);
+ }
}
diff --git a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationActions.java b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationActions.java
new file mode 100644
index 000000000..ada960388
--- /dev/null
+++ b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationActions.java
@@ -0,0 +1,102 @@
+package com.dieam.reactnativepushnotification.modules;
+
+import android.app.Application;
+import android.app.NotificationManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+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 androidx.core.app.RemoteInput;
+
+import static com.dieam.reactnativepushnotification.modules.RNPushNotification.LOG_TAG;
+import static com.dieam.reactnativepushnotification.modules.RNPushNotification.KEY_TEXT_REPLY;
+
+public class RNPushNotificationActions extends BroadcastReceiver {
+ @Override
+ public void onReceive(final Context context, Intent intent) {
+ String intentActionPrefix = context.getPackageName() + ".ACTION_";
+
+ Log.i(LOG_TAG, "RNPushNotificationBootEventReceiver loading scheduled notifications");
+
+ if (null == intent.getAction() || !intent.getAction().startsWith(intentActionPrefix)) {
+ return;
+ }
+
+ final Bundle bundle = intent.getBundleExtra("notification");
+ Bundle remoteInput = null;
+
+ if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT_WATCH){
+ remoteInput = RemoteInput.getResultsFromIntent(intent);
+ }
+ if (remoteInput != null) {
+ // Add to reply_text the text written by the user in the notification
+ bundle.putCharSequence("reply_text", remoteInput.getCharSequence(KEY_TEXT_REPLY));
+ }
+ // Dismiss the notification popup.
+ NotificationManager manager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
+ int notificationID = Integer.parseInt(bundle.getString("id"));
+
+ boolean autoCancel = bundle.getBoolean("autoCancel", true);
+
+ if(autoCancel) {
+ if (bundle.containsKey("tag")) {
+ String tag = bundle.getString("tag");
+ manager.cancel(tag, notificationID);
+ } else {
+ manager.cancel(notificationID);
+ }
+ }
+
+ boolean invokeApp = bundle.getBoolean("invokeApp", true);
+
+ // Notify the action.
+ if(invokeApp) {
+ RNPushNotificationHelper helper = new RNPushNotificationHelper((Application) context.getApplicationContext());
+
+ helper.invokeApp(bundle);
+
+ context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+ } else {
+
+ // We need to run this on the main thread, as the React code assumes that is true.
+ // Namely, DevServerHelper constructs a Handler() without a Looper, which triggers:
+ // "Can't create handler inside thread that has not called Looper.prepare()"
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+ public void run() {
+ // Construct and load our normal React JS code bundle
+ final ReactInstanceManager mReactInstanceManager = ((ReactApplication) context.getApplicationContext()).getReactNativeHost().getReactInstanceManager();
+ ReactContext context = mReactInstanceManager.getCurrentReactContext();
+ // If it's constructed, send a notification
+ if (context != null) {
+ RNPushNotificationJsDelivery mJsDelivery = new RNPushNotificationJsDelivery(context);
+
+ mJsDelivery.notifyNotificationAction(bundle);
+ } else {
+ // Otherwise wait for construction, then send the notification
+ mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
+ public void onReactContextInitialized(ReactContext context) {
+ RNPushNotificationJsDelivery mJsDelivery = new RNPushNotificationJsDelivery(context);
+
+ mJsDelivery.notifyNotificationAction(bundle);
+
+ mReactInstanceManager.removeReactInstanceEventListener(this);
+ }
+ });
+ if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
+ // Construct it in the background
+ mReactInstanceManager.createReactContextInBackground();
+ }
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationAttributes.java b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationAttributes.java
index 0d2099ab7..a2d3d5657 100644
--- a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationAttributes.java
+++ b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationAttributes.java
@@ -10,6 +10,8 @@
import org.json.JSONException;
import org.json.JSONObject;
+import java.util.Iterator;
+
import static com.dieam.reactnativepushnotification.modules.RNPushNotification.LOG_TAG;
public class RNPushNotificationAttributes {
@@ -18,48 +20,80 @@ public class RNPushNotificationAttributes {
private static final String FIRE_DATE = "fireDate";
private static final String TITLE = "title";
private static final String TICKER = "ticker";
+ private static final String SHOW_WHEN = "showWhen";
private static final String AUTO_CANCEL = "autoCancel";
private static final String LARGE_ICON = "largeIcon";
+ private static final String LARGE_ICON_URL = "largeIconUrl";
private static final String SMALL_ICON = "smallIcon";
private static final String BIG_TEXT = "bigText";
private static final String SUB_TEXT = "subText";
+ private static final String BIG_PICTURE_URL = "bigPictureUrl";
+ private static final String SHORTCUT_ID = "shortcutId";
+ private static final String CHANNEL_ID = "channelId";
private static final String NUMBER = "number";
private static final String SOUND = "sound";
private static final String COLOR = "color";
private static final String GROUP = "group";
- private static final String USER_INTERACTION = "userInteraction";
+ private static final String GROUP_SUMMARY = "groupSummary";
+ private static final String MESSAGE_ID = "messageId";
private static final String PLAY_SOUND = "playSound";
private static final String VIBRATE = "vibrate";
private static final String VIBRATION = "vibration";
private static final String ACTIONS = "actions";
+ private static final String INVOKE_APP = "invokeApp";
private static final String TAG = "tag";
private static final String REPEAT_TYPE = "repeatType";
private static final String REPEAT_TIME = "repeatTime";
+ private static final String WHEN = "when";
+ private static final String USES_CHRONOMETER = "usesChronometer";
+ private static final String TIMEOUT_AFTER = "timeoutAfter";
+ private static final String ONLY_ALERT_ONCE = "onlyAlertOnce";
private static final String ONGOING = "ongoing";
+ private static final String REPLY_BUTTON_TEXT = "reply_button_text";
+ private static final String REPLAY_PLACEHOLDER_TEXT = "reply_placeholder_text";
+ private static final String ALLOW_WHILE_IDLE = "allowWhileIdle";
+ private static final String IGNORE_IN_FOREGROUND = "ignoreInForeground";
+ private static final String USER_INFO = "userInfo";
private final String id;
private final String message;
private final double fireDate;
private final String title;
private final String ticker;
+ private final boolean showWhen;
private final boolean autoCancel;
private final String largeIcon;
+ private final String largeIconUrl;
private final String smallIcon;
private final String bigText;
private final String subText;
+ private final String bigPictureUrl;
+ private final String shortcutId;
private final String number;
+ private final String channelId;
private final String sound;
private final String color;
private final String group;
- private final boolean userInteraction;
+ private final boolean groupSummary;
+ private final String messageId;
private final boolean playSound;
private final boolean vibrate;
private final double vibration;
private final String actions;
+ private final boolean invokeApp;
private final String tag;
private final String repeatType;
private final double repeatTime;
+ private final double when;
+ private final boolean usesChronometer;
+ private final double timeoutAfter;
+ private final boolean onlyAlertOnce;
private final boolean ongoing;
+ private final String reply_button_text;
+ private final String reply_placeholder_text;
+ private final boolean allowWhileIdle;
+ private final boolean ignoreInForeground;
+ private final String userInfo;
public RNPushNotificationAttributes(Bundle bundle) {
id = bundle.getString(ID);
@@ -67,24 +101,40 @@ public RNPushNotificationAttributes(Bundle bundle) {
fireDate = bundle.getDouble(FIRE_DATE);
title = bundle.getString(TITLE);
ticker = bundle.getString(TICKER);
+ showWhen = bundle.getBoolean(SHOW_WHEN);
autoCancel = bundle.getBoolean(AUTO_CANCEL);
largeIcon = bundle.getString(LARGE_ICON);
+ largeIconUrl = bundle.getString(LARGE_ICON_URL);
smallIcon = bundle.getString(SMALL_ICON);
bigText = bundle.getString(BIG_TEXT);
subText = bundle.getString(SUB_TEXT);
+ bigPictureUrl= bundle.getString(BIG_PICTURE_URL);
+ shortcutId = bundle.getString(SHORTCUT_ID);
number = bundle.getString(NUMBER);
+ channelId = bundle.getString(CHANNEL_ID);
sound = bundle.getString(SOUND);
color = bundle.getString(COLOR);
group = bundle.getString(GROUP);
- userInteraction = bundle.getBoolean(USER_INTERACTION);
+ groupSummary = bundle.getBoolean(GROUP_SUMMARY);
+ messageId = bundle.getString(MESSAGE_ID);
playSound = bundle.getBoolean(PLAY_SOUND);
vibrate = bundle.getBoolean(VIBRATE);
vibration = bundle.getDouble(VIBRATION);
actions = bundle.getString(ACTIONS);
+ invokeApp = bundle.getBoolean(INVOKE_APP);
tag = bundle.getString(TAG);
repeatType = bundle.getString(REPEAT_TYPE);
repeatTime = bundle.getDouble(REPEAT_TIME);
+ when = bundle.getDouble(WHEN);
+ usesChronometer = bundle.getBoolean(USES_CHRONOMETER);
+ timeoutAfter = bundle.getDouble(TIMEOUT_AFTER);
+ onlyAlertOnce = bundle.getBoolean(ONLY_ALERT_ONCE);
ongoing = bundle.getBoolean(ONGOING);
+ reply_button_text = bundle.getString(REPLY_BUTTON_TEXT);
+ reply_placeholder_text = bundle.getString(REPLAY_PLACEHOLDER_TEXT);
+ allowWhileIdle = bundle.getBoolean(ALLOW_WHILE_IDLE);
+ ignoreInForeground = bundle.getBoolean(IGNORE_IN_FOREGROUND);
+ userInfo = bundle.getString(USER_INFO);
}
private RNPushNotificationAttributes(JSONObject jsonObject) {
@@ -94,24 +144,40 @@ private RNPushNotificationAttributes(JSONObject jsonObject) {
fireDate = jsonObject.has(FIRE_DATE) ? jsonObject.getDouble(FIRE_DATE) : 0.0;
title = jsonObject.has(TITLE) ? jsonObject.getString(TITLE) : null;
ticker = jsonObject.has(TICKER) ? jsonObject.getString(TICKER) : null;
+ showWhen = jsonObject.has(SHOW_WHEN) ? jsonObject.getBoolean(SHOW_WHEN) : true;
autoCancel = jsonObject.has(AUTO_CANCEL) ? jsonObject.getBoolean(AUTO_CANCEL) : true;
largeIcon = jsonObject.has(LARGE_ICON) ? jsonObject.getString(LARGE_ICON) : null;
+ largeIconUrl = jsonObject.has(LARGE_ICON_URL) ? jsonObject.getString(LARGE_ICON_URL) : null;
smallIcon = jsonObject.has(SMALL_ICON) ? jsonObject.getString(SMALL_ICON) : null;
bigText = jsonObject.has(BIG_TEXT) ? jsonObject.getString(BIG_TEXT) : null;
subText = jsonObject.has(SUB_TEXT) ? jsonObject.getString(SUB_TEXT) : null;
+ bigPictureUrl = jsonObject.has(BIG_PICTURE_URL) ? jsonObject.getString(BIG_PICTURE_URL) : null;
+ shortcutId = jsonObject.has(SHORTCUT_ID) ? jsonObject.getString(SHORTCUT_ID) : null;
number = jsonObject.has(NUMBER) ? jsonObject.getString(NUMBER) : null;
+ channelId = jsonObject.has(CHANNEL_ID) ? jsonObject.getString(CHANNEL_ID) : null;
sound = jsonObject.has(SOUND) ? jsonObject.getString(SOUND) : null;
color = jsonObject.has(COLOR) ? jsonObject.getString(COLOR) : null;
group = jsonObject.has(GROUP) ? jsonObject.getString(GROUP) : null;
- userInteraction = jsonObject.has(USER_INTERACTION) ? jsonObject.getBoolean(USER_INTERACTION) : false;
+ groupSummary = jsonObject.has(GROUP_SUMMARY) ? jsonObject.getBoolean(GROUP_SUMMARY) : false;
+ messageId = jsonObject.has(MESSAGE_ID) ? jsonObject.getString(MESSAGE_ID) : null;
playSound = jsonObject.has(PLAY_SOUND) ? jsonObject.getBoolean(PLAY_SOUND) : true;
vibrate = jsonObject.has(VIBRATE) ? jsonObject.getBoolean(VIBRATE) : true;
vibration = jsonObject.has(VIBRATION) ? jsonObject.getDouble(VIBRATION) : 1000;
actions = jsonObject.has(ACTIONS) ? jsonObject.getString(ACTIONS) : null;
+ invokeApp = jsonObject.has(INVOKE_APP) ? jsonObject.getBoolean(INVOKE_APP) : true;
tag = jsonObject.has(TAG) ? jsonObject.getString(TAG) : null;
repeatType = jsonObject.has(REPEAT_TYPE) ? jsonObject.getString(REPEAT_TYPE) : null;
repeatTime = jsonObject.has(REPEAT_TIME) ? jsonObject.getDouble(REPEAT_TIME) : 0.0;
+ when = jsonObject.has(WHEN) ? jsonObject.getDouble(WHEN) : -1;
+ usesChronometer = jsonObject.has(USES_CHRONOMETER) ? jsonObject.getBoolean(USES_CHRONOMETER) : false;
+ timeoutAfter = jsonObject.has(TIMEOUT_AFTER) ? jsonObject.getDouble(TIMEOUT_AFTER) : -1;
+ onlyAlertOnce = jsonObject.has(ONLY_ALERT_ONCE) ? jsonObject.getBoolean(ONLY_ALERT_ONCE) : false;
ongoing = jsonObject.has(ONGOING) ? jsonObject.getBoolean(ONGOING) : false;
+ reply_button_text = jsonObject.has(REPLY_BUTTON_TEXT) ? jsonObject.getString(REPLY_BUTTON_TEXT) : null;
+ reply_placeholder_text = jsonObject.has(REPLAY_PLACEHOLDER_TEXT) ? jsonObject.getString(REPLAY_PLACEHOLDER_TEXT) : null;
+ allowWhileIdle = jsonObject.has(ALLOW_WHILE_IDLE) ? jsonObject.getBoolean(ALLOW_WHILE_IDLE) : false;
+ ignoreInForeground = jsonObject.has(IGNORE_IN_FOREGROUND) ? jsonObject.getBoolean(IGNORE_IN_FOREGROUND) : false;
+ userInfo = jsonObject.has(USER_INFO) ? jsonObject.getString(USER_INFO) : null;
} catch (JSONException e) {
throw new IllegalStateException("Exception while initializing RNPushNotificationAttributes from JSON", e);
}
@@ -120,6 +186,7 @@ private RNPushNotificationAttributes(JSONObject jsonObject) {
@NonNull
public static RNPushNotificationAttributes fromJson(String notificationAttributesJson) throws JSONException {
JSONObject jsonObject = new JSONObject(notificationAttributesJson);
+
return new RNPushNotificationAttributes(jsonObject);
}
@@ -131,41 +198,49 @@ public static RNPushNotificationAttributes fromJson(String notificationAttribute
* @return true all fields in userInfo object match, false otherwise
*/
public boolean matches(ReadableMap userInfo) {
- Bundle bundle = toBundle();
+ try {
+ if(this.userInfo == null) {
+ return false;
+ }
- ReadableMapKeySetIterator iterator = userInfo.keySetIterator();
- while (iterator.hasNextKey()) {
- String key = iterator.nextKey();
+ JSONObject jsonObject = new JSONObject(this.userInfo);
- if (!bundle.containsKey(key))
- return false;
+ ReadableMapKeySetIterator iterator = userInfo.keySetIterator();
+ while (iterator.hasNextKey()) {
+ String key = iterator.nextKey();
- switch (userInfo.getType(key)) {
- case Null: {
- if (bundle.get(key) != null)
- return false;
- break;
- }
- case Boolean: {
- if (userInfo.getBoolean(key) != bundle.getBoolean(key))
- return false;
- break;
- }
- case Number: {
- if ((userInfo.getDouble(key) != bundle.getDouble(key)) && (userInfo.getInt(key) != bundle.getInt(key)))
- return false;
- break;
- }
- case String: {
- if (!userInfo.getString(key).equals(bundle.getString(key)))
- return false;
- break;
- }
- case Map:
- return false;//there are no maps in the bundle
- case Array:
- return false;//there are no arrays in the bundle
- }
+ if (!jsonObject.has(key))
+ return false;
+
+ switch (userInfo.getType(key)) {
+ case Null: {
+ if (jsonObject.get(key) != null)
+ return false;
+ break;
+ }
+ case Boolean: {
+ if (userInfo.getBoolean(key) != jsonObject.getBoolean(key))
+ return false;
+ break;
+ }
+ case Number: {
+ if ((userInfo.getDouble(key) != jsonObject.getDouble(key)) && (userInfo.getInt(key) != jsonObject.getInt(key)))
+ return false;
+ break;
+ }
+ case String: {
+ if (!userInfo.getString(key).equals(jsonObject.getString(key)))
+ return false;
+ break;
+ }
+ case Map:
+ return false;//there are no maps in the jsonObject
+ case Array:
+ return false;//there are no arrays in the jsonObject
+ }
+ }
+ } catch(JSONException e) {
+ return false;
}
return true;
@@ -178,24 +253,40 @@ public Bundle toBundle() {
bundle.putDouble(FIRE_DATE, fireDate);
bundle.putString(TITLE, title);
bundle.putString(TICKER, ticker);
+ bundle.putBoolean(SHOW_WHEN, showWhen);
bundle.putBoolean(AUTO_CANCEL, autoCancel);
bundle.putString(LARGE_ICON, largeIcon);
+ bundle.putString(LARGE_ICON_URL, largeIconUrl);
bundle.putString(SMALL_ICON, smallIcon);
bundle.putString(BIG_TEXT, bigText);
bundle.putString(SUB_TEXT, subText);
+ bundle.putString(BIG_PICTURE_URL, bigPictureUrl);
+ bundle.putString(SHORTCUT_ID, shortcutId);
bundle.putString(NUMBER, number);
+ bundle.putString(CHANNEL_ID, channelId);
bundle.putString(SOUND, sound);
bundle.putString(COLOR, color);
bundle.putString(GROUP, group);
- bundle.putBoolean(USER_INTERACTION, userInteraction);
+ bundle.putBoolean(GROUP_SUMMARY, groupSummary);
+ bundle.putString(MESSAGE_ID, messageId);
bundle.putBoolean(PLAY_SOUND, playSound);
bundle.putBoolean(VIBRATE, vibrate);
bundle.putDouble(VIBRATION, vibration);
bundle.putString(ACTIONS, actions);
+ bundle.putBoolean(INVOKE_APP, invokeApp);
bundle.putString(TAG, tag);
bundle.putString(REPEAT_TYPE, repeatType);
bundle.putDouble(REPEAT_TIME, repeatTime);
+ bundle.putDouble(WHEN, when);
+ bundle.putBoolean(USES_CHRONOMETER, usesChronometer);
+ bundle.putDouble(TIMEOUT_AFTER, timeoutAfter);
+ bundle.putBoolean(ONLY_ALERT_ONCE, onlyAlertOnce);
bundle.putBoolean(ONGOING, ongoing);
+ bundle.putString(REPLY_BUTTON_TEXT, reply_button_text);
+ bundle.putString(REPLAY_PLACEHOLDER_TEXT, reply_placeholder_text);
+ bundle.putBoolean(ALLOW_WHILE_IDLE, allowWhileIdle);
+ bundle.putBoolean(IGNORE_IN_FOREGROUND, ignoreInForeground);
+ bundle.putString(USER_INFO, userInfo);
return bundle;
}
@@ -207,24 +298,40 @@ public JSONObject toJson() {
jsonObject.put(FIRE_DATE, fireDate);
jsonObject.put(TITLE, title);
jsonObject.put(TICKER, ticker);
+ jsonObject.put(SHOW_WHEN, showWhen);
jsonObject.put(AUTO_CANCEL, autoCancel);
jsonObject.put(LARGE_ICON, largeIcon);
+ jsonObject.put(LARGE_ICON_URL, largeIconUrl);
jsonObject.put(SMALL_ICON, smallIcon);
jsonObject.put(BIG_TEXT, bigText);
+ jsonObject.put(BIG_PICTURE_URL, bigPictureUrl);
jsonObject.put(SUB_TEXT, subText);
+ jsonObject.put(SHORTCUT_ID, shortcutId);
jsonObject.put(NUMBER, number);
+ jsonObject.put(CHANNEL_ID, channelId);
jsonObject.put(SOUND, sound);
jsonObject.put(COLOR, color);
jsonObject.put(GROUP, group);
- jsonObject.put(USER_INTERACTION, userInteraction);
+ jsonObject.put(GROUP_SUMMARY, groupSummary);
+ jsonObject.put(MESSAGE_ID, messageId);
jsonObject.put(PLAY_SOUND, playSound);
jsonObject.put(VIBRATE, vibrate);
jsonObject.put(VIBRATION, vibration);
jsonObject.put(ACTIONS, actions);
+ jsonObject.put(INVOKE_APP, invokeApp);
jsonObject.put(TAG, tag);
jsonObject.put(REPEAT_TYPE, repeatType);
jsonObject.put(REPEAT_TIME, repeatTime);
+ jsonObject.put(WHEN, when);
+ jsonObject.put(USES_CHRONOMETER, usesChronometer);
+ jsonObject.put(TIMEOUT_AFTER, timeoutAfter);
+ jsonObject.put(ONLY_ALERT_ONCE, onlyAlertOnce);
jsonObject.put(ONGOING, ongoing);
+ jsonObject.put(REPLY_BUTTON_TEXT, reply_button_text);
+ jsonObject.put(REPLAY_PLACEHOLDER_TEXT, reply_placeholder_text);
+ jsonObject.put(ALLOW_WHILE_IDLE, allowWhileIdle);
+ jsonObject.put(IGNORE_IN_FOREGROUND, ignoreInForeground);
+ jsonObject.put(USER_INFO, userInfo);
} catch (JSONException e) {
Log.e(LOG_TAG, "Exception while converting RNPushNotificationAttributes to " +
"JSON. Returning an empty object", e);
@@ -242,24 +349,40 @@ public String toString() {
", fireDate=" + fireDate +
", title='" + title + '\'' +
", ticker='" + ticker + '\'' +
+ ", showWhen=" + showWhen +
", autoCancel=" + autoCancel +
", largeIcon='" + largeIcon + '\'' +
+ ", largeIconUrl='" + largeIconUrl + '\'' +
", smallIcon='" + smallIcon + '\'' +
", bigText='" + bigText + '\'' +
", subText='" + subText + '\'' +
+ ", bigPictureUrl='" + bigPictureUrl + '\'' +
+ ", shortcutId='" + shortcutId + '\'' +
", number='" + number + '\'' +
+ ", channelId='" + channelId + '\'' +
", sound='" + sound + '\'' +
", color='" + color + '\'' +
", group='" + group + '\'' +
- ", userInteraction=" + userInteraction +
+ ", groupSummary='" + groupSummary + '\'' +
+ ", messageId='" + messageId + '\'' +
", playSound=" + playSound +
", vibrate=" + vibrate +
", vibration=" + vibration +
", actions='" + actions + '\'' +
+ ", invokeApp=" + invokeApp +
", tag='" + tag + '\'' +
", repeatType='" + repeatType + '\'' +
", repeatTime=" + repeatTime +
+ ", when=" + when +
+ ", usesChronometer=" + usesChronometer +
+ ", timeoutAfter=" + timeoutAfter +
+ ", onlyAlertOnce=" + onlyAlertOnce +
", ongoing=" + ongoing +
+ ", reply_button_text=" + reply_button_text +
+ ", reply_placeholder_text=" + reply_placeholder_text +
+ ", allowWhileIdle=" + allowWhileIdle +
+ ", ignoreInForeground=" + ignoreInForeground +
+ ", userInfo=" + userInfo +
'}';
}
@@ -267,8 +390,31 @@ public String getId() {
return id;
}
+ public String getSound() {
+ return sound;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public String getNumber() {
+ return number;
+ }
+
+ public String getUserInfo() {
+ return userInfo;
+ }
+
+ public String getRepeatType() {
+ return repeatType;
+ }
+
public double getFireDate() {
return fireDate;
}
-
}
diff --git a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationBootEventReceiver.java b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationBootEventReceiver.java
index 82a575883..6552a87ec 100644
--- a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationBootEventReceiver.java
+++ b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationBootEventReceiver.java
@@ -20,32 +20,30 @@ public class RNPushNotificationBootEventReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
Log.i(LOG_TAG, "RNPushNotificationBootEventReceiver loading scheduled notifications");
- if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
- SharedPreferences sharedPreferences = context.getSharedPreferences(RNPushNotificationHelper.PREFERENCES_KEY, Context.MODE_PRIVATE);
- Set ids = sharedPreferences.getAll().keySet();
-
- Application applicationContext = (Application) context.getApplicationContext();
- RNPushNotificationHelper rnPushNotificationHelper = new RNPushNotificationHelper(applicationContext);
-
- for (String id : ids) {
- try {
- String notificationAttributesJson = sharedPreferences.getString(id, null);
- if (notificationAttributesJson != null) {
- RNPushNotificationAttributes notificationAttributes = RNPushNotificationAttributes.fromJson(notificationAttributesJson);
-
- if (notificationAttributes.getFireDate() < System.currentTimeMillis()) {
- Log.i(LOG_TAG, "RNPushNotificationBootEventReceiver: Showing notification for " +
- notificationAttributes.getId());
- rnPushNotificationHelper.sendToNotificationCentre(notificationAttributes.toBundle());
- } else {
- Log.i(LOG_TAG, "RNPushNotificationBootEventReceiver: Scheduling notification for " +
- notificationAttributes.getId());
- rnPushNotificationHelper.sendNotificationScheduledCore(notificationAttributes.toBundle());
- }
+ SharedPreferences sharedPreferences = context.getSharedPreferences(RNPushNotificationHelper.PREFERENCES_KEY, Context.MODE_PRIVATE);
+ Set ids = sharedPreferences.getAll().keySet();
+
+ Application applicationContext = (Application) context.getApplicationContext();
+ RNPushNotificationHelper rnPushNotificationHelper = new RNPushNotificationHelper(applicationContext);
+
+ for (String id : ids) {
+ try {
+ String notificationAttributesJson = sharedPreferences.getString(id, null);
+ if (notificationAttributesJson != null) {
+ RNPushNotificationAttributes notificationAttributes = RNPushNotificationAttributes.fromJson(notificationAttributesJson);
+
+ if (notificationAttributes.getFireDate() < System.currentTimeMillis()) {
+ Log.i(LOG_TAG, "RNPushNotificationBootEventReceiver: Showing notification for " +
+ notificationAttributes.getId());
+ rnPushNotificationHelper.sendToNotificationCentre(notificationAttributes.toBundle());
+ } else {
+ Log.i(LOG_TAG, "RNPushNotificationBootEventReceiver: Scheduling notification for " +
+ notificationAttributes.getId());
+ rnPushNotificationHelper.sendNotificationScheduledCore(notificationAttributes.toBundle());
}
- } catch (Exception e) {
- Log.e(LOG_TAG, "Problem with boot receiver loading notification " + id, e);
}
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Problem with boot receiver loading notification " + id, e);
}
}
}
diff --git a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationConfig.java b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationConfig.java
index f65a8dfc5..e17927753 100644
--- a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationConfig.java
+++ b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationConfig.java
@@ -8,8 +8,8 @@
import android.util.Log;
class RNPushNotificationConfig {
- private static final String KEY_CHANNEL_NAME = "com.dieam.reactnativepushnotification.notification_channel_name";
- private static final String KEY_CHANNEL_DESCRIPTION = "com.dieam.reactnativepushnotification.notification_channel_description";
+ private static final String KEY_NOTIFICATION_FIREBASE_DEFAULT_CHANNEL_ID = "com.google.firebase.messaging.default_notification_channel_id";
+ private static final String KEY_NOTIFICATION_DEFAULT_CHANNEL_ID = "com.dieam.reactnativepushnotification.default_notification_channel_id";
private static final String KEY_NOTIFICATION_FOREGROUND = "com.dieam.reactnativepushnotification.notification_foreground";
private static final String KEY_NOTIFICATION_COLOR = "com.dieam.reactnativepushnotification.notification_color";
@@ -30,29 +30,19 @@ public RNPushNotificationConfig(Context context) {
}
}
- public String getChannelName() {
+ private String getStringValue(String key, String defaultValue) {
try {
- final String name = metadata.getString(KEY_CHANNEL_NAME);
- if (name != null && name.length() > 0) {
- return name;
- }
- } catch (Exception e) {
- Log.w(RNPushNotification.LOG_TAG, "Unable to find " + KEY_CHANNEL_NAME + " in manifest. Falling back to default");
- }
- // Default
- return "rn-push-notification-channel";
- }
- public String getChannelDescription() {
- try {
- final String description = metadata.getString(KEY_CHANNEL_DESCRIPTION);
- if (description != null) {
- return description;
+ final String value = metadata.getString(key);
+
+ if (value != null && value.length() > 0) {
+ return value;
}
} catch (Exception e) {
- Log.w(RNPushNotification.LOG_TAG, "Unable to find " + KEY_CHANNEL_DESCRIPTION + " in manifest. Falling back to default");
+ Log.w(RNPushNotification.LOG_TAG, "Unable to find " + key + " in manifest. Falling back to default");
}
+
// Default
- return "";
+ return defaultValue;
}
public int getNotificationColor() {
@@ -75,4 +65,16 @@ public boolean getNotificationForeground() {
// Default
return false;
}
+
+ public String getNotificationDefaultChannelId() {
+ try {
+ return getStringValue(KEY_NOTIFICATION_DEFAULT_CHANNEL_ID,
+ getStringValue(KEY_NOTIFICATION_FIREBASE_DEFAULT_CHANNEL_ID, "fcm_fallback_notification_channel")
+ );
+ } catch (Exception e) {
+ Log.w(RNPushNotification.LOG_TAG, "Unable to find " + KEY_NOTIFICATION_DEFAULT_CHANNEL_ID + " in manifest. Falling back to default");
+ }
+ // Default
+ return "fcm_fallback_notification_channel";
+ }
}
diff --git a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationHelper.java b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationHelper.java
index a2abe57e0..478f903e1 100644
--- a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationHelper.java
+++ b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationHelper.java
@@ -1,6 +1,5 @@
package com.dieam.reactnativepushnotification.modules;
-
import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.AlarmManager;
@@ -24,6 +23,9 @@
import android.os.Bundle;
import android.service.notification.StatusBarNotification;
import android.util.Log;
+import androidx.core.app.RemoteInput;
+
+import androidx.annotation.RequiresApi;
import androidx.core.app.NotificationCompat;
import com.facebook.react.bridge.Arguments;
@@ -35,20 +37,21 @@
import org.json.JSONArray;
import org.json.JSONException;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
-import java.util.Set;
+import java.util.Map;
import static com.dieam.reactnativepushnotification.modules.RNPushNotification.LOG_TAG;
import static com.dieam.reactnativepushnotification.modules.RNPushNotificationAttributes.fromJson;
+import static com.dieam.reactnativepushnotification.modules.RNPushNotification.KEY_TEXT_REPLY;
public class RNPushNotificationHelper {
public static final String PREFERENCES_KEY = "rn_push_notification";
private static final long DEFAULT_VIBRATION = 300L;
- private static final String NOTIFICATION_CHANNEL_ID = "rn-push-notification-channel-id";
private Context context;
private RNPushNotificationConfig config;
@@ -79,14 +82,42 @@ private AlarmManager getAlarmManager() {
return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
}
+ public void invokeApp(Bundle bundle) {
+ String packageName = context.getPackageName();
+ Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
+ String className = launchIntent.getComponent().getClassName();
+
+ try {
+ Class> activityClass = Class.forName(className);
+ Intent activityIntent = new Intent(context, activityClass);
+
+ if(bundle != null) {
+ activityIntent.putExtra("notification", bundle);
+ }
+
+ activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ context.startActivity(activityIntent);
+ } catch(Exception e) {
+ Log.e(LOG_TAG, "Class not found", e);
+ return;
+ }
+ }
+
private PendingIntent toScheduleNotificationIntent(Bundle bundle) {
- int notificationID = Integer.parseInt(bundle.getString("id"));
+ try {
+ int notificationID = Integer.parseInt(bundle.getString("id"));
- Intent notificationIntent = new Intent(context, RNPushNotificationPublisher.class);
- notificationIntent.putExtra(RNPushNotificationPublisher.NOTIFICATION_ID, notificationID);
- notificationIntent.putExtras(bundle);
+ Intent notificationIntent = new Intent(context, RNPushNotificationPublisher.class);
+ notificationIntent.putExtra(RNPushNotificationPublisher.NOTIFICATION_ID, notificationID);
+ notificationIntent.putExtras(bundle);
- return PendingIntent.getBroadcast(context, notificationID, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ return PendingIntent.getBroadcast(context, notificationID, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Unable to parse Notification ID", e);
+ }
+
+ return null;
}
public void sendNotificationScheduled(Bundle bundle) {
@@ -119,7 +150,7 @@ public void sendNotificationScheduled(Bundle bundle) {
SharedPreferences.Editor editor = scheduledNotificationsPersistence.edit();
editor.putString(id, notificationAttributes.toJson().toString());
- commit(editor);
+ editor.apply();
boolean isSaved = scheduledNotificationsPersistence.contains(id);
if (!isSaved) {
@@ -137,10 +168,14 @@ public void sendNotificationScheduledCore(Bundle bundle) {
// notification to the user
PendingIntent pendingIntent = toScheduleNotificationIntent(bundle);
+ if (pendingIntent == null) {
+ return;
+ }
+
Log.d(LOG_TAG, String.format("Setting a notification with id %s at time %s",
bundle.getString("id"), Long.toString(fireDate)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- if(allowWhileIdle && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (allowWhileIdle && Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
getAlarmManager().setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
} else {
getAlarmManager().setExact(AlarmManager.RTC_WAKEUP, fireDate, pendingIntent);
@@ -150,7 +185,20 @@ public void sendNotificationScheduledCore(Bundle bundle) {
}
}
- public void sendToNotificationCentre(Bundle bundle) {
+
+ public void sendToNotificationCentre(final Bundle bundle) {
+ RNPushNotificationPicturesAggregator aggregator = new RNPushNotificationPicturesAggregator(new RNPushNotificationPicturesAggregator.Callback() {
+ public void call(Bitmap largeIconImage, Bitmap bigPictureImage, Bitmap bigLargeIconImage) {
+ sendToNotificationCentreWithPicture(bundle, largeIconImage, bigPictureImage, bigLargeIconImage);
+ }
+ });
+
+ aggregator.setLargeIconUrl(context, bundle.getString("largeIconUrl"));
+ aggregator.setBigLargeIconUrl(context, bundle.getString("bigLargeIconUrl"));
+ aggregator.setBigPictureUrl(context, bundle.getString("bigPictureUrl"));
+ }
+
+ public void sendToNotificationCentreWithPicture(Bundle bundle, Bitmap largeIconBitmap, Bitmap bigPictureBitmap, Bitmap bigLargeIconBitmap) {
try {
Class intentClass = getMainActivityClass();
if (intentClass == null) {
@@ -160,7 +208,7 @@ public void sendToNotificationCentre(Bundle bundle) {
if (bundle.getString("message") == null) {
// this happens when a 'data' notification is received - we do not synthesize a local notification in this case
- Log.d(LOG_TAG, "Cannot send to notification centre because there is no 'message' field in: " + bundle);
+ Log.d(LOG_TAG, "Ignore this message if you sent data-only notification. Cannot send to notification centre because there is no 'message' field in: " + bundle);
return;
}
@@ -173,8 +221,6 @@ public void sendToNotificationCentre(Bundle bundle) {
Resources res = context.getResources();
String packageName = context.getPackageName();
- String channel_id = NOTIFICATION_CHANNEL_ID;
-
String title = bundle.getString("title");
if (title == null) {
ApplicationInfo appInfo = context.getApplicationInfo();
@@ -185,7 +231,7 @@ public void sendToNotificationCentre(Bundle bundle) {
final String priorityString = bundle.getString("priority");
if (priorityString != null) {
- switch(priorityString.toLowerCase()) {
+ switch (priorityString.toLowerCase()) {
case "max":
priority = NotificationCompat.PRIORITY_MAX;
break;
@@ -206,44 +252,11 @@ public void sendToNotificationCentre(Bundle bundle) {
}
}
- int importance = NotificationManager.IMPORTANCE_HIGH;
- final String importanceString = bundle.getString("importance");
-
- if (importanceString != null) {
- switch(importanceString.toLowerCase()) {
- case "default":
- importance = NotificationManager.IMPORTANCE_DEFAULT;
- break;
- case "max":
- importance = NotificationManager.IMPORTANCE_MAX;
- break;
- case "high":
- importance = NotificationManager.IMPORTANCE_HIGH;
- break;
- case "low":
- importance = NotificationManager.IMPORTANCE_LOW;
- break;
- case "min":
- importance = NotificationManager.IMPORTANCE_MIN;
- break;
- case "none":
- importance = NotificationManager.IMPORTANCE_NONE;
- break;
- case "unspecified":
- importance = NotificationManager.IMPORTANCE_UNSPECIFIED;
- break;
- default:
- importance = NotificationManager.IMPORTANCE_HIGH;
- }
- }
-
- channel_id = channel_id + "-" + importance;
-
int visibility = NotificationCompat.VISIBILITY_PRIVATE;
final String visibilityString = bundle.getString("visibility");
if (visibilityString != null) {
- switch(visibilityString.toLowerCase()) {
+ switch (visibilityString.toLowerCase()) {
case "private":
visibility = NotificationCompat.VISIBILITY_PRIVATE;
break;
@@ -258,47 +271,62 @@ public void sendToNotificationCentre(Bundle bundle) {
}
}
+ String channel_id = bundle.getString("channelId");
+
+ if(channel_id == null) {
+ channel_id = this.config.getNotificationDefaultChannelId();
+ }
+
NotificationCompat.Builder notification = new NotificationCompat.Builder(context, channel_id)
.setContentTitle(title)
.setTicker(bundle.getString("ticker"))
.setVisibility(visibility)
.setPriority(priority)
- .setAutoCancel(bundle.getBoolean("autoCancel", true));
-
+ .setAutoCancel(bundle.getBoolean("autoCancel", true))
+ .setOnlyAlertOnce(bundle.getBoolean("onlyAlertOnce", false));
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { // API 24 and higher
+ // Restore showing timestamp on Android 7+
+ // Source: https://developer.android.com/reference/android/app/Notification.Builder.html#setShowWhen(boolean)
+ boolean showWhen = bundle.getBoolean("showWhen", true);
+
+ notification.setShowWhen(showWhen);
+ }
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // API 26 and higher
// Changing Default mode of notification
notification.setDefaults(Notification.DEFAULT_LIGHTS);
}
-
- String group = bundle.getString("group");
- if (group != null) {
- notification.setGroup(group);
- }
-
- notification.setContentText(bundle.getString("message"));
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) { // API 20 and higher
+ String group = bundle.getString("group");
- String largeIcon = bundle.getString("largeIcon");
+ if (group != null) {
+ notification.setGroup(group);
+ }
- String subText = bundle.getString("subText");
-
- if (subText != null) {
- notification.setSubText(subText);
+ if (bundle.containsKey("groupSummary") || bundle.getBoolean("groupSummary")) {
+ notification.setGroupSummary(bundle.getBoolean("groupSummary"));
+ }
}
String numberString = bundle.getString("number");
+
if (numberString != null) {
notification.setNumber(Integer.parseInt(numberString));
}
- int smallIconResId;
- int largeIconResId;
+ // Small icon
+ int smallIconResId = 0;
String smallIcon = bundle.getString("smallIcon");
- if (smallIcon != null) {
- smallIconResId = res.getIdentifier(smallIcon, "mipmap", packageName);
- } else {
+ if (smallIcon != null && !smallIcon.isEmpty()) {
+ smallIconResId = res.getIdentifier(smallIcon, "drawable", packageName);
+ if (smallIconResId == 0) {
+ smallIconResId = res.getIdentifier(smallIcon, "mipmap", packageName);
+ }
+ } else if(smallIcon == null) {
smallIconResId = res.getIdentifier("ic_notification", "mipmap", packageName);
}
@@ -310,61 +338,97 @@ public void sendToNotificationCentre(Bundle bundle) {
}
}
- if (largeIcon != null) {
- largeIconResId = res.getIdentifier(largeIcon, "mipmap", packageName);
- } else {
- largeIconResId = res.getIdentifier("ic_launcher", "mipmap", packageName);
+ notification.setSmallIcon(smallIconResId);
+
+ // Large icon
+ if(largeIconBitmap == null) {
+ int largeIconResId = 0;
+
+ String largeIcon = bundle.getString("largeIcon");
+
+ if (largeIcon != null && !largeIcon.isEmpty()) {
+ largeIconResId = res.getIdentifier(largeIcon, "drawable", packageName);
+ if (largeIconResId == 0) {
+ largeIconResId = res.getIdentifier(largeIcon, "mipmap", packageName);
+ }
+ } else if(largeIcon == null) {
+ largeIconResId = res.getIdentifier("ic_launcher", "mipmap", packageName);
+ }
+
+ // Before Lolipop there was no large icon for notifications.
+ if (largeIconResId != 0 && (largeIcon != null || Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)) {
+ largeIconBitmap = BitmapFactory.decodeResource(res, largeIconResId);
+ }
}
- Bitmap largeIconBitmap = BitmapFactory.decodeResource(res, largeIconResId);
+ if (largeIconBitmap != null){
+ notification.setLargeIcon(largeIconBitmap);
+ }
+
+ String message = bundle.getString("message");
+
+ notification.setContentText(message);
- if (largeIconResId != 0 && (largeIcon != null || Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP)) {
- notification.setLargeIcon(largeIconBitmap);
+ String subText = bundle.getString("subText");
+
+ if (subText != null) {
+ notification.setSubText(subText);
}
- notification.setSmallIcon(smallIconResId);
String bigText = bundle.getString("bigText");
if (bigText == null) {
- bigText = bundle.getString("message");
+ bigText = message;
}
- notification.setStyle(new NotificationCompat.BigTextStyle().bigText(bigText));
+ NotificationCompat.Style style;
+
+ if(bigPictureBitmap != null) {
+
+ // Big large icon
+ if(bigLargeIconBitmap == null) {
+ int bigLargeIconResId = 0;
+
+ String bigLargeIcon = bundle.getString("bigLargeIcon");
+
+ if (bigLargeIcon != null && !bigLargeIcon.isEmpty()) {
+ bigLargeIconResId = res.getIdentifier(bigLargeIcon, "mipmap", packageName);
+ if (bigLargeIconResId != 0) {
+ bigLargeIconBitmap = BitmapFactory.decodeResource(res, bigLargeIconResId);
+ }
+ }
+ }
+
+ style = new NotificationCompat.BigPictureStyle()
+ .bigPicture(bigPictureBitmap)
+ .setBigContentTitle(title)
+ .setSummaryText(message)
+ .bigLargeIcon(bigLargeIconBitmap);
+ }
+ else {
+ style = new NotificationCompat.BigTextStyle().bigText(bigText);
+ }
+
+ notification.setStyle(style);
Intent intent = new Intent(context, intentClass);
intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
+ bundle.putBoolean("foreground", this.isApplicationInForeground());
bundle.putBoolean("userInteraction", true);
intent.putExtra("notification", bundle);
+ // Add message_id to intent so react-native-firebase/messaging can identify it
+ String messageId = bundle.getString("messageId");
+ if (messageId != null) {
+ intent.putExtra("message_id", messageId);
+ }
+
Uri soundUri = null;
if (!bundle.containsKey("playSound") || bundle.getBoolean("playSound")) {
- soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
-
String soundName = bundle.getString("soundName");
-
- if (soundName != null) {
- if (!"default".equalsIgnoreCase(soundName)) {
-
- // sound name can be full filename, or just the resource name.
- // So the strings 'my_sound.mp3' AND 'my_sound' are accepted
- // The reason is to make the iOS and android javascript interfaces compatible
- int resId;
- if (context.getResources().getIdentifier(soundName, "raw", context.getPackageName()) != 0) {
- resId = context.getResources().getIdentifier(soundName, "raw", context.getPackageName());
- } else {
- soundName = soundName.substring(0, soundName.lastIndexOf('.'));
- resId = context.getResources().getIdentifier(soundName, "raw", context.getPackageName());
- }
-
- soundUri = Uri.parse("android.resource://" + context.getPackageName() + "/" + resId);
-
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // API 26 and higher
- channel_id = channel_id + "-" + soundName;
- }
- }
- }
+ soundUri = getSoundUri(soundName);
notification.setSound(soundUri);
}
@@ -402,15 +466,34 @@ public void sendToNotificationCentre(Bundle bundle) {
long vibration = bundle.containsKey("vibration") ? (long) bundle.getDouble("vibration") : DEFAULT_VIBRATION;
if (vibration == 0)
vibration = DEFAULT_VIBRATION;
-
- channel_id = channel_id + "-" + vibration;
vibratePattern = new long[]{0, vibration};
notification.setVibrate(vibratePattern);
}
- checkOrCreateChannel(notificationManager, channel_id, soundUri, importance, vibratePattern);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ // Define the shortcutId
+ String shortcutId = bundle.getString("shortcutId");
+
+ if (shortcutId != null) {
+ notification.setShortcutId(shortcutId);
+ }
+
+ Long timeoutAfter = (long) bundle.getDouble("timeoutAfter");
+
+ if (timeoutAfter != null && timeoutAfter >= 0) {
+ notification.setTimeoutAfter(timeoutAfter);
+ }
+ }
+
+ Long when = (long) bundle.getDouble("when");
+
+ if (when != null && when >= 0) {
+ notification.setWhen(when);
+ }
+
+ notification.setUsesChronometer(bundle.getBoolean("usesChronometer", false));
notification.setChannelId(channel_id);
notification.setContentIntent(pendingIntent);
@@ -436,19 +519,51 @@ public void sendToNotificationCentre(Bundle bundle) {
continue;
}
- Intent actionIntent = new Intent(context, intentClass);
+
+ Intent actionIntent = new Intent(context, RNPushNotificationActions.class);
+ actionIntent.setAction(packageName + ".ACTION_" + i);
+
actionIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
- actionIntent.setAction(packageName + "." + action);
// Add "action" for later identifying which button gets pressed.
bundle.putString("action", action);
actionIntent.putExtra("notification", bundle);
actionIntent.setPackage(packageName);
+ if (messageId != null) {
+ intent.putExtra("message_id", messageId);
+ }
- PendingIntent pendingActionIntent = PendingIntent.getActivity(context, notificationID, actionIntent,
+ PendingIntent pendingActionIntent = PendingIntent.getBroadcast(context, notificationID, actionIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
- notification.addAction(icon, action, pendingActionIntent);
+ if(action.equals("ReplyInput")){
+ //Action with inline reply
+ if(android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT_WATCH){
+ RemoteInput remoteInput = new RemoteInput.Builder(KEY_TEXT_REPLY)
+ .setLabel(bundle.getString("reply_placeholder_text"))
+ .build();
+ NotificationCompat.Action replyAction = new NotificationCompat.Action.Builder(
+ icon, bundle.getString("reply_button_text"), pendingActionIntent)
+ .addRemoteInput(remoteInput)
+ .setAllowGeneratedReplies(true)
+ .build();
+
+ notification.addAction(replyAction);
+ }
+ else{
+ // The notification will not have action
+ break;
+ }
+ }
+ else{
+ // Add "action" for later identifying which button gets pressed
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ notification.addAction(new NotificationCompat.Action.Builder(icon, action, pendingActionIntent).build());
+ } else {
+ notification.addAction(icon, action, pendingActionIntent);
+ }
+ }
}
+
}
// Remove the notification from the shared preferences once it has been shown
@@ -462,12 +577,12 @@ public void sendToNotificationCentre(Bundle bundle) {
if (scheduledNotificationsPersistence.getString(notificationIdString, null) != null) {
SharedPreferences.Editor editor = scheduledNotificationsPersistence.edit();
editor.remove(notificationIdString);
- commit(editor);
+ editor.apply();
}
Notification info = notification.build();
info.defaults |= Notification.DEFAULT_LIGHTS;
-
+
if (bundle.containsKey("tag")) {
String tag = bundle.getString("tag");
notificationManager.notify(tag, notificationID, info);
@@ -526,7 +641,7 @@ private void scheduleNextNotificationIfRepeating(Bundle bundle) {
nextEvent.set(Calendar.YEAR, nextEvent.get(Calendar.YEAR) + (nextMonth == 0 ? 1 : 0));
nextEvent.set(Calendar.MONTH, nextMonth);
final int maxDay = nextEvent.getActualMaximum(Calendar.DAY_OF_MONTH);
- nextEvent.set(Calendar.DAY_OF_MONTH, fireDay <= maxDay ? fireDay : maxDay);
+ nextEvent.set(Calendar.DAY_OF_MONTH, Math.min(fireDay, maxDay));
nextEvent.set(Calendar.HOUR_OF_DAY, fireHour);
nextEvent.set(Calendar.MINUTE, fireMinute);
nextEvent.set(Calendar.SECOND, 0);
@@ -556,6 +671,27 @@ private void scheduleNextNotificationIfRepeating(Bundle bundle) {
}
}
+ private Uri getSoundUri(String soundName) {
+ if (soundName == null || "default".equalsIgnoreCase(soundName)) {
+ return RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+ } else {
+
+ // sound name can be full filename, or just the resource name.
+ // So the strings 'my_sound.mp3' AND 'my_sound' are accepted
+ // The reason is to make the iOS and android javascript interfaces compatible
+
+ int resId;
+ if (context.getResources().getIdentifier(soundName, "raw", context.getPackageName()) != 0) {
+ resId = context.getResources().getIdentifier(soundName, "raw", context.getPackageName());
+ } else {
+ soundName = soundName.substring(0, soundName.lastIndexOf('.'));
+ resId = context.getResources().getIdentifier(soundName, "raw", context.getPackageName());
+ }
+
+ return Uri.parse("android.resource://" + context.getPackageName() + "/" + resId);
+ }
+ }
+
public void clearNotifications() {
Log.i(LOG_TAG, "Clearing alerts from the notification centre");
@@ -563,11 +699,15 @@ public void clearNotifications() {
notificationManager.cancelAll();
}
- public void clearNotification(int notificationID) {
+ public void clearNotification(String tag, int notificationID) {
Log.i(LOG_TAG, "Clearing notification: " + notificationID);
NotificationManager notificationManager = notificationManager();
- notificationManager.cancel(notificationID);
+ if(tag != null) {
+ notificationManager.cancel(tag, notificationID);
+ } else {
+ notificationManager.cancel(notificationID);
+ }
}
public void clearDeliveredNotifications(ReadableArray identifiers) {
@@ -579,11 +719,17 @@ public void clearDeliveredNotifications(ReadableArray identifiers) {
}
}
+ @RequiresApi(api = Build.VERSION_CODES.M)
public WritableArray getDeliveredNotifications() {
+ WritableArray result = Arguments.createArray();
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
+ return result;
+ }
+
NotificationManager notificationManager = notificationManager();
StatusBarNotification delivered[] = notificationManager.getActiveNotifications();
Log.i(LOG_TAG, "Found " + delivered.length + " delivered notifications");
- WritableArray result = Arguments.createArray();
/*
* stay consistent to the return structure in
* https://facebook.github.io/react-native/docs/pushnotificationios.html#getdeliverednotifications
@@ -604,6 +750,35 @@ public WritableArray getDeliveredNotifications() {
return result;
}
+
+ public WritableArray getScheduledLocalNotifications() {
+ WritableArray scheduled = Arguments.createArray();
+
+ Map scheduledNotifications = scheduledNotificationsPersistence.getAll();
+
+ for (Map.Entry entry : scheduledNotifications.entrySet()) {
+ try {
+ RNPushNotificationAttributes notification = fromJson(entry.getValue().toString());
+ WritableMap notificationMap = Arguments.createMap();
+
+ notificationMap.putString("title", notification.getTitle());
+ notificationMap.putString("message", notification.getMessage());
+ notificationMap.putString("number", notification.getNumber());
+ notificationMap.putDouble("date", notification.getFireDate());
+ notificationMap.putString("id", notification.getId());
+ notificationMap.putString("repeatInterval", notification.getRepeatType());
+ notificationMap.putString("soundName", notification.getSound());
+ notificationMap.putString("data", notification.getUserInfo());
+
+ scheduled.pushMap(notificationMap);
+ } catch (JSONException e) {
+ Log.e(LOG_TAG, e.getMessage());
+ }
+ }
+
+ return scheduled;
+ }
+
public void cancelAllScheduledNotifications() {
Log.i(LOG_TAG, "Cancelling all notifications");
@@ -634,13 +809,17 @@ private void cancelScheduledNotification(String notificationIDString) {
// remove it from the alarm manger schedule
Bundle b = new Bundle();
b.putString("id", notificationIDString);
- getAlarmManager().cancel(toScheduleNotificationIntent(b));
+ PendingIntent pendingIntent = toScheduleNotificationIntent(b);
+
+ if (pendingIntent != null) {
+ getAlarmManager().cancel(pendingIntent);
+ }
if (scheduledNotificationsPersistence.contains(notificationIDString)) {
// remove it from local storage
SharedPreferences.Editor editor = scheduledNotificationsPersistence.edit();
editor.remove(notificationIDString);
- commit(editor);
+ editor.apply();
} else {
Log.w(LOG_TAG, "Unable to find notification " + notificationIDString);
}
@@ -648,51 +827,109 @@ private void cancelScheduledNotification(String notificationIDString) {
// removed it from the notification center
NotificationManager notificationManager = notificationManager();
- notificationManager.cancel(Integer.parseInt(notificationIDString));
+ try {
+ notificationManager.cancel(Integer.parseInt(notificationIDString));
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Unable to parse Notification ID " + notificationIDString, e);
+ }
}
private NotificationManager notificationManager() {
return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
}
- private static void commit(SharedPreferences.Editor editor) {
- if (Build.VERSION.SDK_INT < 9) {
- editor.commit();
- } else {
- editor.apply();
- }
+ public List listChannels() {
+ List channels = new ArrayList<>();
+
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+ return channels;
+
+ NotificationManager manager = notificationManager();
+
+ if (manager == null)
+ return channels;
+
+ List listChannels = manager.getNotificationChannels();
+
+ for(NotificationChannel channel : listChannels) {
+ channels.add(channel.getId());
+ }
+
+ return channels;
}
- public void checkOrCreateDefaultChannel() {
+ public boolean channelBlocked(String channel_id) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+ return false;
+
NotificationManager manager = notificationManager();
- int importance = NotificationManager.IMPORTANCE_HIGH;
+ if (manager == null)
+ return false;
+
+ NotificationChannel channel = manager.getNotificationChannel(channel_id);
- String channel_id = NOTIFICATION_CHANNEL_ID + "-" + importance + "-" + DEFAULT_VIBRATION;
+ if(channel == null)
+ return false;
- checkOrCreateChannel(manager, channel_id, null, importance, new long[] {0, DEFAULT_VIBRATION});
+ return NotificationManager.IMPORTANCE_NONE == channel.getImportance();
}
- private void checkOrCreateChannel(NotificationManager manager, String channel_id, Uri soundUri, int importance, long[] vibratePattern) {
+ public boolean channelExists(String channel_id) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+ return false;
+
+ NotificationManager manager = notificationManager();
+
+ if (manager == null)
+ return false;
+
+ NotificationChannel channel = manager.getNotificationChannel(channel_id);
+
+ return channel != null;
+ }
+
+ public void deleteChannel(String channel_id) {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
return;
+
+ NotificationManager manager = notificationManager();
+
if (manager == null)
return;
- NotificationChannel channel = manager.getNotificationChannel(channel_id);
+ manager.deleteNotificationChannel(channel_id);
+ }
- if (channel == null) {
- channel = new NotificationChannel(channel_id, this.config.getChannelName() != null ? this.config.getChannelName() : "rn-push-notification-channel", importance);
+ private boolean checkOrCreateChannel(NotificationManager manager, String channel_id, String channel_name, String channel_description, Uri soundUri, int importance, long[] vibratePattern) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+ return false;
+ if (manager == null)
+ return false;
+
+ NotificationChannel channel = manager.getNotificationChannel(channel_id);
- channel.setDescription(this.config.getChannelDescription());
+ if (
+ channel == null && channel_name != null && channel_description != null ||
+ channel != null &&
+ (
+ channel_name != null && !channel_name.equals(channel.getName()) ||
+ channel_description != null && !channel_description.equals(channel.getDescription())
+ )
+ ) {
+ // If channel doesn't exist create a new one.
+ // If channel name or description is updated then update the existing channel.
+ channel = new NotificationChannel(channel_id, channel_name, importance);
+
+ channel.setDescription(channel_description);
channel.enableLights(true);
- channel.enableVibration(true);
+ channel.enableVibration(vibratePattern != null);
channel.setVibrationPattern(vibratePattern);
if (soundUri != null) {
AudioAttributes audioAttributes = new AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
- .setUsage(AudioAttributes.USAGE_ALARM)
+ .setUsage(AudioAttributes.USAGE_NOTIFICATION)
.build();
channel.setSound(soundUri, audioAttributes);
@@ -701,22 +938,45 @@ private void checkOrCreateChannel(NotificationManager manager, String channel_id
}
manager.createNotificationChannel(channel);
+
+ return true;
}
+
+ return false;
+ }
+
+ public boolean createChannel(ReadableMap channelInfo) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O)
+ return false;
+
+ String channelId = channelInfo.getString("channelId");
+ String channelName = channelInfo.getString("channelName");
+ String channelDescription = channelInfo.hasKey("channelDescription") ? channelInfo.getString("channelDescription") : "";
+ boolean playSound = !channelInfo.hasKey("playSound") || channelInfo.getBoolean("playSound");
+ String soundName = channelInfo.hasKey("soundName") ? channelInfo.getString("soundName") : "default";
+ int importance = channelInfo.hasKey("importance") ? channelInfo.getInt("importance") : 4;
+ boolean vibrate = channelInfo.hasKey("vibrate") && channelInfo.getBoolean("vibrate");
+ long[] vibratePattern = vibrate ? new long[] { 0, DEFAULT_VIBRATION } : null;
+
+ NotificationManager manager = notificationManager();
+
+ Uri soundUri = playSound ? getSoundUri(soundName) : null;
+
+ return checkOrCreateChannel(manager, channelId, channelName, channelDescription, soundUri, importance, vibratePattern);
}
-
- private boolean isApplicationInForeground(Context context) {
+
+ public boolean isApplicationInForeground() {
ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
List processInfos = activityManager.getRunningAppProcesses();
if (processInfos != null) {
for (RunningAppProcessInfo processInfo : processInfos) {
if (processInfo.processName.equals(context.getPackageName())
- && processInfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND
- && processInfo.pkgList.length > 0) {
+ && processInfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND
+ && processInfo.pkgList.length > 0) {
return true;
}
}
}
return false;
}
-
}
diff --git a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationJsDelivery.java b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationJsDelivery.java
index c6721d7fc..350758e57 100644
--- a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationJsDelivery.java
+++ b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationJsDelivery.java
@@ -4,7 +4,7 @@
import android.os.Bundle;
import com.facebook.react.bridge.Arguments;
-import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
@@ -18,9 +18,9 @@
*/
public class RNPushNotificationJsDelivery {
- private ReactApplicationContext mReactContext;
+ private ReactContext mReactContext;
- public RNPushNotificationJsDelivery(ReactApplicationContext reactContext) {
+ public RNPushNotificationJsDelivery(ReactContext reactContext) {
mReactContext = reactContext;
}
@@ -67,7 +67,7 @@ String convertJSON(Bundle bundle) {
}
// a Bundle is not a map, so we have to convert it explicitly
- JSONObject convertJSONObject(Bundle bundle) throws JSONException {
+ private JSONObject convertJSONObject(Bundle bundle) throws JSONException {
JSONObject json = new JSONObject();
Set keys = bundle.keySet();
for (String key : keys) {
diff --git a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationListenerService.java b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationListenerService.java
index e62ef1100..4e3a8c37e 100644
--- a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationListenerService.java
+++ b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationListenerService.java
@@ -1,18 +1,12 @@
package com.dieam.reactnativepushnotification.modules;
-import java.util.Map;
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningAppProcessInfo;
-import android.app.Application;
-import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
-import com.dieam.reactnativepushnotification.helpers.ApplicationBadgeHelper;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.bridge.Arguments;
@@ -20,27 +14,37 @@
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.WritableMap;
-import org.json.JSONObject;
-
-import java.util.List;
-import java.util.Random;
-
import static com.dieam.reactnativepushnotification.modules.RNPushNotification.LOG_TAG;
public class RNPushNotificationListenerService extends FirebaseMessagingService {
+ private RNReceivedMessageHandler mMessageReceivedHandler;
+ private FirebaseMessagingService mFirebaseServiceDelegate;
+
+ public RNPushNotificationListenerService() {
+ super();
+ this.mMessageReceivedHandler = new RNReceivedMessageHandler(this);
+ }
+
+ public RNPushNotificationListenerService(FirebaseMessagingService delegate) {
+ super();
+ this.mFirebaseServiceDelegate = delegate;
+ this.mMessageReceivedHandler = new RNReceivedMessageHandler(delegate);
+ }
+
@Override
public void onNewToken(String token) {
final String deviceToken = token;
+ final FirebaseMessagingService serviceRef = (this.mFirebaseServiceDelegate == null) ? this : this.mFirebaseServiceDelegate;
Log.d(LOG_TAG, "Refreshed token: " + deviceToken);
Handler handler = new Handler(Looper.getMainLooper());
handler.post(new Runnable() {
public void run() {
// Construct and load our normal React JS code bundle
- ReactInstanceManager mReactInstanceManager = ((ReactApplication) getApplication()).getReactNativeHost().getReactInstanceManager();
+ final ReactInstanceManager mReactInstanceManager = ((ReactApplication)serviceRef.getApplication()).getReactNativeHost().getReactInstanceManager();
ReactContext context = mReactInstanceManager.getCurrentReactContext();
- // If it's constructed, send a notificationre
+ // If it's constructed, send a notification
if (context != null) {
handleNewToken((ReactApplicationContext) context, deviceToken);
} else {
@@ -48,6 +52,7 @@ public void run() {
mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
public void onReactContextInitialized(ReactContext context) {
handleNewToken((ReactApplicationContext) context, deviceToken);
+ mReactInstanceManager.removeReactInstanceEventListener(this);
}
});
if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
@@ -67,7 +72,6 @@ private void handleNewToken(ReactApplicationContext context, String token) {
jsDelivery.sendEvent("remoteNotificationsRegistered", params);
}
-
@Override
public void onMessageReceived(RemoteMessage message) {
String from = message.getFrom();
diff --git a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationPicturesAggregator.java b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationPicturesAggregator.java
new file mode 100644
index 000000000..733bcbe44
--- /dev/null
+++ b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationPicturesAggregator.java
@@ -0,0 +1,173 @@
+package com.dieam.reactnativepushnotification.modules;
+
+import androidx.annotation.Nullable;
+import com.facebook.common.executors.CallerThreadExecutor;
+import com.facebook.common.references.CloseableReference;
+import com.facebook.datasource.DataSource;
+import com.facebook.drawee.backends.pipeline.Fresco;
+import com.facebook.imagepipeline.common.Priority;
+import com.facebook.imagepipeline.core.ImagePipeline;
+import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
+import com.facebook.imagepipeline.image.CloseableImage;
+import com.facebook.imagepipeline.request.ImageRequest;
+import com.facebook.imagepipeline.request.ImageRequestBuilder;
+
+import android.util.Log;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import static com.dieam.reactnativepushnotification.modules.RNPushNotification.LOG_TAG;
+
+public class RNPushNotificationPicturesAggregator {
+ interface Callback {
+ public void call(Bitmap largeIconImage, Bitmap bigPictureImage, Bitmap bigLargeIconImage);
+ }
+
+ private AtomicInteger count = new AtomicInteger(0);
+
+ private Bitmap largeIconImage;
+ private Bitmap bigPictureImage;
+ private Bitmap bigLargeIconImage;
+
+ private Callback callback;
+
+ public RNPushNotificationPicturesAggregator(Callback callback) {
+ this.callback = callback;
+ }
+
+ public void setBigPicture(Bitmap bitmap) {
+ this.bigPictureImage = bitmap;
+ this.finished();
+ }
+
+ public void setBigPictureUrl(Context context, String url) {
+ if(null == url) {
+ this.setBigPicture(null);
+ return;
+ }
+
+ Uri uri = null;
+
+ try {
+ uri = Uri.parse(url);
+ } catch(Exception ex) {
+ Log.e(LOG_TAG, "Failed to parse bigPictureUrl", ex);
+ this.setBigPicture(null);
+ return;
+ }
+
+ final RNPushNotificationPicturesAggregator aggregator = this;
+
+ this.downloadRequest(context, uri, new BaseBitmapDataSubscriber() {
+ @Override
+ public void onNewResultImpl(@Nullable Bitmap bitmap) {
+ aggregator.setBigPicture(bitmap);
+ }
+
+ @Override
+ public void onFailureImpl(DataSource dataSource) {
+ aggregator.setBigPicture(null);
+ }
+ });
+ }
+
+ public void setLargeIcon(Bitmap bitmap) {
+ this.largeIconImage = bitmap;
+ this.finished();
+ }
+
+ public void setLargeIconUrl(Context context, String url) {
+ if(null == url) {
+ this.setLargeIcon(null);
+ return;
+ }
+
+ Uri uri = null;
+
+ try {
+ uri = Uri.parse(url);
+ } catch(Exception ex) {
+ Log.e(LOG_TAG, "Failed to parse largeIconUrl", ex);
+ this.setLargeIcon(null);
+ return;
+ }
+
+ final RNPushNotificationPicturesAggregator aggregator = this;
+
+ this.downloadRequest(context, uri, new BaseBitmapDataSubscriber() {
+ @Override
+ public void onNewResultImpl(@Nullable Bitmap bitmap) {
+ aggregator.setLargeIcon(bitmap);
+ }
+
+ @Override
+ public void onFailureImpl(DataSource dataSource) {
+ aggregator.setLargeIcon(null);
+ }
+ });
+ }
+
+ public void setBigLargeIcon(Bitmap bitmap) {
+ this.bigLargeIconImage = bitmap;
+ this.finished();
+ }
+
+ public void setBigLargeIconUrl(Context context, String url) {
+ if(null == url) {
+ this.setBigLargeIcon(null);
+ return;
+ }
+
+ Uri uri = null;
+
+ try {
+ uri = Uri.parse(url);
+ } catch(Exception ex) {
+ Log.e(LOG_TAG, "Failed to parse bigLargeIconUrl", ex);
+ this.setBigLargeIcon(null);
+ return;
+ }
+
+ final RNPushNotificationPicturesAggregator aggregator = this;
+
+ this.downloadRequest(context, uri, new BaseBitmapDataSubscriber() {
+ @Override
+ public void onNewResultImpl(@Nullable Bitmap bitmap) {
+ aggregator.setBigLargeIcon(bitmap);
+ }
+
+ @Override
+ public void onFailureImpl(DataSource dataSource) {
+ aggregator.setBigLargeIcon(null);
+ }
+ });
+ }
+
+ private void downloadRequest(Context context, Uri uri, BaseBitmapDataSubscriber subscriber) {
+ ImageRequest imageRequest = ImageRequestBuilder
+ .newBuilderWithSource(uri)
+ .setRequestPriority(Priority.HIGH)
+ .setLowestPermittedRequestLevel(ImageRequest.RequestLevel.FULL_FETCH)
+ .build();
+
+ if(!Fresco.hasBeenInitialized()) {
+ Fresco.initialize(context);
+ }
+
+ DataSource> dataSource = Fresco.getImagePipeline().fetchDecodedImage(imageRequest, context);
+
+ dataSource.subscribe(subscriber, CallerThreadExecutor.getInstance());
+ }
+
+ private void finished() {
+ synchronized(this.count) {
+ int val = this.count.incrementAndGet();
+
+ if(val >= 3 && this.callback != null) {
+ this.callback.call(this.largeIconImage, this.bigPictureImage, this.bigLargeIconImage);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationPublisher.java b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationPublisher.java
index 8f75f91ba..c779f5fb2 100644
--- a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationPublisher.java
+++ b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationPublisher.java
@@ -4,23 +4,44 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
+import android.os.Bundle;
import android.util.Log;
+import java.util.List;
+import java.security.SecureRandom;
+
import static com.dieam.reactnativepushnotification.modules.RNPushNotification.LOG_TAG;
public class RNPushNotificationPublisher extends BroadcastReceiver {
final static String NOTIFICATION_ID = "notificationId";
@Override
- public void onReceive(Context context, Intent intent) {
+ public void onReceive(final Context context, Intent intent) {
int id = intent.getIntExtra(NOTIFICATION_ID, 0);
long currentTime = System.currentTimeMillis();
Log.i(LOG_TAG, "NotificationPublisher: Prepare To Publish: " + id + ", Now Time: " + currentTime);
+ final Bundle bundle = intent.getExtras();
+
+ Log.v(LOG_TAG, "onMessageReceived: " + bundle);
+
+ handleLocalNotification(context, bundle);
+ }
+
+ private void handleLocalNotification(Context context, Bundle bundle) {
+
+ // If notification ID is not provided by the user for push notification, generate one at random
+ if (bundle.getString("id") == null) {
+ SecureRandom randomNumberGenerator = new SecureRandom();
+ bundle.putString("id", String.valueOf(randomNumberGenerator.nextInt()));
+ }
+
Application applicationContext = (Application) context.getApplicationContext();
+ RNPushNotificationHelper pushNotificationHelper = new RNPushNotificationHelper(applicationContext);
+
+ Log.v(LOG_TAG, "sendNotification: " + bundle);
- new RNPushNotificationHelper(applicationContext)
- .sendToNotificationCentre(intent.getExtras());
+ pushNotificationHelper.sendToNotificationCentre(bundle);
}
-}
+}
\ No newline at end of file
diff --git a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationRegistrationService.java b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationRegistrationService.java
deleted file mode 100644
index 633ed9c8e..000000000
--- a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNPushNotificationRegistrationService.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.dieam.reactnativepushnotification.modules;
-
-import android.app.IntentService;
-import android.content.Intent;
-import android.util.Log;
-
-import static com.dieam.reactnativepushnotification.modules.RNPushNotification.LOG_TAG;
-
-public class RNPushNotificationRegistrationService extends IntentService {
- private static final String TAG = "RNPushNotification";
-
- public RNPushNotificationRegistrationService() {
- super(TAG);
-
- Log.w(LOG_TAG, TAG + " RNPushNotificationRegistrationService is not needed anymore.");
- }
-
- @Override
- protected void onHandleIntent(Intent intent) {
- Log.w(LOG_TAG, TAG + " RNPushNotificationRegistrationService is not needed anymore.");
- }
-}
diff --git a/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNReceivedMessageHandler.java b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNReceivedMessageHandler.java
new file mode 100644
index 000000000..b255f89eb
--- /dev/null
+++ b/android/src/main/java/com/dieam/reactnativepushnotification/modules/RNReceivedMessageHandler.java
@@ -0,0 +1,209 @@
+package com.dieam.reactnativepushnotification.modules;
+
+import com.google.firebase.messaging.FirebaseMessagingService;
+import com.google.firebase.messaging.RemoteMessage;
+
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningAppProcessInfo;
+import android.app.Application;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.content.Context;
+import android.util.Log;
+import android.net.Uri;
+import androidx.annotation.NonNull;
+import androidx.core.app.NotificationCompat;
+
+import com.dieam.reactnativepushnotification.helpers.ApplicationBadgeHelper;
+import com.facebook.react.ReactApplication;
+import com.facebook.react.ReactInstanceManager;
+import com.facebook.react.bridge.ReactApplicationContext;
+import com.facebook.react.bridge.ReactContext;
+
+import org.json.JSONObject;
+
+import java.util.Map;
+import java.util.List;
+import java.security.SecureRandom;
+
+import static android.content.Context.ACTIVITY_SERVICE;
+import static com.dieam.reactnativepushnotification.modules.RNPushNotification.LOG_TAG;
+
+public class RNReceivedMessageHandler {
+ private FirebaseMessagingService mFirebaseMessagingService;
+
+ public RNReceivedMessageHandler(@NonNull FirebaseMessagingService service) {
+ this.mFirebaseMessagingService = service;
+ }
+
+ public void handleReceivedMessage(RemoteMessage message) {
+ String from = message.getFrom();
+ RemoteMessage.Notification remoteNotification = message.getNotification();
+ final Bundle bundle = new Bundle();
+ // Putting it from remoteNotification first so it can be overriden if message
+ // data has it
+ if (remoteNotification != null) {
+ // ^ It's null when message is from GCM
+ RNPushNotificationConfig config = new RNPushNotificationConfig(mFirebaseMessagingService.getApplication());
+
+ String title = getLocalizedString(remoteNotification.getTitle(), remoteNotification.getTitleLocalizationKey(), remoteNotification.getTitleLocalizationArgs());
+ String body = getLocalizedString(remoteNotification.getBody(), remoteNotification.getBodyLocalizationKey(), remoteNotification.getBodyLocalizationArgs());
+
+ bundle.putString("title", title);
+ bundle.putString("message", body);
+ bundle.putString("sound", remoteNotification.getSound());
+ bundle.putString("color", remoteNotification.getColor());
+ bundle.putString("tag", remoteNotification.getTag());
+
+ if(remoteNotification.getChannelId() != null) {
+ bundle.putString("channelId", remoteNotification.getChannelId());
+ }
+ else {
+ bundle.putString("channelId", config.getNotificationDefaultChannelId());
+ }
+
+ Integer visibilty = remoteNotification.getVisibility();
+ String visibilityString = "private";
+
+ if (visibilty != null) {
+ switch (visibilty) {
+ case NotificationCompat.VISIBILITY_PUBLIC:
+ visibilityString = "public";
+ break;
+ case NotificationCompat.VISIBILITY_SECRET:
+ visibilityString = "secret";
+ break;
+ }
+ }
+
+ bundle.putString("visibility", visibilityString);
+
+ Integer priority = remoteNotification.getNotificationPriority();
+ String priorityString = "high";
+
+ if (priority != null) {
+ switch (priority) {
+ case NotificationCompat.PRIORITY_MAX:
+ priorityString = "max";
+ break;
+ case NotificationCompat.PRIORITY_LOW:
+ priorityString = "low";
+ break;
+ case NotificationCompat.PRIORITY_MIN:
+ priorityString = "min";
+ break;
+ case NotificationCompat.PRIORITY_DEFAULT:
+ priorityString = "default";
+ break;
+ }
+ }
+
+ bundle.putString("priority", priorityString);
+
+ Uri uri = remoteNotification.getImageUrl();
+
+ if(uri != null) {
+ String imageUrl = uri.toString();
+
+ bundle.putString("bigPictureUrl", imageUrl);
+ bundle.putString("largeIconUrl", imageUrl);
+ }
+ }
+
+ Bundle dataBundle = new Bundle();
+ Map notificationData = message.getData();
+
+ for(Map.Entry entry : notificationData.entrySet()) {
+ dataBundle.putString(entry.getKey(), entry.getValue());
+ }
+
+ bundle.putParcelable("data", dataBundle);
+
+ Log.v(LOG_TAG, "onMessageReceived: " + bundle);
+
+ // We need to run this on the main thread, as the React code assumes that is true.
+ // Namely, DevServerHelper constructs a Handler() without a Looper, which triggers:
+ // "Can't create handler inside thread that has not called Looper.prepare()"
+ Handler handler = new Handler(Looper.getMainLooper());
+ handler.post(new Runnable() {
+ public void run() {
+ // Construct and load our normal React JS code bundle
+ final ReactInstanceManager mReactInstanceManager = ((ReactApplication) mFirebaseMessagingService.getApplication()).getReactNativeHost().getReactInstanceManager();
+ ReactContext context = mReactInstanceManager.getCurrentReactContext();
+ // If it's constructed, send a notificationre
+ if (context != null) {
+ handleRemotePushNotification((ReactApplicationContext) context, bundle);
+ } else {
+ // Otherwise wait for construction, then send the notification
+ mReactInstanceManager.addReactInstanceEventListener(new ReactInstanceManager.ReactInstanceEventListener() {
+ public void onReactContextInitialized(ReactContext context) {
+ handleRemotePushNotification((ReactApplicationContext) context, bundle);
+ mReactInstanceManager.removeReactInstanceEventListener(this);
+ }
+ });
+ if (!mReactInstanceManager.hasStartedCreatingInitialContext()) {
+ // Construct it in the background
+ mReactInstanceManager.createReactContextInBackground();
+ }
+ }
+ }
+ });
+ }
+
+ private void handleRemotePushNotification(ReactApplicationContext context, Bundle bundle) {
+
+ // If notification ID is not provided by the user for push notification, generate one at random
+ if (bundle.getString("id") == null) {
+ SecureRandom randomNumberGenerator = new SecureRandom();
+ bundle.putString("id", String.valueOf(randomNumberGenerator.nextInt()));
+ }
+
+ Application applicationContext = (Application) context.getApplicationContext();
+
+ RNPushNotificationConfig config = new RNPushNotificationConfig(mFirebaseMessagingService.getApplication());
+ RNPushNotificationHelper pushNotificationHelper = new RNPushNotificationHelper(applicationContext);
+
+ boolean isForeground = pushNotificationHelper.isApplicationInForeground();
+
+ RNPushNotificationJsDelivery jsDelivery = new RNPushNotificationJsDelivery(context);
+ bundle.putBoolean("foreground", isForeground);
+ bundle.putBoolean("userInteraction", false);
+ jsDelivery.notifyNotification(bundle);
+
+ // If contentAvailable is set to true, then send out a remote fetch event
+ if (bundle.getString("contentAvailable", "false").equalsIgnoreCase("true")) {
+ jsDelivery.notifyRemoteFetch(bundle);
+ }
+
+ if (config.getNotificationForeground() || !isForeground) {
+ Log.v(LOG_TAG, "sendNotification: " + bundle);
+
+ pushNotificationHelper.sendToNotificationCentre(bundle);
+ }
+ }
+
+ private String getLocalizedString(String text, String locKey, String[] locArgs) {
+ if(text != null) {
+ return text;
+ }
+
+ Context context = mFirebaseMessagingService.getApplicationContext();
+ String packageName = context.getPackageName();
+
+ String result = null;
+
+ if (locKey != null) {
+ int id = context.getResources().getIdentifier(locKey, "string", packageName);
+ if (id != 0) {
+ if (locArgs != null) {
+ result = context.getResources().getString(id, (Object[]) locArgs);
+ } else {
+ result = context.getResources().getString(id);
+ }
+ }
+ }
+
+ return result;
+ }
+}
diff --git a/component/index.android.js b/component/index.android.js
index 13a442ead..3496c045b 100644
--- a/component/index.android.js
+++ b/component/index.android.js
@@ -1,18 +1,16 @@
'use strict';
-var {
- NativeModules,
- DeviceEventEmitter,
-} = require('react-native');
+import { NativeModules, DeviceEventEmitter } from "react-native";
-var RNPushNotification = NativeModules.RNPushNotification;
-var _notifHandlers = new Map();
+let RNPushNotification = NativeModules.ReactNativePushNotification;
+let _notifHandlers = new Map();
var DEVICE_NOTIF_EVENT = 'remoteNotificationReceived';
var NOTIF_REGISTER_EVENT = 'remoteNotificationsRegistered';
+var NOTIF_ACTION_EVENT = 'notificationActionReceived';
var REMOTE_FETCH_EVENT = 'remoteFetch';
-var NotificationsComponent = function() {
+let NotificationsComponent = function() {
};
@@ -34,12 +32,16 @@ NotificationsComponent.prototype.subscribeToTopic = function(topic) {
RNPushNotification.subscribeToTopic(topic);
};
+NotificationsComponent.prototype.unsubscribeFromTopic = function(topic) {
+ RNPushNotification.unsubscribeFromTopic(topic);
+};
+
NotificationsComponent.prototype.cancelLocalNotifications = function(details) {
RNPushNotification.cancelLocalNotifications(details);
};
-NotificationsComponent.prototype.clearLocalNotification = function(details) {
- RNPushNotification.clearLocalNotification(details);
+NotificationsComponent.prototype.clearLocalNotification = function(details, tag) {
+ RNPushNotification.clearLocalNotification(details, tag);
};
NotificationsComponent.prototype.cancelAllLocalNotifications = function() {
@@ -66,13 +68,13 @@ NotificationsComponent.prototype.checkPermissions = function(callback) {
};
NotificationsComponent.prototype.addEventListener = function(type, handler) {
- var listener;
+ let listener;
if (type === 'notification') {
listener = DeviceEventEmitter.addListener(
DEVICE_NOTIF_EVENT,
function(notifData) {
if (notifData && notifData.dataJSON) {
- var data = JSON.parse(notifData.dataJSON);
+ let data = JSON.parse(notifData.dataJSON);
handler(data);
}
}
@@ -89,18 +91,28 @@ NotificationsComponent.prototype.addEventListener = function(type, handler) {
REMOTE_FETCH_EVENT,
function(notifData) {
if (notifData && notifData.dataJSON) {
- var notificationData = JSON.parse(notifData.dataJSON)
+ let notificationData = JSON.parse(notifData.dataJSON)
handler(notificationData);
}
}
);
- }
+ } else if (type === 'action') {
+ listener = DeviceEventEmitter.addListener(
+ NOTIF_ACTION_EVENT,
+ function(actionData) {
+ if (actionData && actionData.dataJSON) {
+ var action = JSON.parse(actionData.dataJSON)
+ handler(action);
+ }
+ }
+ );
+ }
_notifHandlers.set(type, listener);
};
NotificationsComponent.prototype.removeEventListener = function(type, handler) {
- var listener = _notifHandlers.get(type);
+ let listener = _notifHandlers.get(type);
if (!listener) {
return;
}
@@ -123,6 +135,9 @@ NotificationsComponent.prototype.removeAllDeliveredNotifications = function() {
NotificationsComponent.prototype.getDeliveredNotifications = function(callback) {
RNPushNotification.getDeliveredNotifications(callback);
}
+NotificationsComponent.prototype.getScheduledLocalNotifications = function(callback) {
+ RNPushNotification.getScheduledLocalNotifications(callback);
+}
NotificationsComponent.prototype.removeDeliveredNotifications = function(identifiers) {
RNPushNotification.removeDeliveredNotifications(identifiers);
}
@@ -131,7 +146,30 @@ NotificationsComponent.prototype.abandonPermissions = function() {
RNPushNotification.abandonPermissions();
}
+NotificationsComponent.prototype.invokeApp = function(data) {
+ RNPushNotification.invokeApp(data);
+}
+
+NotificationsComponent.prototype.getChannels = function(callback) {
+ RNPushNotification.getChannels(callback);
+}
+
+NotificationsComponent.prototype.channelExists = function(channel_id, callback) {
+ RNPushNotification.channelExists(channel_id, callback);
+}
+
+NotificationsComponent.prototype.createChannel = function(channelInfo, callback) {
+ RNPushNotification.createChannel(channelInfo, callback);
+}
+
+NotificationsComponent.prototype.channelBlocked = function(channel_id, callback) {
+ RNPushNotification.channelBlocked(channel_id, callback);
+}
+
+NotificationsComponent.prototype.deleteChannel = function(channel_id) {
+ RNPushNotification.deleteChannel(channel_id);
+}
+
module.exports = {
- state: false,
component: new NotificationsComponent()
};
diff --git a/component/index.ios.js b/component/index.ios.js
index 400214be0..82eee6a92 100644
--- a/component/index.ios.js
+++ b/component/index.ios.js
@@ -1,9 +1,7 @@
"use strict";
-import { AppState } from "react-native";
import PushNotificationIOS from "@react-native-community/push-notification-ios";
module.exports = {
- state: AppState,
component: PushNotificationIOS
};
diff --git a/example/App.js b/example/App.js
index 05840d965..eb08ef637 100644
--- a/example/App.js
+++ b/example/App.js
@@ -105,6 +105,34 @@ export default class App extends Component {
}}>
Abandon Permissions
+ {
+ this.notif.getScheduledLocalNotifications(notifs => console.log(notifs));
+ }}>
+ Console.Log Scheduled Local Notifications
+
+ {
+ this.notif.getDeliveredNotifications(notifs => console.log(notifs));
+ }}>
+ Console.Log Delivered Notifications
+
+ {
+ this.notif.createOrUpdateChannel();
+ }}>
+ Create or update a channel
+
+ {
+ this.notif.popInitialNotification();
+ }}>
+ popInitialNotification
+
diff --git a/example/NotifService.js b/example/NotifService.js
index de686b639..c57ccfc9c 100644
--- a/example/NotifService.js
+++ b/example/NotifService.js
@@ -4,23 +4,74 @@ import NotificationHandler from './NotificationHandler';
export default class NotifService {
constructor(onRegister, onNotification) {
this.lastId = 0;
+ this.lastChannelCounter = 0;
+
+ this.createDefaultChannels();
NotificationHandler.attachRegister(onRegister);
NotificationHandler.attachNotification(onNotification);
// Clear badge number at start
- PushNotification.getApplicationIconBadgeNumber(function(number) {
- if(number > 0) {
+ PushNotification.getApplicationIconBadgeNumber(function (number) {
+ if (number > 0) {
PushNotification.setApplicationIconBadgeNumber(0);
}
});
+
+ PushNotification.getChannels(function(channels) {
+ console.log(channels);
+ });
+ }
+
+ createDefaultChannels() {
+ PushNotification.createChannel(
+ {
+ channelId: "default-channel-id", // (required)
+ channelName: `Default channel`, // (required)
+ channelDescription: "A default channel", // (optional) default: undefined.
+ soundName: "default", // (optional) See `soundName` parameter of `localNotification` function
+ importance: 4, // (optional) default: 4. Int value of the Android notification importance
+ vibrate: true, // (optional) default: true. Creates the default vibration patten if true.
+ },
+ (created) => console.log(`createChannel 'default-channel-id' returned '${created}'`) // (optional) callback returns whether the channel was created, false means it already existed.
+ );
+ PushNotification.createChannel(
+ {
+ channelId: "sound-channel-id", // (required)
+ channelName: `Sound channel`, // (required)
+ channelDescription: "A sound channel", // (optional) default: undefined.
+ soundName: "sample.mp3", // (optional) See `soundName` parameter of `localNotification` function
+ importance: 4, // (optional) default: 4. Int value of the Android notification importance
+ vibrate: true, // (optional) default: true. Creates the default vibration patten if true.
+ },
+ (created) => console.log(`createChannel 'sound-channel-id' returned '${created}'`) // (optional) callback returns whether the channel was created, false means it already existed.
+ );
+ }
+
+ createOrUpdateChannel() {
+ this.lastChannelCounter++;
+ PushNotification.createChannel(
+ {
+ channelId: "custom-channel-id", // (required)
+ channelName: `Custom channel - Counter: ${this.lastChannelCounter}`, // (required)
+ channelDescription: `A custom channel to categorise your custom notifications. Updated at: ${Date.now()}`, // (optional) default: undefined.
+ soundName: "default", // (optional) See `soundName` parameter of `localNotification` function
+ importance: 4, // (optional) default: 4. Int value of the Android notification importance
+ vibrate: true, // (optional) default: true. Creates the default vibration patten if true.
+ },
+ (created) => console.log(`createChannel returned '${created}'`) // (optional) callback returns whether the channel was created, false means it already existed.
+ );
+ }
+
+ popInitialNotification() {
+ PushNotification.popInitialNotification((notification) => console.log('InitialNotication:', notification));
}
localNotif(soundName) {
this.lastId++;
PushNotification.localNotification({
/* Android Only Properties */
- id: this.lastId, // (optional) Valid unique 32 bit integer specified as string. default: Autogenerated Unique ID
+ channelId: soundName ? 'sound-channel-id' : 'default-channel-id',
ticker: 'My Notification Ticker', // (optional)
autoCancel: true, // (optional) default: true
largeIcon: 'ic_launcher', // (optional) default: "ic_launcher"
@@ -32,20 +83,26 @@ export default class NotifService {
vibration: 300, // vibration length in milliseconds, ignored if vibrate=false, default: 1000
tag: 'some_tag', // (optional) add tag to message
group: 'group', // (optional) add group to message
+ groupSummary: false, // (optional) set this notification to be the group summary for a group of notifications, default: false
ongoing: false, // (optional) set whether this is an "ongoing" notification
+ actions: ['Yes', 'No'], // (Android only) See the doc for notification actions to know more
+ invokeApp: true, // (optional) This enable click on actions to bring back the application to foreground or stay in background, default: true
+
+ when: null, // (optionnal) Add a timestamp pertaining to the notification (usually the time the event occurred). For apps targeting Build.VERSION_CODES.N and above, this time is not shown anymore by default and must be opted into by using `showWhen`, default: null.
+ usesChronometer: false, // (optional) Show the `when` field as a stopwatch. Instead of presenting `when` as a timestamp, the notification will show an automatically updating display of the minutes and seconds since when. Useful when showing an elapsed time (like an ongoing phone call), default: false.
+ timeoutAfter: null, // (optional) Specifies a duration in milliseconds after which this notification should be canceled, if it is not already canceled, default: null
/* iOS only properties */
- alertAction: 'view', // (optional) default: view
category: '', // (optional) default: empty string
- userInfo: {}, // (optional) default: {} (using null throws a JSON value '' error)
-
+
/* iOS and Android properties */
+ id: this.lastId, // (optional) Valid unique 32 bit integer specified as string. default: Autogenerated Unique ID
title: 'Local Notification', // (optional)
message: 'My Notification Message', // (required)
+ userInfo: { screen: 'home' }, // (optional) default: {} (using null throws a JSON value '' error)
playSound: !!soundName, // (optional) default: true
soundName: soundName ? soundName : 'default', // (optional) Sound to play when the notification is shown. Value of 'default' plays the default sound. It can be set to a custom sound such as 'android.resource://com.xyz/raw/my_sound'. It will look for the 'my_sound' audio file in 'res/raw' directory and play it. default: 'default' (default sound is played)
number: 10, // (optional) Valid 32 bit integer specified as string. default: none (Cannot be zero)
- actions: '["Yes", "No"]', // (Android only) See the doc for notification actions to know more
});
}
@@ -55,7 +112,7 @@ export default class NotifService {
date: new Date(Date.now() + 30 * 1000), // in 30 secs
/* Android Only Properties */
- id: this.lastId, // (optional) Valid unique 32 bit integer specified as string. default: Autogenerated Unique ID
+ channelId: soundName ? 'sound-channel-id' : 'default-channel-id',
ticker: 'My Notification Ticker', // (optional)
autoCancel: true, // (optional) default: true
largeIcon: 'ic_launcher', // (optional) default: "ic_launcher"
@@ -67,19 +124,26 @@ export default class NotifService {
vibration: 300, // vibration length in milliseconds, ignored if vibrate=false, default: 1000
tag: 'some_tag', // (optional) add tag to message
group: 'group', // (optional) add group to message
+ groupSummary: false, // (optional) set this notification to be the group summary for a group of notifications, default: false
ongoing: false, // (optional) set whether this is an "ongoing" notification
+ actions: ['Yes', 'No'], // (Android only) See the doc for notification actions to know more
+ invokeApp: false, // (optional) This enable click on actions to bring back the application to foreground or stay in background, default: true
+ when: null, // (optionnal) Add a timestamp pertaining to the notification (usually the time the event occurred). For apps targeting Build.VERSION_CODES.N and above, this time is not shown anymore by default and must be opted into by using `showWhen`, default: null.
+ usesChronometer: false, // (optional) Show the `when` field as a stopwatch. Instead of presenting `when` as a timestamp, the notification will show an automatically updating display of the minutes and seconds since when. Useful when showing an elapsed time (like an ongoing phone call), default: false.
+ timeoutAfter: null, // (optional) Specifies a duration in milliseconds after which this notification should be canceled, if it is not already canceled, default: null
+
/* iOS only properties */
- alertAction: 'view', // (optional) default: view
category: '', // (optional) default: empty string
- userInfo: {}, // (optional) default: {} (using null throws a JSON value '' error)
-
+
/* iOS and Android properties */
+ id: this.lastId, // (optional) Valid unique 32 bit integer specified as string. default: Autogenerated Unique ID
title: 'Scheduled Notification', // (optional)
message: 'My Notification Message', // (required)
+ userInfo: { sceen: "home" }, // (optional) default: {} (using null throws a JSON value '' error)
playSound: !!soundName, // (optional) default: true
- number: 10, // (optional) Valid 32 bit integer specified as string. default: none (Cannot be zero)
soundName: soundName ? soundName : 'default', // (optional) Sound to play when the notification is shown. Value of 'default' plays the default sound. It can be set to a custom sound such as 'android.resource://com.xyz/raw/my_sound'. It will look for the 'my_sound' audio file in 'res/raw' directory and play it. default: 'default' (default sound is played)
+ number: 10, // (optional) Valid 32 bit integer specified as string. default: none (Cannot be zero)
});
}
@@ -102,4 +166,12 @@ export default class NotifService {
abandonPermissions() {
PushNotification.abandonPermissions();
}
+
+ getScheduledLocalNotifications(callback) {
+ PushNotification.getScheduledLocalNotifications(callback);
+ }
+
+ getDeliveredNotifications(callback) {
+ PushNotification.getDeliveredNotifications(callback);
+ }
}
diff --git a/example/NotificationHandler.js b/example/NotificationHandler.js
index d21992416..040d3c0cf 100644
--- a/example/NotificationHandler.js
+++ b/example/NotificationHandler.js
@@ -17,6 +17,21 @@ class NotificationHandler {
}
}
+ onAction(notification) {
+ console.log ('Notification action received:');
+ console.log(notification.action);
+ console.log(notification);
+
+ if(notification.action === 'Yes') {
+ PushNotification.invokeApp(notification);
+ }
+ }
+
+ // (optional) Called when the user fails to register for remote notifications. Typically occurs when APNS is having issues, or the device is a simulator. (iOS)
+ onRegistrationError(err) {
+ console.log(err);
+ }
+
attachRegister(handler) {
this._onRegister = handler;
}
@@ -35,6 +50,12 @@ PushNotification.configure({
// (required) Called when a remote or local notification is opened or received
onNotification: handler.onNotification.bind(handler),
+ // (optional) Called when Action is pressed (Android)
+ onAction: handler.onAction.bind(handler),
+
+ // (optional) Called when the user fails to register for remote notifications. Typically occurs when APNS is having issues, or the device is a simulator. (iOS)
+ onRegistrationError: handler.onRegistrationError.bind(handler),
+
// IOS ONLY (optional): default: all - Permissions to register.
permissions: {
alert: true,
diff --git a/example/android/app/.settings/org.eclipse.buildship.core.prefs b/example/android/app/.settings/org.eclipse.buildship.core.prefs
index b1886adb4..b3d49cd88 100644
--- a/example/android/app/.settings/org.eclipse.buildship.core.prefs
+++ b/example/android/app/.settings/org.eclipse.buildship.core.prefs
@@ -1,2 +1,13 @@
+arguments=
+auto.sync=false
+build.scans.enabled=false
+connection.gradle.distribution=GRADLE_DISTRIBUTION(VERSION(6.3))
connection.project.dir=..
eclipse.preferences.version=1
+gradle.user.home=
+java.home=/Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home
+jvm.arguments=
+offline.mode=false
+override.workspace.settings=true
+show.console.view=true
+show.executions.view=true
diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml
index 8f8efe768..7e9056b6f 100644
--- a/example/android/app/src/main/AndroidManifest.xml
+++ b/example/android/app/src/main/AndroidManifest.xml
@@ -7,8 +7,6 @@
-
-
+
+
+
@@ -46,10 +47,6 @@
-
-
diff --git a/example/android/app/src/main/res/values/strings.xml b/example/android/app/src/main/res/values/strings.xml
index d75426c8a..3ea30b725 100644
--- a/example/android/app/src/main/res/values/strings.xml
+++ b/example/android/app/src/main/res/values/strings.xml
@@ -1,3 +1,5 @@
example
+ Exemple title loc_key
+ Exemple message loc_key: %1$s
diff --git a/example/ios/Podfile b/example/ios/Podfile
index faf88bd5e..00f500b39 100644
--- a/example/ios/Podfile
+++ b/example/ios/Podfile
@@ -1,94 +1,24 @@
-platform :ios, '9.0'
+platform :ios, '10.0'
+require_relative '../node_modules/react-native/scripts/react_native_pods'
require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules'
-def add_flipper_pods!(versions = {})
- versions['Flipper'] ||= '~> 0.33.1'
- versions['DoubleConversion'] ||= '1.1.7'
- versions['Flipper-Folly'] ||= '~> 2.1'
- versions['Flipper-Glog'] ||= '0.3.6'
- versions['Flipper-PeerTalk'] ||= '~> 0.0.4'
- versions['Flipper-RSocket'] ||= '~> 1.0'
-
- pod 'FlipperKit', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FlipperKitLayoutPlugin', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/SKIOSNetworkPlugin', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FlipperKitUserDefaultsPlugin', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FlipperKitReactPlugin', versions['Flipper'], :configuration => 'Debug'
-
- # List all transitive dependencies for FlipperKit pods
- # to avoid them being linked in Release builds
- pod 'Flipper', versions['Flipper'], :configuration => 'Debug'
- pod 'Flipper-DoubleConversion', versions['DoubleConversion'], :configuration => 'Debug'
- pod 'Flipper-Folly', versions['Flipper-Folly'], :configuration => 'Debug'
- pod 'Flipper-Glog', versions['Flipper-Glog'], :configuration => 'Debug'
- pod 'Flipper-PeerTalk', versions['Flipper-PeerTalk'], :configuration => 'Debug'
- pod 'Flipper-RSocket', versions['Flipper-RSocket'], :configuration => 'Debug'
- pod 'FlipperKit/Core', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/CppBridge', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FBCxxFollyDynamicConvert', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FBDefines', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FKPortForwarding', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FlipperKitHighlightOverlay', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FlipperKitLayoutTextSearchable', versions['Flipper'], :configuration => 'Debug'
- pod 'FlipperKit/FlipperKitNetworkPlugin', versions['Flipper'], :configuration => 'Debug'
-end
-
-# Post Install processing for Flipper
-def flipper_post_install(installer)
- installer.pods_project.targets.each do |target|
- if target.name == 'YogaKit'
- target.build_configurations.each do |config|
- config.build_settings['SWIFT_VERSION'] = '4.1'
- end
- end
- end
-end
-
target 'example' do
# Pods for example
- pod 'FBLazyVector', :path => "../node_modules/react-native/Libraries/FBLazyVector"
- pod 'FBReactNativeSpec', :path => "../node_modules/react-native/Libraries/FBReactNativeSpec"
- pod 'RCTRequired', :path => "../node_modules/react-native/Libraries/RCTRequired"
- pod 'RCTTypeSafety', :path => "../node_modules/react-native/Libraries/TypeSafety"
- pod 'React', :path => '../node_modules/react-native/'
- pod 'React-Core', :path => '../node_modules/react-native/'
- pod 'React-CoreModules', :path => '../node_modules/react-native/React/CoreModules'
- pod 'React-Core/DevSupport', :path => '../node_modules/react-native/'
- pod 'React-RCTActionSheet', :path => '../node_modules/react-native/Libraries/ActionSheetIOS'
- pod 'React-RCTAnimation', :path => '../node_modules/react-native/Libraries/NativeAnimation'
- pod 'React-RCTBlob', :path => '../node_modules/react-native/Libraries/Blob'
- pod 'React-RCTImage', :path => '../node_modules/react-native/Libraries/Image'
- pod 'React-RCTLinking', :path => '../node_modules/react-native/Libraries/LinkingIOS'
- pod 'React-RCTNetwork', :path => '../node_modules/react-native/Libraries/Network'
- pod 'React-RCTSettings', :path => '../node_modules/react-native/Libraries/Settings'
- pod 'React-RCTText', :path => '../node_modules/react-native/Libraries/Text'
- pod 'React-RCTVibration', :path => '../node_modules/react-native/Libraries/Vibration'
- pod 'React-Core/RCTWebSocket', :path => '../node_modules/react-native/'
-
- pod 'React-cxxreact', :path => '../node_modules/react-native/ReactCommon/cxxreact'
- pod 'React-jsi', :path => '../node_modules/react-native/ReactCommon/jsi'
- pod 'React-jsiexecutor', :path => '../node_modules/react-native/ReactCommon/jsiexecutor'
- pod 'React-jsinspector', :path => '../node_modules/react-native/ReactCommon/jsinspector'
- pod 'ReactCommon/callinvoker', :path => "../node_modules/react-native/ReactCommon"
- pod 'ReactCommon/turbomodule/core', :path => "../node_modules/react-native/ReactCommon"
- pod 'Yoga', :path => '../node_modules/react-native/ReactCommon/yoga', :modular_headers => true
+ config = use_native_modules!
- pod 'DoubleConversion', :podspec => '../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec'
- pod 'glog', :podspec => '../node_modules/react-native/third-party-podspecs/glog.podspec'
- pod 'Folly', :podspec => '../node_modules/react-native/third-party-podspecs/Folly.podspec'
+ use_react_native!(:path => config["reactNativePath"])
target 'exampleTests' do
inherit! :complete
# Pods for testing
end
- use_native_modules!
# Enables Flipper.
#
# Note that if you have use_frameworks! enabled, Flipper will not work and
# you should disable these next few lines.
- add_flipper_pods!
+ use_flipper!
post_install do |installer|
flipper_post_install(installer)
end
diff --git a/example/ios/Podfile.lock b/example/ios/Podfile.lock
index cc1639da9..dfed099b2 100644
--- a/example/ios/Podfile.lock
+++ b/example/ios/Podfile.lock
@@ -3,297 +3,301 @@ PODS:
- CocoaAsyncSocket (7.6.4)
- CocoaLibEvent (1.0.0)
- DoubleConversion (1.1.6)
- - FBLazyVector (0.62.2)
- - FBReactNativeSpec (0.62.2):
- - Folly (= 2018.10.22.00)
- - RCTRequired (= 0.62.2)
- - RCTTypeSafety (= 0.62.2)
- - React-Core (= 0.62.2)
- - React-jsi (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - Flipper (0.33.1):
- - Flipper-Folly (~> 2.1)
- - Flipper-RSocket (~> 1.0)
+ - FBLazyVector (0.63.3)
+ - FBReactNativeSpec (0.63.3):
+ - Folly (= 2020.01.13.00)
+ - RCTRequired (= 0.63.3)
+ - RCTTypeSafety (= 0.63.3)
+ - React-Core (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - ReactCommon/turbomodule/core (= 0.63.3)
+ - Flipper (0.54.0):
+ - Flipper-Folly (~> 2.2)
+ - Flipper-RSocket (~> 1.1)
- Flipper-DoubleConversion (1.1.7)
- - Flipper-Folly (2.1.1):
+ - Flipper-Folly (2.3.0):
- boost-for-react-native
- CocoaLibEvent (~> 1.0)
- Flipper-DoubleConversion
- Flipper-Glog
- - OpenSSL-Universal (= 1.0.2.19)
+ - OpenSSL-Universal (= 1.0.2.20)
- Flipper-Glog (0.3.6)
- Flipper-PeerTalk (0.0.4)
- - Flipper-RSocket (1.0.0):
- - Flipper-Folly (~> 2.0)
- - FlipperKit (0.33.1):
- - FlipperKit/Core (= 0.33.1)
- - FlipperKit/Core (0.33.1):
- - Flipper (~> 0.33.1)
+ - Flipper-RSocket (1.1.0):
+ - Flipper-Folly (~> 2.2)
+ - FlipperKit (0.54.0):
+ - FlipperKit/Core (= 0.54.0)
+ - FlipperKit/Core (0.54.0):
+ - Flipper (~> 0.54.0)
- FlipperKit/CppBridge
- FlipperKit/FBCxxFollyDynamicConvert
- FlipperKit/FBDefines
- FlipperKit/FKPortForwarding
- - FlipperKit/CppBridge (0.33.1):
- - Flipper (~> 0.33.1)
- - FlipperKit/FBCxxFollyDynamicConvert (0.33.1):
- - Flipper-Folly (~> 2.1)
- - FlipperKit/FBDefines (0.33.1)
- - FlipperKit/FKPortForwarding (0.33.1):
+ - FlipperKit/CppBridge (0.54.0):
+ - Flipper (~> 0.54.0)
+ - FlipperKit/FBCxxFollyDynamicConvert (0.54.0):
+ - Flipper-Folly (~> 2.2)
+ - FlipperKit/FBDefines (0.54.0)
+ - FlipperKit/FKPortForwarding (0.54.0):
- CocoaAsyncSocket (~> 7.6)
- Flipper-PeerTalk (~> 0.0.4)
- - FlipperKit/FlipperKitHighlightOverlay (0.33.1)
- - FlipperKit/FlipperKitLayoutPlugin (0.33.1):
+ - FlipperKit/FlipperKitHighlightOverlay (0.54.0)
+ - FlipperKit/FlipperKitLayoutPlugin (0.54.0):
- FlipperKit/Core
- FlipperKit/FlipperKitHighlightOverlay
- FlipperKit/FlipperKitLayoutTextSearchable
- YogaKit (~> 1.18)
- - FlipperKit/FlipperKitLayoutTextSearchable (0.33.1)
- - FlipperKit/FlipperKitNetworkPlugin (0.33.1):
+ - FlipperKit/FlipperKitLayoutTextSearchable (0.54.0)
+ - FlipperKit/FlipperKitNetworkPlugin (0.54.0):
- FlipperKit/Core
- - FlipperKit/FlipperKitReactPlugin (0.33.1):
+ - FlipperKit/FlipperKitReactPlugin (0.54.0):
- FlipperKit/Core
- - FlipperKit/FlipperKitUserDefaultsPlugin (0.33.1):
+ - FlipperKit/FlipperKitUserDefaultsPlugin (0.54.0):
- FlipperKit/Core
- - FlipperKit/SKIOSNetworkPlugin (0.33.1):
+ - FlipperKit/SKIOSNetworkPlugin (0.54.0):
- FlipperKit/Core
- FlipperKit/FlipperKitNetworkPlugin
- - Folly (2018.10.22.00):
+ - Folly (2020.01.13.00):
- boost-for-react-native
- DoubleConversion
- - Folly/Default (= 2018.10.22.00)
+ - Folly/Default (= 2020.01.13.00)
- glog
- - Folly/Default (2018.10.22.00):
+ - Folly/Default (2020.01.13.00):
- boost-for-react-native
- DoubleConversion
- glog
- glog (0.3.5)
- - OpenSSL-Universal (1.0.2.19):
- - OpenSSL-Universal/Static (= 1.0.2.19)
- - OpenSSL-Universal/Static (1.0.2.19)
- - RCTRequired (0.62.2)
- - RCTTypeSafety (0.62.2):
- - FBLazyVector (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - RCTRequired (= 0.62.2)
- - React-Core (= 0.62.2)
- - React (0.62.2):
- - React-Core (= 0.62.2)
- - React-Core/DevSupport (= 0.62.2)
- - React-Core/RCTWebSocket (= 0.62.2)
- - React-RCTActionSheet (= 0.62.2)
- - React-RCTAnimation (= 0.62.2)
- - React-RCTBlob (= 0.62.2)
- - React-RCTImage (= 0.62.2)
- - React-RCTLinking (= 0.62.2)
- - React-RCTNetwork (= 0.62.2)
- - React-RCTSettings (= 0.62.2)
- - React-RCTText (= 0.62.2)
- - React-RCTVibration (= 0.62.2)
- - React-Core (0.62.2):
- - Folly (= 2018.10.22.00)
+ - OpenSSL-Universal (1.0.2.20):
+ - OpenSSL-Universal/Static (= 1.0.2.20)
+ - OpenSSL-Universal/Static (1.0.2.20)
+ - RCTRequired (0.63.3)
+ - RCTTypeSafety (0.63.3):
+ - FBLazyVector (= 0.63.3)
+ - Folly (= 2020.01.13.00)
+ - RCTRequired (= 0.63.3)
+ - React-Core (= 0.63.3)
+ - React (0.63.3):
+ - React-Core (= 0.63.3)
+ - React-Core/DevSupport (= 0.63.3)
+ - React-Core/RCTWebSocket (= 0.63.3)
+ - React-RCTActionSheet (= 0.63.3)
+ - React-RCTAnimation (= 0.63.3)
+ - React-RCTBlob (= 0.63.3)
+ - React-RCTImage (= 0.63.3)
+ - React-RCTLinking (= 0.63.3)
+ - React-RCTNetwork (= 0.63.3)
+ - React-RCTSettings (= 0.63.3)
+ - React-RCTText (= 0.63.3)
+ - React-RCTVibration (= 0.63.3)
+ - React-callinvoker (0.63.3)
+ - React-Core (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- - React-Core/Default (= 0.62.2)
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-Core/Default (= 0.63.3)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
- Yoga
- - React-Core/CoreModulesHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/CoreModulesHeaders (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
- Yoga
- - React-Core/Default (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/Default (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
- Yoga
- - React-Core/DevSupport (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/DevSupport (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- - React-Core/Default (= 0.62.2)
- - React-Core/RCTWebSocket (= 0.62.2)
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
- - React-jsinspector (= 0.62.2)
+ - React-Core/Default (= 0.63.3)
+ - React-Core/RCTWebSocket (= 0.63.3)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
+ - React-jsinspector (= 0.63.3)
- Yoga
- - React-Core/RCTActionSheetHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTActionSheetHeaders (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
- Yoga
- - React-Core/RCTAnimationHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTAnimationHeaders (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
- Yoga
- - React-Core/RCTBlobHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTBlobHeaders (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
- Yoga
- - React-Core/RCTImageHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTImageHeaders (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
- Yoga
- - React-Core/RCTLinkingHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTLinkingHeaders (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
- Yoga
- - React-Core/RCTNetworkHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTNetworkHeaders (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
- Yoga
- - React-Core/RCTSettingsHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTSettingsHeaders (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
- Yoga
- - React-Core/RCTTextHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTTextHeaders (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
- Yoga
- - React-Core/RCTVibrationHeaders (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTVibrationHeaders (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- React-Core/Default
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
- Yoga
- - React-Core/RCTWebSocket (0.62.2):
- - Folly (= 2018.10.22.00)
+ - React-Core/RCTWebSocket (0.63.3):
+ - Folly (= 2020.01.13.00)
- glog
- - React-Core/Default (= 0.62.2)
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsiexecutor (= 0.62.2)
+ - React-Core/Default (= 0.63.3)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsiexecutor (= 0.63.3)
- Yoga
- - React-CoreModules (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - RCTTypeSafety (= 0.62.2)
- - React-Core/CoreModulesHeaders (= 0.62.2)
- - React-RCTImage (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-cxxreact (0.62.2):
+ - React-CoreModules (0.63.3):
+ - FBReactNativeSpec (= 0.63.3)
+ - Folly (= 2020.01.13.00)
+ - RCTTypeSafety (= 0.63.3)
+ - React-Core/CoreModulesHeaders (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-RCTImage (= 0.63.3)
+ - ReactCommon/turbomodule/core (= 0.63.3)
+ - React-cxxreact (0.63.3):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- - Folly (= 2018.10.22.00)
+ - Folly (= 2020.01.13.00)
- glog
- - React-jsinspector (= 0.62.2)
- - React-jsi (0.62.2):
+ - React-callinvoker (= 0.63.3)
+ - React-jsinspector (= 0.63.3)
+ - React-jsi (0.63.3):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- - Folly (= 2018.10.22.00)
+ - Folly (= 2020.01.13.00)
- glog
- - React-jsi/Default (= 0.62.2)
- - React-jsi/Default (0.62.2):
+ - React-jsi/Default (= 0.63.3)
+ - React-jsi/Default (0.63.3):
- boost-for-react-native (= 1.63.0)
- DoubleConversion
- - Folly (= 2018.10.22.00)
+ - Folly (= 2020.01.13.00)
- glog
- - React-jsiexecutor (0.62.2):
+ - React-jsiexecutor (0.63.3):
- DoubleConversion
- - Folly (= 2018.10.22.00)
+ - Folly (= 2020.01.13.00)
- glog
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-jsinspector (0.62.2)
- - React-RCTActionSheet (0.62.2):
- - React-Core/RCTActionSheetHeaders (= 0.62.2)
- - React-RCTAnimation (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - RCTTypeSafety (= 0.62.2)
- - React-Core/RCTAnimationHeaders (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-RCTBlob (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - React-Core/RCTBlobHeaders (= 0.62.2)
- - React-Core/RCTWebSocket (= 0.62.2)
- - React-jsi (= 0.62.2)
- - React-RCTNetwork (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-RCTImage (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - RCTTypeSafety (= 0.62.2)
- - React-Core/RCTImageHeaders (= 0.62.2)
- - React-RCTNetwork (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-RCTLinking (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - React-Core/RCTLinkingHeaders (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-RCTNetwork (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - RCTTypeSafety (= 0.62.2)
- - React-Core/RCTNetworkHeaders (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-RCTSettings (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - RCTTypeSafety (= 0.62.2)
- - React-Core/RCTSettingsHeaders (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - React-RCTText (0.62.2):
- - React-Core/RCTTextHeaders (= 0.62.2)
- - React-RCTVibration (0.62.2):
- - FBReactNativeSpec (= 0.62.2)
- - Folly (= 2018.10.22.00)
- - React-Core/RCTVibrationHeaders (= 0.62.2)
- - ReactCommon/turbomodule/core (= 0.62.2)
- - ReactCommon/callinvoker (0.62.2):
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-jsinspector (0.63.3)
+ - React-RCTActionSheet (0.63.3):
+ - React-Core/RCTActionSheetHeaders (= 0.63.3)
+ - React-RCTAnimation (0.63.3):
+ - FBReactNativeSpec (= 0.63.3)
+ - Folly (= 2020.01.13.00)
+ - RCTTypeSafety (= 0.63.3)
+ - React-Core/RCTAnimationHeaders (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - ReactCommon/turbomodule/core (= 0.63.3)
+ - React-RCTBlob (0.63.3):
+ - FBReactNativeSpec (= 0.63.3)
+ - Folly (= 2020.01.13.00)
+ - React-Core/RCTBlobHeaders (= 0.63.3)
+ - React-Core/RCTWebSocket (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-RCTNetwork (= 0.63.3)
+ - ReactCommon/turbomodule/core (= 0.63.3)
+ - React-RCTImage (0.63.3):
+ - FBReactNativeSpec (= 0.63.3)
+ - Folly (= 2020.01.13.00)
+ - RCTTypeSafety (= 0.63.3)
+ - React-Core/RCTImageHeaders (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - React-RCTNetwork (= 0.63.3)
+ - ReactCommon/turbomodule/core (= 0.63.3)
+ - React-RCTLinking (0.63.3):
+ - FBReactNativeSpec (= 0.63.3)
+ - React-Core/RCTLinkingHeaders (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - ReactCommon/turbomodule/core (= 0.63.3)
+ - React-RCTNetwork (0.63.3):
+ - FBReactNativeSpec (= 0.63.3)
+ - Folly (= 2020.01.13.00)
+ - RCTTypeSafety (= 0.63.3)
+ - React-Core/RCTNetworkHeaders (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - ReactCommon/turbomodule/core (= 0.63.3)
+ - React-RCTSettings (0.63.3):
+ - FBReactNativeSpec (= 0.63.3)
+ - Folly (= 2020.01.13.00)
+ - RCTTypeSafety (= 0.63.3)
+ - React-Core/RCTSettingsHeaders (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - ReactCommon/turbomodule/core (= 0.63.3)
+ - React-RCTText (0.63.3):
+ - React-Core/RCTTextHeaders (= 0.63.3)
+ - React-RCTVibration (0.63.3):
+ - FBReactNativeSpec (= 0.63.3)
+ - Folly (= 2020.01.13.00)
+ - React-Core/RCTVibrationHeaders (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - ReactCommon/turbomodule/core (= 0.63.3)
+ - ReactCommon/turbomodule/core (0.63.3):
- DoubleConversion
- - Folly (= 2018.10.22.00)
+ - Folly (= 2020.01.13.00)
- glog
- - React-cxxreact (= 0.62.2)
- - ReactCommon/turbomodule/core (0.62.2):
- - DoubleConversion
- - Folly (= 2018.10.22.00)
- - glog
- - React-Core (= 0.62.2)
- - React-cxxreact (= 0.62.2)
- - React-jsi (= 0.62.2)
- - ReactCommon/callinvoker (= 0.62.2)
- - RNCPushNotificationIOS (1.1.1):
- - React
+ - React-callinvoker (= 0.63.3)
+ - React-Core (= 0.63.3)
+ - React-cxxreact (= 0.63.3)
+ - React-jsi (= 0.63.3)
+ - RNCPushNotificationIOS (1.8.0):
+ - React-Core
- Yoga (1.14.0)
- YogaKit (1.18.1):
- Yoga (~> 1.14)
@@ -302,30 +306,31 @@ DEPENDENCIES:
- DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`)
- FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`)
- FBReactNativeSpec (from `../node_modules/react-native/Libraries/FBReactNativeSpec`)
- - Flipper (~> 0.33.1)
+ - Flipper (~> 0.54.0)
- Flipper-DoubleConversion (= 1.1.7)
- - Flipper-Folly (~> 2.1)
+ - Flipper-Folly (~> 2.2)
- Flipper-Glog (= 0.3.6)
- Flipper-PeerTalk (~> 0.0.4)
- - Flipper-RSocket (~> 1.0)
- - FlipperKit (~> 0.33.1)
- - FlipperKit/Core (~> 0.33.1)
- - FlipperKit/CppBridge (~> 0.33.1)
- - FlipperKit/FBCxxFollyDynamicConvert (~> 0.33.1)
- - FlipperKit/FBDefines (~> 0.33.1)
- - FlipperKit/FKPortForwarding (~> 0.33.1)
- - FlipperKit/FlipperKitHighlightOverlay (~> 0.33.1)
- - FlipperKit/FlipperKitLayoutPlugin (~> 0.33.1)
- - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.33.1)
- - FlipperKit/FlipperKitNetworkPlugin (~> 0.33.1)
- - FlipperKit/FlipperKitReactPlugin (~> 0.33.1)
- - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.33.1)
- - FlipperKit/SKIOSNetworkPlugin (~> 0.33.1)
+ - Flipper-RSocket (~> 1.1)
+ - FlipperKit (~> 0.54.0)
+ - FlipperKit/Core (~> 0.54.0)
+ - FlipperKit/CppBridge (~> 0.54.0)
+ - FlipperKit/FBCxxFollyDynamicConvert (~> 0.54.0)
+ - FlipperKit/FBDefines (~> 0.54.0)
+ - FlipperKit/FKPortForwarding (~> 0.54.0)
+ - FlipperKit/FlipperKitHighlightOverlay (~> 0.54.0)
+ - FlipperKit/FlipperKitLayoutPlugin (~> 0.54.0)
+ - FlipperKit/FlipperKitLayoutTextSearchable (~> 0.54.0)
+ - FlipperKit/FlipperKitNetworkPlugin (~> 0.54.0)
+ - FlipperKit/FlipperKitReactPlugin (~> 0.54.0)
+ - FlipperKit/FlipperKitUserDefaultsPlugin (~> 0.54.0)
+ - FlipperKit/SKIOSNetworkPlugin (~> 0.54.0)
- Folly (from `../node_modules/react-native/third-party-podspecs/Folly.podspec`)
- glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`)
- RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`)
- RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`)
- React (from `../node_modules/react-native/`)
+ - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`)
- React-Core (from `../node_modules/react-native/`)
- React-Core/DevSupport (from `../node_modules/react-native/`)
- React-Core/RCTWebSocket (from `../node_modules/react-native/`)
@@ -343,7 +348,6 @@ DEPENDENCIES:
- React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`)
- React-RCTText (from `../node_modules/react-native/Libraries/Text`)
- React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`)
- - ReactCommon/callinvoker (from `../node_modules/react-native/ReactCommon`)
- ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`)
- "RNCPushNotificationIOS (from `../node_modules/@react-native-community/push-notification-ios`)"
- Yoga (from `../node_modules/react-native/ReactCommon/yoga`)
@@ -380,6 +384,8 @@ EXTERNAL SOURCES:
:path: "../node_modules/react-native/Libraries/TypeSafety"
React:
:path: "../node_modules/react-native/"
+ React-callinvoker:
+ :path: "../node_modules/react-native/ReactCommon/callinvoker"
React-Core:
:path: "../node_modules/react-native/"
React-CoreModules:
@@ -421,42 +427,43 @@ SPEC CHECKSUMS:
boost-for-react-native: 39c7adb57c4e60d6c5479dd8623128eb5b3f0f2c
CocoaAsyncSocket: 694058e7c0ed05a9e217d1b3c7ded962f4180845
CocoaLibEvent: 2fab71b8bd46dd33ddb959f7928ec5909f838e3f
- DoubleConversion: 5805e889d232975c086db112ece9ed034df7a0b2
- FBLazyVector: 4aab18c93cd9546e4bfed752b4084585eca8b245
- FBReactNativeSpec: 5465d51ccfeecb7faa12f9ae0024f2044ce4044e
- Flipper: 6c1f484f9a88d30ab3e272800d53688439e50f69
+ DoubleConversion: cde416483dac037923206447da6e1454df403714
+ FBLazyVector: 878b59e31113e289e275165efbe4b54fa614d43d
+ FBReactNativeSpec: 7da9338acfb98d4ef9e5536805a0704572d33c2f
+ Flipper: be611d4b742d8c87fbae2ca5f44603a02539e365
Flipper-DoubleConversion: 38631e41ef4f9b12861c67d17cb5518d06badc41
- Flipper-Folly: 2de3d03e0acc7064d5e4ed9f730e2f217486f162
+ Flipper-Folly: e4493b013c02d9347d5e0cb4d128680239f6c78a
Flipper-Glog: 1dfd6abf1e922806c52ceb8701a3599a79a200a6
Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9
- Flipper-RSocket: 1260a31c05c238eabfa9bb8a64e3983049048371
- FlipperKit: 6dc9b8f4ef60d9e5ded7f0264db299c91f18832e
- Folly: 30e7936e1c45c08d884aa59369ed951a8e68cf51
- glog: 1f3da668190260b06b429bb211bfbee5cd790c28
- OpenSSL-Universal: 8b48cc0d10c1b2923617dfe5c178aa9ed2689355
- RCTRequired: cec6a34b3ac8a9915c37e7e4ad3aa74726ce4035
- RCTTypeSafety: 93006131180074cffa227a1075802c89a49dd4ce
- React: 29a8b1a02bd764fb7644ef04019270849b9a7ac3
- React-Core: b12bffb3f567fdf99510acb716ef1abd426e0e05
- React-CoreModules: 4a9b87bbe669d6c3173c0132c3328e3b000783d0
- React-cxxreact: e65f9c2ba0ac5be946f53548c1aaaee5873a8103
- React-jsi: b6dc94a6a12ff98e8877287a0b7620d365201161
- React-jsiexecutor: 1540d1c01bb493ae3124ed83351b1b6a155db7da
- React-jsinspector: 512e560d0e985d0e8c479a54a4e5c147a9c83493
- React-RCTActionSheet: f41ea8a811aac770e0cc6e0ad6b270c644ea8b7c
- React-RCTAnimation: 49ab98b1c1ff4445148b72a3d61554138565bad0
- React-RCTBlob: a332773f0ebc413a0ce85942a55b064471587a71
- React-RCTImage: e70be9b9c74fe4e42d0005f42cace7981c994ac3
- React-RCTLinking: c1b9739a88d56ecbec23b7f63650e44672ab2ad2
- React-RCTNetwork: 73138b6f45e5a2768ad93f3d57873c2a18d14b44
- React-RCTSettings: 6e3738a87e21b39a8cb08d627e68c44acf1e325a
- React-RCTText: fae545b10cfdb3d247c36c56f61a94cfd6dba41d
- React-RCTVibration: 4356114dbcba4ce66991096e51a66e61eda51256
- ReactCommon: ed4e11d27609d571e7eee8b65548efc191116eb3
- RNCPushNotificationIOS: a0b6894f4ad9b93d9dac467fdf4d055660ac8a0d
- Yoga: 3ebccbdd559724312790e7742142d062476b698e
+ Flipper-RSocket: 64e7431a55835eb953b0bf984ef3b90ae9fdddd7
+ FlipperKit: ab353d41aea8aae2ea6daaf813e67496642f3d7d
+ Folly: b73c3869541e86821df3c387eb0af5f65addfab4
+ glog: 40a13f7840415b9a77023fbcae0f1e6f43192af3
+ OpenSSL-Universal: ff34003318d5e1163e9529b08470708e389ffcdd
+ RCTRequired: 48884c74035a0b5b76dbb7a998bd93bcfc5f2047
+ RCTTypeSafety: edf4b618033c2f1c5b7bc3d90d8e085ed95ba2ab
+ React: f36e90f3ceb976546e97df3403e37d226f79d0e3
+ React-callinvoker: 18874f621eb96625df7a24a7dc8d6e07391affcd
+ React-Core: ac3d816b8e3493970153f4aaf0cff18af0bb95e6
+ React-CoreModules: 4016d3a4e518bcfc4f5a51252b5a05692ca6f0e1
+ React-cxxreact: ffc9129013b87cb36cf3f30a86695a3c397b0f99
+ React-jsi: df07aa95b39c5be3e41199921509bfa929ed2b9d
+ React-jsiexecutor: b56c03e61c0dd5f5801255f2160a815f4a53d451
+ React-jsinspector: 8e68ffbfe23880d3ee9bafa8be2777f60b25cbe2
+ React-RCTActionSheet: 53ea72699698b0b47a6421cb1c8b4ab215a774aa
+ React-RCTAnimation: 1befece0b5183c22ae01b966f5583f42e69a83c2
+ React-RCTBlob: 0b284339cbe4b15705a05e2313a51c6d8b51fa40
+ React-RCTImage: d1756599ebd4dc2cb19d1682fe67c6b976658387
+ React-RCTLinking: 9af0a51c6d6a4dd1674daadafffc6d03033a6d18
+ React-RCTNetwork: 332c83929cc5eae0b3bbca4add1d668e1fc18bda
+ React-RCTSettings: d6953772cfd55f2c68ad72b7ef29efc7ec49f773
+ React-RCTText: 65a6de06a7389098ce24340d1d3556015c38f746
+ React-RCTVibration: 8e9fb25724a0805107fc1acc9075e26f814df454
+ ReactCommon: 4167844018c9ed375cc01a843e9ee564399e53c3
+ RNCPushNotificationIOS: 61a7c72bd1ebad3568025957d001e0f0e7b32191
+ Yoga: 7d13633d129fd179e01b8953d38d47be90db185a
YogaKit: f782866e155069a2cca2517aafea43200b01fd5a
-PODFILE CHECKSUM: 56c2537f71f3f02200d6918c542a8e89a0b422fa
+PODFILE CHECKSUM: 2ce9d926fa5d7b425b9668ca074ec805d1ac1617
-COCOAPODS: 1.9.1
+COCOAPODS: 1.10.0
diff --git a/example/ios/example.xcodeproj/project.pbxproj b/example/ios/example.xcodeproj/project.pbxproj
index 268d7cc50..bc6e7614d 100644
--- a/example/ios/example.xcodeproj/project.pbxproj
+++ b/example/ios/example.xcodeproj/project.pbxproj
@@ -213,6 +213,7 @@
00E356EA1AD99517003FC87E /* Sources */,
00E356EB1AD99517003FC87E /* Frameworks */,
00E356EC1AD99517003FC87E /* Resources */,
+ CEF8B2703D93599E45D6BF6B /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -234,6 +235,7 @@
13B07F8C1A680F5B00A75B9A /* Frameworks */,
13B07F8E1A680F5B00A75B9A /* Resources */,
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
+ 14C94C1C1903F2189AE891A6 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -380,6 +382,24 @@
shellPath = /bin/sh;
shellScript = "export NODE_BINARY=node\n../node_modules/react-native/scripts/react-native-xcode.sh";
};
+ 14C94C1C1903F2189AE891A6 /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh",
+ "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputPaths = (
+ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example/Pods-example-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
2D02E4CB1E0B4B27006451C7 /* Bundle React Native Code And Images */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
@@ -482,6 +502,24 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
+ CEF8B2703D93599E45D6BF6B /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-resources.sh",
+ "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/AccessibilityResources.bundle",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputPaths = (
+ "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/AccessibilityResources.bundle",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-example-exampleTests/Pods-example-exampleTests-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
FD10A7F022414F080027D42C /* Start Packager */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
diff --git a/example/ios/example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 000000000..18d981003
--- /dev/null
+++ b/example/ios/example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/example/ios/example/AppDelegate.m b/example/ios/example/AppDelegate.m
index 0e513c62e..c703e13b0 100644
--- a/example/ios/example/AppDelegate.m
+++ b/example/ios/example/AppDelegate.m
@@ -6,7 +6,7 @@
#import
#import
-#if DEBUG
+#if FB_SONARKIT_ENABLED
#import
#import
#import
@@ -29,7 +29,7 @@ @implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
-#if DEBUG
+#if FB_SONARKIT_ENABLED
InitializeFlipper(application);
#endif
@@ -40,7 +40,12 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
// Define UNUserNotificationCenter
UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
- center.delegate = self;
+
+ [center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound + UNAuthorizationOptionBadge)completionHandler:^(BOOL granted, NSError * _Nullable error) {
+ if (granted) {
+ center.delegate = self;
+ }
+ }];
rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1];
@@ -52,6 +57,11 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(
return YES;
}
+-(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
+{
+ completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
+}
+
// Required to register for notifications
- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
@@ -73,17 +83,22 @@ - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotif
{
[RNCPushNotificationIOS didFailToRegisterForRemoteNotificationsWithError:error];
}
-// Required for the localNotification event.
-- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
+
+// IOS 10+ Required for localNotification event
+- (void)userNotificationCenter:(UNUserNotificationCenter *)center
+didReceiveNotificationResponse:(UNNotificationResponse *)response
+ withCompletionHandler:(void (^)(void))completionHandler
{
- [RNCPushNotificationIOS didReceiveLocalNotification:notification];
+ [RNCPushNotificationIOS didReceiveNotificationResponse:response];
+ completionHandler();
}
-//Called when a notification is delivered to a foreground app.
--(void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
+// IOS 4-10 Required for the localNotification event.
+- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification
{
- completionHandler(UNAuthorizationOptionSound | UNAuthorizationOptionAlert | UNAuthorizationOptionBadge);
+ [RNCPushNotificationIOS didReceiveLocalNotification:notification];
}
+
- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
diff --git a/example/ios/example/Info.plist b/example/ios/example/Info.plist
index 20fb35c96..173ee5c0a 100644
--- a/example/ios/example/Info.plist
+++ b/example/ios/example/Info.plist
@@ -39,6 +39,12 @@
NSLocationWhenInUseUsageDescription
+ UIBackgroundModes
+
+ fetch
+ processing
+ remote-notification
+
UILaunchStoryboardName
LaunchScreen
UIRequiredDeviceCapabilities
diff --git a/example/package.json b/example/package.json
index b01e827fe..867a10807 100644
--- a/example/package.json
+++ b/example/package.json
@@ -7,13 +7,14 @@
"ios": "react-native run-ios",
"start": "react-native start",
"test": "jest",
- "lint": "eslint ."
+ "lint": "eslint .",
+ "pod-install": "cd ios && pod install"
},
"dependencies": {
- "@react-native-community/push-notification-ios": "^1.1.1",
- "react": "16.11.0",
- "react-native": "0.62.2",
- "react-native-push-notification": "git+https://git@github.com/zo0r/react-native-push-notification.git"
+ "@react-native-community/push-notification-ios": "^1.7.0",
+ "react": "16.13.1",
+ "react-native": "0.63.3",
+ "react-native-push-notification": "zo0r/react-native-push-notification#dev"
},
"devDependencies": {
"@babel/core": "^7.9.0",
diff --git a/index.js b/index.js
index a99c5f3ea..47f67900c 100644
--- a/index.js
+++ b/index.js
@@ -4,309 +4,476 @@
'use strict';
-var RNNotificationsComponent = require( './component' );
-
-var AppState = RNNotificationsComponent.state;
-var RNNotifications = RNNotificationsComponent.component;
-
-var Platform = require('react-native').Platform;
-
-var Notifications = {
- handler: RNNotifications,
- onRegister: false,
- onError: false,
- onNotification: false,
+import { AppState, Platform } from 'react-native';
+import { component } from './component';
+
+const Notifications = {
+ handler: component,
+ onRegister: false,
+ onRegistrationError: false,
+ onNotification: false,
+ onAction: false,
onRemoteFetch: false,
- isLoaded: false,
- hasPoppedInitialNotification: false,
+ isLoaded: false,
+ isPopInitialNotification: false,
- isPermissionsRequestPending: false,
+ isPermissionsRequestPending: false,
- permissions: {
- alert: true,
- badge: true,
- sound: true
- }
+ permissions: {
+ alert: true,
+ badge: true,
+ sound: true
+ }
};
Notifications.callNative = function(name, params) {
- if ( typeof this.handler[name] === 'function' ) {
- if ( typeof params !== 'array' &&
- typeof params !== 'object' ) {
- params = [];
- }
+ if ( typeof this.handler[name] === 'function' ) {
+ if ( typeof params !== 'array' &&
+ typeof params !== 'object' ) {
+ params = [];
+ }
- return this.handler[name](...params);
- } else {
- return null;
- }
+ return this.handler[name](...params);
+ } else {
+ return null;
+ }
};
/**
* Configure local and remote notifications
- * @param {Object} options
- * @param {function} options.onRegister - Fired when the user registers for remote notifications.
- * @param {function} options.onNotification - Fired when a remote notification is received.
- * @param {function} options.onError - None
- * @param {Object} options.permissions - Permissions list
- * @param {Boolean} options.requestPermissions - Check permissions when register
+ * @param {Object} options
+ * @param {function} options.onRegister - Fired when the user registers for remote notifications.
+ * @param {function} options.onNotification - Fired when a remote notification is received.
+ * @param {function} options.onAction - Fired when a remote notification is received.
+ * @param {function} options.onRegistrationError - Fired when the user fails to register for remote notifications.
+ * @param {Object} options.permissions - Permissions list
+ * @param {Boolean} options.requestPermissions - Check permissions when register
*/
Notifications.configure = function(options) {
- if ( typeof options.onRegister !== 'undefined' ) {
- this.onRegister = options.onRegister;
- }
-
- if ( typeof options.onError !== 'undefined' ) {
- this.onError = options.onError;
- }
-
- if ( typeof options.onNotification !== 'undefined' ) {
- this.onNotification = options.onNotification;
- }
-
- if ( typeof options.permissions !== 'undefined' ) {
- this.permissions = options.permissions;
- }
+ if ( typeof options.onRegister !== 'undefined' ) {
+ this.onRegister = options.onRegister;
+ }
+
+ if ( typeof options.onRegistrationError !== 'undefined' ) {
+ this.onRegistrationError = options.onRegistrationError;
+ }
+
+ if ( typeof options.onNotification !== 'undefined' ) {
+ this.onNotification = options.onNotification;
+ }
+
+ if ( typeof options.onAction !== 'undefined' ) {
+ this.onAction = options.onAction;
+ }
+
+ if ( typeof options.permissions !== 'undefined' ) {
+ this.permissions = options.permissions;
+ }
+
+ if ( typeof options.onRemoteFetch !== 'undefined' ) {
+ this.onRemoteFetch = options.onRemoteFetch;
+ }
+
+ if ( this.isLoaded === false ) {
+ this._onRegister = this._onRegister.bind(this);
+ this._onRegistrationError = this._onRegistrationError.bind(this);
+ this._onNotification = this._onNotification.bind(this);
+ this._onRemoteFetch = this._onRemoteFetch.bind(this);
+ this._onAction = this._onAction.bind(this);
+ this.callNative( 'addEventListener', [ 'register', this._onRegister ] );
+ this.callNative( 'addEventListener', [ 'registrationError', this._onRegistrationError ] );
+ this.callNative( 'addEventListener', [ 'notification', this._onNotification ] );
+ this.callNative( 'addEventListener', [ 'localNotification', this._onNotification ] );
+ Platform.OS === 'android' ? this.callNative( 'addEventListener', [ 'action', this._onAction ] ) : null
+ Platform.OS === 'android' ? this.callNative( 'addEventListener', [ 'remoteFetch', this._onRemoteFetch ] ) : null
+
+ this.isLoaded = true;
+ }
+
+ const handlePopInitialNotification = (state) => {
+ if('active' !== state) {
+ return;
+ }
- if ( typeof options.onRemoteFetch !== 'undefined' ) {
- this.onRemoteFetch = options.onRemoteFetch;
- }
+ if (options.popInitialNotification === undefined || options.popInitialNotification === true) {
+ this.popInitialNotification(function(firstNotification) {
+ if(this.isPopInitialNotification) {
+ return;
+ }
+
+ this.isPopInitialNotification = true;
+
+ if (!firstNotification || false === firstNotification.userInteraction) {
+ return;
+ }
+
+ this._onNotification(firstNotification, true);
+ }.bind(this));
+ }
+ }
- if ( this.isLoaded === false ) {
- this._onRegister = this._onRegister.bind(this);
- this._onNotification = this._onNotification.bind(this);
- this._onRemoteFetch = this._onRemoteFetch.bind(this);
- this.callNative( 'addEventListener', [ 'register', this._onRegister ] );
- this.callNative( 'addEventListener', [ 'notification', this._onNotification ] );
- this.callNative( 'addEventListener', [ 'localNotification', this._onNotification ] );
- Platform.OS === 'android' ? this.callNative( 'addEventListener', [ 'remoteFetch', this._onRemoteFetch ] ) : null
+ AppState.addEventListener('change', handlePopInitialNotification.bind(this));
- this.isLoaded = true;
- }
-
- if ( this.hasPoppedInitialNotification === false &&
- ( options.popInitialNotification === undefined || options.popInitialNotification === true ) ) {
- this.popInitialNotification(function(firstNotification) {
- if ( firstNotification !== null ) {
- this._onNotification(firstNotification, true);
- }
- }.bind(this));
- this.hasPoppedInitialNotification = true;
- }
-
- if ( options.requestPermissions !== false ) {
- this._requestPermissions();
- }
+ handlePopInitialNotification(AppState.currentState);
+ if ( options.requestPermissions !== false ) {
+ this._requestPermissions();
+ }
};
/* Unregister */
Notifications.unregister = function() {
- this.callNative( 'removeEventListener', [ 'register', this._onRegister ] )
- this.callNative( 'removeEventListener', [ 'notification', this._onNotification ] )
- this.callNative( 'removeEventListener', [ 'localNotification', this._onNotification ] )
- Platform.OS === 'android' ? this.callNative( 'removeEventListener', [ 'remoteFetch', this._onRemoteFetch ] ) : null
- this.isLoaded = false;
+ this.callNative( 'removeEventListener', [ 'register', this._onRegister ] )
+ this.callNative( 'removeEventListener', [ 'registrationError', this._onRegistrationError ] )
+ this.callNative( 'removeEventListener', [ 'notification', this._onNotification ] )
+ this.callNative( 'removeEventListener', [ 'localNotification', this._onNotification ] )
+ Platform.OS === 'android' ? this.callNative( 'removeEventListener', [ 'action', this._onAction ] ) : null
+ Platform.OS === 'android' ? this.callNative( 'removeEventListener', [ 'remoteFetch', this._onRemoteFetch ] ) : null
+ this.isLoaded = false;
};
/**
* Local Notifications
- * @param {Object} details
- * @param {String} details.title - The title displayed in the notification alert.
- * @param {String} details.message - The message displayed in the notification alert.
- * @param {String} details.ticker - ANDROID ONLY: The ticker displayed in the status bar.
- * @param {Object} details.userInfo - iOS ONLY: The userInfo used in the notification alert.
+ * @param {Object} details
+ * @param {String} details.title - The title displayed in the notification alert.
+ * @param {String} details.message - The message displayed in the notification alert.
+ * @param {String} details.ticker - ANDROID ONLY: The ticker displayed in the status bar.
+ * @param {Object} details.userInfo - iOS ONLY: The userInfo used in the notification alert.
*/
-Notifications.localNotification = function(details) {
- if ( Platform.OS === 'ios' ) {
- // https://developer.apple.com/reference/uikit/uilocalnotification
+Notifications.localNotification = function({...details}) {
+ if ('android' === Platform.os && details && !details.channelId) {
+ console.warn('No channel id passed, notifications may not work.');
+ }
+
+ if (details && typeof details.id === 'number') {
+ if (isNaN(details.id)) {
+ console.warn('NaN value has been passed as id');
+ delete details.id;
+ }
+ else {
+ details.id = '' + details.id;
+ }
+ }
- let soundName = details.soundName ? details.soundName : 'default'; // play sound (and vibrate) as default behaviour
+ if (details.userInfo) {
+ details.userInfo.id = details.userInfo.id || details.id;
+ } else {
+ details.userInfo = {id: details.id};
+ }
- if (details.hasOwnProperty('playSound') && !details.playSound) {
- soundName = ''; // empty string results in no sound (and no vibration)
- }
+ if (Platform.OS === 'ios') {
+ // https://developer.apple.com/reference/uikit/uilocalnotification
- // for valid fields see: https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html
- // alertTitle only valid for apple watch: https://developer.apple.com/library/ios/documentation/iPhone/Reference/UILocalNotification_Class/#//apple_ref/occ/instp/UILocalNotification/alertTitle
-
- this.handler.presentLocalNotification({
- alertTitle: details.title,
- alertBody: details.message,
- alertAction: details.alertAction,
- category: details.category,
- soundName: soundName,
- applicationIconBadgeNumber: details.number,
- userInfo: details.userInfo
- });
- } else {
- if(details && typeof details.id === 'number') {
- details.id = '' + details.id;
+ let soundName = details.soundName ? details.soundName : 'default'; // play sound (and vibrate) as default behaviour
+
+ if (details.hasOwnProperty('playSound') && !details.playSound) {
+ soundName = ''; // empty string results in no sound (and no vibration)
+ }
+
+ // for valid fields see: https://developer.apple.com/library/archive/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/PayloadKeyReference.html
+
+ this.handler.addNotificationRequest({
+ id: (!details.id ? Math.floor(Math.random() * Math.pow(2, 32)).toString() : details.id),
+ title: details.title,
+ body: details.message,
+ badge: details.number,
+ sound: soundName,
+ isSilent: details.playSound === false,
+ category: details.category,
+ userInfo: details.userInfo
+ });
+ } else {
+ if (details && typeof details.number === 'number') {
+ if(isNaN(details.number)) {
+ console.warn('NaN value has been passed as number');
+ delete details.number;
+ }
+ else {
+ details.number = '' + details.number;
+ }
+ }
+
+ if (details && typeof details.shortcutId === 'number') {
+ if(isNaN(details.shortcutId)) {
+ console.warn('NaN value has been passed as shortcutId');
+ delete details.shortcutId;
+ }
+ else {
+ details.shortcutId = '' + details.shortcutId;
+ }
}
- if(details && typeof details.number === 'number') {
- details.number = '' + details.number;
+ if(details && Array.isArray(details.actions)) {
+ details.actions = JSON.stringify(details.actions);
+ }
+
+ if(details.userInfo) {
+ details.userInfo = JSON.stringify(details.userInfo);
}
- this.handler.presentLocalNotification(details);
- }
+ this.handler.presentLocalNotification(details);
+ }
};
/**
* Local Notifications Schedule
- * @param {Object} details (same as localNotification)
- * @param {Date} details.date - The date and time when the system should deliver the notification
+ * @param {Object} details (same as localNotification)
+ * @param {Date} details.date - The date and time when the system should deliver the notification
*/
-Notifications.localNotificationSchedule = function(details) {
- if ( Platform.OS === 'ios' ) {
- let soundName = details.soundName ? details.soundName : 'default'; // play sound (and vibrate) as default behaviour
+Notifications.localNotificationSchedule = function({...details}) {
+ if ('android' === Platform.os && details && !details.channelId) {
+ console.warn('No channel id passed, notifications may not work.');
+ }
+
+ if (details && typeof details.id === 'number') {
+ if(isNaN(details.id)) {
+ console.warn('NaN value has been passed as id');
+ delete details.id;
+ }
+ else {
+ details.id = '' + details.id;
+ }
+ }
- if (details.hasOwnProperty('playSound') && !details.playSound) {
- soundName = ''; // empty string results in no sound (and no vibration)
- }
+ if (details.userInfo) {
+ details.userInfo.id = details.userInfo.id || details.id;
+ } else {
+ details.userInfo = {id: details.id};
+ }
- const iosDetails = {
- fireDate: details.date.toISOString(),
- alertTitle: details.title,
- alertBody: details.message,
- category: details.category,
- soundName: soundName,
- userInfo: details.userInfo,
- repeatInterval: details.repeatType,
- category: details.category,
- };
-
- if(details.number) {
- iosDetails.applicationIconBadgeNumber = parseInt(details.number, 10);
- }
+ if (Platform.OS === 'ios') {
+ let soundName = details.soundName ? details.soundName : 'default'; // play sound (and vibrate) as default behaviour
- // ignore Android only repeatType
- if (!details.repeatType || details.repeatType === 'time') {
- delete iosDetails.repeatInterval;
- }
- this.handler.scheduleLocalNotification(iosDetails);
- } else {
- if(details && typeof details.id === 'number') {
- details.id = '' + details.id;
+ if (details.hasOwnProperty('playSound') && !details.playSound) {
+ soundName = ''; // empty string results in no sound (and no vibration)
+ }
+
+ const iosDetails = {
+ id: (!details.id ? Math.floor(Math.random() * Math.pow(2, 32)).toString() : details.id),
+ fireDate: details.date.toISOString(),
+ title: details.title,
+ body: details.message,
+ sound: soundName,
+ isSilent: details.playSound === false,
+ category: details.category,
+ userInfo: details.userInfo,
+ repeats: (details.repeatType && details.repeatType == "day"),
+ };
+
+ if (details.number) {
+ iosDetails.badge = parseInt(details.number, 10);
}
- if(details && typeof details.number === 'number') {
- details.number = '' + details.number;
+ this.handler.addNotificationRequest(iosDetails);
+ } else {
+ if (details && typeof details.number === 'number') {
+ if (isNaN(details.number)) {
+ console.warn('NaN value has been passed as number');
+ delete details.number;
+ }
+ else {
+ details.number = '' + details.number;
+ }
+ }
+
+ if (details && typeof details.shortcutId === 'number') {
+ if (isNaN(details.shortcutId)) {
+ console.warn('NaN value has been passed as shortcutId');
+ delete details.shortcutId;
+ }
+ else {
+ details.shortcutId = '' + details.shortcutId;
+ }
}
- details.fireDate = details.date.getTime();
- delete details.date;
- // ignore iOS only repeatType
- if (['year'].includes(details.repeatType)) {
- delete details.repeatType;
- }
- this.handler.scheduleLocalNotification(details);
- }
+ if(details && Array.isArray(details.actions)) {
+ details.actions = JSON.stringify(details.actions);
+ }
+
+ if(details.userInfo) {
+ details.userInfo = JSON.stringify(details.userInfo);
+ }
+
+ details.fireDate = details.date.getTime();
+ delete details.date;
+ // ignore iOS only repeatType
+ if (['year'].includes(details.repeatType)) {
+ delete details.repeatType;
+ }
+ this.handler.scheduleLocalNotification(details);
+ }
};
/* Internal Functions */
Notifications._onRegister = function(token) {
- if ( this.onRegister !== false ) {
- this.onRegister({
- token: token,
- os: Platform.OS
- });
- }
+ if ( this.onRegister !== false ) {
+ this.onRegister({
+ token: token,
+ os: Platform.OS
+ });
+ }
+};
+
+Notifications._onRegistrationError = function(err) {
+ if ( this.onRegistrationError !== false ) {
+ this.onRegistrationError(err);
+ }
};
Notifications._onRemoteFetch = function(notificationData) {
- if ( this.onRemoteFetch !== false ) {
- this.onRemoteFetch(notificationData)
- }
+ if ( this.onRemoteFetch !== false ) {
+ this.onRemoteFetch(notificationData)
+ }
};
-Notifications._onNotification = function(data, isFromBackground = null) {
- if ( isFromBackground === null ) {
- isFromBackground = (
- data.foreground === false ||
- AppState.currentState === 'background'
- );
- }
+Notifications._onAction = function({...notification}) {
+ if ( typeof notification.data === 'string' ) {
+ try {
+ notification.data = JSON.parse(notificationData.data);
+ } catch(e) {
+ /* void */
+ }
+ }
- if ( this.onNotification !== false ) {
- if ( Platform.OS === 'ios' ) {
- this.onNotification({
- foreground: ! isFromBackground,
- userInteraction: isFromBackground,
- message: data.getMessage(),
- data: data.getData(),
- badge: data.getBadgeCount(),
- alert: data.getAlert(),
- sound: data.getSound(),
- finish: (res) => data.finish(res)
- });
- } else {
- var notificationData = {
- foreground: ! isFromBackground,
- finish: () => {},
- ...data
- };
-
- if ( typeof notificationData.data === 'string' ) {
- try {
- notificationData.data = JSON.parse(notificationData.data);
- } catch(e) {
- /* void */
- }
- }
+ this.onAction(notification);
+}
- this.onNotification(notificationData);
- }
- }
+Notifications._transformNotificationObject = function(data, isFromBackground = null) {
+ if(!data) {
+ return;
+ }
+
+ if ( isFromBackground === null ) {
+ isFromBackground = (
+ data.foreground === false ||
+ AppState.currentState === 'background' ||
+ AppState.currentState === 'unknown'
+ );
+ }
+
+ let _notification;
+
+ if ( Platform.OS === 'ios' ) {
+ const notifData = data.getData();
+
+ _notification = {
+ id: notifData?.id,
+ foreground: !isFromBackground,
+ userInteraction: notifData?.userInteraction === 1 || false,
+ message: data.getMessage(),
+ data: notifData,
+ badge: data.getBadgeCount(),
+ title: data.getTitle(),
+ soundName: data.getSound(),
+ fireDate: Date.parse(data._fireDate),
+ action: data.getActionIdentifier(),
+ reply_text: data.getUserText(),
+ finish: (res) => data.finish(res)
+ };
+
+ if(isNaN(_notification.fireDate)) {
+ delete _notification.fireDate;
+ }
+
+ } else {
+ _notification = {
+ foreground: !isFromBackground,
+ finish: () => {},
+ ...data,
+ };
+
+ if ( typeof _notification.data === 'string' ) {
+ try {
+ _notification.data = JSON.parse(_notification.data);
+ } catch(e) {
+ /* void */
+ }
+ }
+
+ if ( typeof _notification.userInfo === 'string' ) {
+ try {
+ _notification.userInfo = JSON.parse(_notification.userInfo);
+ } catch(e) {
+ /* void */
+ }
+ }
+
+
+ _notification.data = {
+ ...(typeof _notification.userInfo === 'object' ? _notification.userInfo : {}),
+ ...(typeof _notification.data === 'object' ? _notification.data : {}),
+ };
+
+ delete _notification.userInfo;
+ delete _notification.notificationId;
+ }
+
+ return _notification;
+}
+
+Notifications._onNotification = function(data, initialNotification = false) {
+ if ( this.onNotification !== false ) {
+ let notification = data;
+
+ if(!initialNotification) {
+ notification = this._transformNotificationObject(data);
+ }
+
+ this.onNotification(notification);
+ }
};
/* onResultPermissionResult */
Notifications._onPermissionResult = function() {
- this.isPermissionsRequestPending = false;
+ this.isPermissionsRequestPending = false;
};
// Prevent requestPermissions called twice if ios result is pending
Notifications._requestPermissions = function() {
- if ( Platform.OS === 'ios' ) {
- if ( this.isPermissionsRequestPending === false ) {
- this.isPermissionsRequestPending = true;
- return this.callNative( 'requestPermissions', [ this.permissions ])
- .then(this._onPermissionResult.bind(this))
- .catch(this._onPermissionResult.bind(this));
- }
- } else if (Platform.OS === 'android') {
- return this.callNative( 'requestPermissions', []);
- }
+ if ( Platform.OS === 'ios' ) {
+ if ( this.isPermissionsRequestPending === false ) {
+ this.isPermissionsRequestPending = true;
+ return this.callNative( 'requestPermissions', [ this.permissions ])
+ .then(this._onPermissionResult.bind(this))
+ .catch(this._onPermissionResult.bind(this));
+ }
+ } else if (Platform.OS === 'android') {
+ return this.callNative( 'requestPermissions', []);
+ }
};
// Stock requestPermissions function
Notifications.requestPermissions = function() {
- if ( Platform.OS === 'ios' ) {
- return this.callNative( 'requestPermissions', [ this.permissions ]);
- } else if (Platform.OS === 'android') {
- return this.callNative( 'requestPermissions', []);
- }
+ if ( Platform.OS === 'ios' ) {
+ return this.callNative( 'requestPermissions', [ this.permissions ]);
+ } else if (Platform.OS === 'android') {
+ return this.callNative( 'requestPermissions', []);
+ }
};
/* Fallback functions */
Notifications.subscribeToTopic = function() {
- return this.callNative('subscribeToTopic', arguments);
+ return this.callNative('subscribeToTopic', arguments);
};
Notifications.unsubscribeFromTopic = function () {
- return this.callNative('unsubscribeFromTopic', arguments);
+ return this.callNative('unsubscribeFromTopic', arguments);
};
Notifications.presentLocalNotification = function() {
- return this.callNative('presentLocalNotification', arguments);
+ return this.callNative('presentLocalNotification', arguments);
};
Notifications.scheduleLocalNotification = function() {
- return this.callNative('scheduleLocalNotification', arguments);
+ return this.callNative('scheduleLocalNotification', arguments);
};
-Notifications.cancelLocalNotifications = function() {
- return this.callNative('cancelLocalNotifications', arguments);
+Notifications.cancelLocalNotifications = function(userInfo) {
+ if ( Platform.OS === 'ios' ) {
+ return this.callNative('removePendingNotificationRequests', [[userInfo.id]]);
+ } else {
+ return this.callNative('cancelLocalNotifications', [userInfo]);
+ }
};
Notifications.clearLocalNotification = function() {
@@ -314,51 +481,127 @@ Notifications.clearLocalNotification = function() {
};
Notifications.cancelAllLocalNotifications = function() {
- return this.callNative('cancelAllLocalNotifications', arguments);
+ if ( Platform.OS === 'ios' ) {
+ return this.callNative('removeAllPendingNotificationRequests', arguments);
+ } else if (Platform.OS === 'android') {
+ return this.callNative('cancelAllLocalNotifications', arguments);
+ }
};
Notifications.setApplicationIconBadgeNumber = function() {
- return this.callNative('setApplicationIconBadgeNumber', arguments);
+ return this.callNative('setApplicationIconBadgeNumber', arguments);
};
Notifications.getApplicationIconBadgeNumber = function() {
- return this.callNative('getApplicationIconBadgeNumber', arguments);
+ return this.callNative('getApplicationIconBadgeNumber', arguments);
};
Notifications.popInitialNotification = function(handler) {
- this.callNative('getInitialNotification').then(function(result){
- handler(result);
- });
+ this.callNative('getInitialNotification').then((result) => {
+ handler(
+ this._transformNotificationObject(result, true)
+ );
+ });
};
Notifications.checkPermissions = function() {
- return this.callNative('checkPermissions', arguments);
+ return this.callNative('checkPermissions', arguments);
};
/* Abandon Permissions */
Notifications.abandonPermissions = function() {
- return this.callNative('abandonPermissions', arguments);
-}
-
-Notifications.registerNotificationActions = function() {
- return this.callNative('registerNotificationActions', arguments)
+ return this.callNative('abandonPermissions', arguments);
}
Notifications.clearAllNotifications = function() {
- // Only available for Android
- return this.callNative('clearAllNotifications', arguments)
+ // Only available for Android
+ return this.callNative('clearAllNotifications', arguments)
}
Notifications.removeAllDeliveredNotifications = function() {
- return this.callNative('removeAllDeliveredNotifications', arguments);
+ return this.callNative('removeAllDeliveredNotifications', arguments);
}
Notifications.getDeliveredNotifications = function() {
- return this.callNative('getDeliveredNotifications', arguments);
+ return this.callNative('getDeliveredNotifications', arguments);
+}
+
+Notifications.getScheduledLocalNotifications = function(callback) {
+ const mapNotifications = (notifications) => {
+ let mappedNotifications = [];
+ if(notifications?.length > 0) {
+ if(Platform.OS === 'ios'){
+ mappedNotifications = notifications.map(notif => {
+ return ({
+ soundName: notif?.sound,
+ id: notif.id,
+ date: (notif.date ? new Date(notif.date) : null),
+ number: notif?.badge,
+ message: notif?.body,
+ title: notif?.title,
+ data: notif?.userInfo
+ })
+ })
+ } else if(Platform.OS === 'android') {
+ mappedNotifications = notifications.map(notif => {
+
+ try {
+ notif.data = JSON.parse(notif.data);
+ } catch(e) { }
+
+ return ({
+ soundName: notif.soundName,
+ repeatInterval: notif.repeatInterval,
+ id: notif.id,
+ date: new Date(notif.date),
+ number: notif.number,
+ message: notif.message,
+ title: notif.title,
+ data: notif.data,
+ })
+ })
+ }
+ }
+ callback(mappedNotifications);
+ }
+
+ if(Platform.OS === 'ios'){
+ return this.callNative('getPendingNotificationRequests', [mapNotifications]);
+ } else {
+ return this.callNative('getScheduledLocalNotifications', [mapNotifications]);
+ }
}
Notifications.removeDeliveredNotifications = function() {
- return this.callNative('removeDeliveredNotifications', arguments);
+ return this.callNative('removeDeliveredNotifications', arguments);
+}
+
+Notifications.invokeApp = function() {
+ return this.callNative('invokeApp', arguments);
+};
+
+Notifications.getChannels = function() {
+ return this.callNative('getChannels', arguments);
+};
+
+Notifications.channelExists = function() {
+ return this.callNative('channelExists', arguments);
+};
+
+Notifications.createChannel = function() {
+ return this.callNative('createChannel', arguments);
+};
+
+Notifications.channelBlocked = function() {
+ return this.callNative('channelBlocked', arguments);
+};
+
+Notifications.deleteChannel = function() {
+ return this.callNative('deleteChannel', arguments);
+};
+
+Notifications.setNotificationCategories = function() {
+ return this.callNative('setNotificationCategories', arguments);
}
module.exports = Notifications;
diff --git a/package.json b/package.json
index b1b84c444..aa3f039a1 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "react-native-push-notification",
- "version": "3.4.0",
+ "version": "7.2.3",
"description": "React Native Local and Remote Notifications",
"main": "index.js",
"scripts": {
@@ -14,7 +14,7 @@
"notifications",
"push",
"apns",
- "gcm"
+ "firebase"
],
"bugs": {
"url": "https://github.com/zo0r/react-native-push-notification/issues"
@@ -23,10 +23,8 @@
"type": "git",
"url": "git+ssh://git@github.com:zo0r/react-native-push-notification.git"
},
- "dependencies": {
- "@react-native-community/push-notification-ios": "^1.2.0"
- },
"peerDependencies": {
+ "@react-native-community/push-notification-ios": "^1.8.0",
"react-native": ">=0.33"
},
"author": "zo0r ",
diff --git a/submitting-a-pull-request.md b/submitting-a-pull-request.md
index 53e46e7ce..8611b5230 100644
--- a/submitting-a-pull-request.md
+++ b/submitting-a-pull-request.md
@@ -23,7 +23,7 @@ If you are testing someones PR, please provide the following feedback:
* are you happy that the change in behaviour works as described, and you have tested the change on all affected operating systems?
* in your opinion, is the code written _sensibly_?
* is it clean and tidy?
- * there are and unnecessary changes?
+ * there are any unnecessary changes?
* is it documented appropriately?
-
\ No newline at end of file
+
diff --git a/trouble-shooting.md b/trouble-shooting.md
index 52a764e51..e2f3ebe02 100644
--- a/trouble-shooting.md
+++ b/trouble-shooting.md
@@ -7,6 +7,31 @@ Known bugs and issues:
* (Android) Tapping an alert in the notification centre will sometimes not result in `onNotification` being called [issue 281](https://github.com/zo0r/react-native-push-notification/issues/281)
* (Android) Not all local notification features are supported yet (PRs welcome)
* (iOS) The OS can penalise your app for not calling the completion handler and will stop (or delay) sending notifications to your app. This will be supported from RN-0.38 [PR 227](https://github.com/zo0r/react-native-push-notification/pull/277)
+ * (Android and iOS) Don't use a string to get the date for schedule a local notification, it only works with remote debugger enabled, [explanation](https://stackoverflow.com/a/41881765/8519917).
+
+
+ ```javascript
+ // It doesn't works with the javascript engine used by React Native
+ const date = new Date("10-10-2020 12:30");
+ ```
+ A good practice to get valid date could be:
+
+ ```javascript
+ // Get date to schedule a local notification today at 12:30:00
+ const hour = 12;
+ const minute = 30;
+ const second = 0;
+
+ const now = new Date();
+ const date = new Date(
+ now.getFullYear(),
+ now.getMonth(),
+ now.getDate(),
+ hour,
+ minute,
+ second
+ );
+ ```
# Android tips
@@ -25,7 +50,7 @@ Known bugs and issues:
# About notifications...
-There are a number of different types of notifications, and they have subtly different behaviours. There are essentially 4 types, let's call them _local notifications_ (1), _noisy remote push notifications_ (2), _silent remote push notifications_ (3) and _mixed remote push notifications_ (4).
+There are a number of different types of notifications, and they have subtly different behaviours. There are essentially 4 types, let's call them _local notifications_ (1), _noisy remote push notifications_ (2) and _silent remote push notifications_ (3).
## 1. local notifications
@@ -178,50 +203,6 @@ The crucial bit of an iOS silent notification is presence of the `"content-avail
After you have processed the notification you must call isn't `finish` method (as of RN 0.38).
-## 4. _mixed_ remote push notifications
-
-_Mixed_ remote push notifications are both delivered to your app AND to the notification center.
-
-#### Android _mixed_ remote push notifications
-
-Android doesn't directly support mixed notifications. If you try to combine the above approaches you will see a _noisy_ notification but it will not be delivered to your app. This library does however provide a basic work-around. By adding `message` field to a _silent_ notification the library will synthesize a local notification as well as deliver a _silent_ notification to your app. Something like this:
-
-```json
-{
- "to": "",
- "time_to_live": 86400,
- "collapse_key": "new_message",
- "delay_while_idle": false,
- "data": {
- "title": "title",
- "message": "this is a mixed test 14:03:29.676",
- "your-key": "your-value"
- }
-}
-```
-
-The resulting local notification will include the message as well as a few other (optional) fields: _title_, _sound_ and _colour_
-
-#### iOS _mixed_ remote push notifications
-
-Just combine the above _silent_ and _noisy_ notifications and send to APNS:
-
-```json
-{
- "aps": {
- "alert": {
- "body": "body 16:03:49.889",
- "title": "title"
- },
- "badge": 1,
- "sound": "default"
- },
- "payload": "{\"your-key\":\"your-value\"}"
-}
-```
-
-It will be delivered to both the notification centre **and** your app if the app is running in the background, but only to your app if it's running in the foreground.
-
#### Some useful links
* http://www.fantageek.com/blog/2016/04/15/push-notification-in-practice/