├── .gitignore
├── LICENSE
├── README.md
├── android
├── .classpath
├── .project
├── build.gradle
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── com
│ └── staltz
│ └── reactnativeandroidlocalnotification
│ ├── Notification.java
│ ├── NotificationAttributes.java
│ ├── NotificationEventReceiver.java
│ ├── NotificationModule.java
│ ├── NotificationPackage.java
│ ├── NotificationPublisher.java
│ ├── RCTNotificationManager.java
│ └── SystemBootEventReceiver.java
├── index.js
└── package.json
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | local.properties
4 | .idea/
5 | .DS_Store
6 | build/
7 | /captures
8 | .externalNativeBuild
9 | /android/.settings/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Original Work Copyright (c) 2016 Layman
4 | Modified Work Copyright (c) 2018, Staltz.com
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Local Android Notifications
2 |
3 | Forked from [https://github.com/anysome/react-native-pure-notification](https://github.com/anysome/react-native-pure-notification) to update its dependencies.
4 |
5 | ---
6 |
7 | ```js
8 | import React, { DeviceEventEmitter } from 'react-native';
9 | import Notification from 'react-native-android-local-notification';
10 |
11 | // Send a simple notification
12 | Notification.create({ subject: 'Hey', message: 'Yo! Hello world.' });
13 |
14 | // Listen to notification-clicking events
15 | Notification.addListener('press', function(e) {
16 | console.log(e);
17 | });
18 |
19 | // Custom payload for notifications
20 | Notification.create({
21 | subject: 'Notification With Payload',
22 | message: 'This is a notification that contains custom payload.',
23 | payload: { number: 1, what: true, someAnswer: '42' }
24 | });
25 |
26 | // Receive the payload on notification events
27 | Notification.addListener('press', function(e) {
28 | console.log(e.payload); // => { number: 1, what: true, someAnswer: '42' }
29 | });
30 |
31 | // Customize notification
32 | Notification.create({
33 | subject: 'Notification With Custom Icon',
34 | message: 'This is a notification with a specified icon.',
35 | smallIcon: 'ic_alert'
36 | });
37 |
38 | // Scheduled notifications
39 | Notification.create({
40 | subject: 'Scheduled Notification',
41 | message: 'This notification will show on every Friday morning at 8:30 AM, starts at 2015/9/9 and end after 10 times.',
42 | sendAt: new Date(2015, 9, 9, 8, 30),
43 | repeatEvery: 'week',
44 | count: 10
45 | });
46 | ```
47 |
48 | ## Installation
49 |
50 | - For React Native 0.60 or higher, use this package's version 3.0.0 or higher
51 | - For React Native 0.59 or lower, use this package's version 2.0.0 or lower
52 |
53 | - Run `npm install react-native-android-local-notification --save` to install using npm.
54 |
55 | - Add the following two lines to `android/settings.gradle`:
56 |
57 | ```gradle
58 | include ':react-native-android-local-notification'
59 | project(':react-native-android-local-notification').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-android-local-notification/android')
60 | ```
61 |
62 | - Edit `android/app/build.gradle` and add the annoated lines as below:
63 |
64 | ```gradle
65 | ...
66 |
67 | dependencies {
68 | compile fileTree(dir: "libs", include: ["*.jar"])
69 | compile "com.android.support:appcompat-v7:23.0.1"
70 | compile "com.facebook.react:react-native:0.16.+"
71 | compile project(':react-native-android-local-notification') // <- Add this line
72 | }
73 | ```
74 |
75 | - Edit `android/app/src/main/AndroidManifest.xml` and add the annoated lines as below:
76 |
77 | ```xml
78 |
80 |
81 |
82 |
83 |
84 |
85 |
90 |
91 | ...
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 | ```
105 |
106 | > The `RECEIVE_BOOT_COMPLETED` permission is used to re-register all scheduled notifications after reboot.
107 | > Requesting `VIBRATE` permission is required if you want to make the device vibrate while sending notifications.
108 |
109 | - Edit `MainActivity.java` (usually at `android/app/src/main/java/com//MainActivity.java`) and add the annoated lines as below:
110 |
111 | ```java
112 | ...
113 |
114 | import android.content.Intent;
115 | import android.os.Bundle;
116 |
117 | import com.facebook.react.ReactActivity;
118 | import com.facebook.react.ReactPackage;
119 | import com.facebook.react.shell.MainReactPackage;
120 |
121 | import com.staltz.reactnativeandroidlocalnotification.NotificationPackage; // <- Add this line
122 |
123 | public class MainApplication extends Application implements ReactApplication {
124 |
125 | ...
126 |
127 | @Override
128 | protected List getPackages() {
129 | return Arrays.asList(
130 | ...
131 | new NotificationPackage() // <- Add this line
132 | );
133 | }
134 |
135 | ...
136 | ```
137 |
138 | ## Usage
139 |
140 | ### Creating Notifications
141 |
142 | Just do:
143 |
144 | ```js
145 | Notification.create({
146 | id: 1337,
147 | subject: 'Notification With Payload',
148 | message: 'This is a notification that contains custom payload.',
149 | smallIcon: 'ic_launcher',
150 | autoClear: true,
151 | payload: { number: 1, what: true, someAnswer: '42' }
152 | });
153 | ```
154 |
155 | > All functions of this module will return [promise](https://www.promisejs.org/)s with the notification object handing in. So you can get the data of the notification and do anything that is needed, like this:
156 | >
157 | > ```js
158 | > Notification.create({ message: 'Testing.' }).then(function(notification) {
159 | > console.log(notification);
160 | > console.log(notification.id);
161 | > });
162 | > ```
163 |
164 | All available options on a notification are listed below:
165 |
166 | #### Basic
167 |
168 | **id (`number`)**
169 | The unique ID of this notification. It will be randomly chosen if not specified.
170 |
171 | **subject (`string`)**
172 | The notification subject. Defaults to the application name on Android.
173 |
174 | **message (`string`)**
175 | The message showen in the notification.
176 |
177 | **action (`string`)**
178 | An action name that can be used to determine what to do when this notification is clicked. Defaults to `DEFAULT`.
179 |
180 | **payload (`object`)**
181 | A custom payload object. It can be retrieved on events of this notification. Defaults to `{}`.
182 |
183 |
184 | #### Scheduling
185 |
186 | **delay (`number`)**
187 | Milliseconds to delay before showing this notification after it is created. Useful when creating countdown alarms, reminders, etc. Note that it cannot be used with `sendAt`.
188 |
189 | **sendAt (`Date`)**
190 | Schedule this notification to show on a specified time. Note that it cannot be used with `delay`.
191 |
192 | **repeatEvery (`string` or `number`)**
193 | Must use with `sendAt`. Schedule this notification to repeat. Can be `minute`, `hour`, `halfDay`, `day`, `week`, `month`, `year` or a number of time in milliseconds.
194 |
195 | **repeatCount (`number`)**
196 | Must use with `sendAt` and `repeatEvery`. End repeating this notification after n times. Note that it cannot be used with `endAt`.
197 |
198 | **endAt (`Date`)**
199 | Must use with `sendAt` and `repeatEvery`. End repeating this notification after a specified time. Note that it cannot be used with `repeatCount`.
200 |
201 |
202 | > Some Samples of Scheduled Notifications
203 | >
204 | > ```js
205 | > Notification.create({
206 | > subject: 'Scheduled Notification',
207 | > message: 'This notification will show on every Friday morning at 8:30 AM, starts at 2015/9/9 and end after 10 times.',
208 | > sendAt: new Date(2015, 9, 9, 8, 30),
209 | > repeatEvery: 'week',
210 | > repeatCount: 10
211 | > });
212 | > ```
213 | >
214 | > ```js
215 | > Notification.create({
216 | > subject: 'Scheduled Notification',
217 | > message: 'This notification will show on 2015/9/9 morning at 8:30 AM, and repeat for 10 times every minute.',
218 | > sendAt: new Date(2015, 9, 9, 8, 30),
219 | > repeatEvery: 60000,
220 | > repeatCount: 10
221 | > });
222 | > ```
223 | >
224 | > ```js
225 | > Notification.create({
226 | > subject: 'Delayed Notification',
227 | > message: 'This notification will show after 10 seconds, even the app has been stoped.',
228 | > delay: 10000
229 | > });
230 | > ```
231 |
232 | #### Customization
233 |
234 | **priority (`number`)**
235 | Priority of this notification, can be `-2`, `-1`, `0`, `1`, `2`. When this is set to `1` or `2`, heads-up notification will be more likely to show on Android 5+. Defaults to `1`.
236 |
237 | **smallIcon (`string`)**
238 | The icon (file name) to show. This icon must be placed in the project's `android/app/src/main/res/mipmap-*` folder. Defaults to `ic_launcher`.
239 |
240 | **largeIcon (`string`)**
241 | Not yet implemented.
242 |
243 | **sound (`string`)**
244 | Set the sound to play. Defaults to `default` as using the default notification sound, or set this to `null` to disable the sound. Other options are not yet implemented.
245 |
246 | **vibrate (`string`)**
247 | Set the vibration pattern to use. Defaults to `default` as using the default notification vibrate, or set this to `null` to disable the vibrate. Other options are not yet implemented.
248 |
249 | **lights (`string`)**
250 | Set the desired color for the indicator LED on the device. Defaults to `default` as using the default notification lights, or set this to `null` to disable the lights. Other options are not yet implemented.
251 |
252 | **autoClear (`boolean`)**
253 | Clear this notification automatically after the user clicks on it. Defaults to `true`.
254 |
255 | **onlyAlertOnce (`boolean`)**
256 | Do not let the sound, vibrate and ticker to be played if the notification is already showing.
257 |
258 | **tickerText (`string`)**
259 | Set the text to show on ticker. Defaults to `: `. Set this to `null` to disable ticker.
260 |
261 | **when (`Date`)**
262 | Add a timestamp pertaining to the notification (usually the time the event occurred).
263 |
264 | **bigText (`string`)**
265 | Set the text to be shown when the user expand the notification.
266 |
267 | **bigStyleImageBase64 (`string`)**
268 | Set the image in base64 to be shown when the user expand the notification. if bigText is not null, it have priority over bigStyleImageBase64.
269 |
270 | **bigStyleUrlImage (`string`)**
271 | Set URL of a image. Geting it by open a stream connection and it be shown when the user expand the notification.. if bigText is not null, it have priority over bigStyleUrlImage
272 |
273 | **subText (`string`)**
274 | Set the third line of text in the platform notification template. Note that it cannot be used with `progress`.
275 |
276 | **progress (`number`)**
277 | Set the progress this notification represents, range: `0.0` ~ `1.0`. Set this to a number lower then zero to get an indeterminate progress. Note that it cannot be used with `subText`.
278 |
279 | **color (`string`)**
280 | Color to be applied by the standard Style templates when presenting this notification.
281 |
282 | **number (`number`)**
283 | Set a number on the notification.
284 |
285 | **private (`boolean`)**
286 | Not yet implemented.
287 |
288 | **ongoing (`boolean`)**
289 | Not yet implemented.
290 |
291 | **category (`string`)**
292 | Set the notification category, e.g.: `alarm`, `call`, `email`, `event`, `progress`, `reminder`, `social`. It may be used by the Android system for ranking and filtering.
293 |
294 | **localOnly (`boolean`)**
295 | Set whether or not this notification should not bridge to other devices.
296 |
297 | ### Handle Notification Click Event
298 |
299 | Register a listener on `sysNotificationClick` events to handle notification clicking:
300 |
301 | ```js
302 | Notification.addListener('press', function(e) {
303 | console.log(e);
304 | });
305 | ```
306 |
307 | The action and payload of the notification can be retrieved on these events:
308 |
309 | ```js
310 | Notification.send({ message: 'Message', action: 'ACTION_NAME', payload: { data: 'Anything' } });
311 | ```
312 |
313 | ```js
314 | Notification.addListener('press', function(e) {
315 | switch (e.action) {
316 | case 'ACTION_NAME':
317 | console.log(`Action Triggered! Data: ${e.payload.data}`);
318 | break;
319 |
320 | case 'ANOTHER_ACTION_NAME':
321 | console.log(`Another Action Triggered! Data: ${e.payload.data}`);
322 | break;
323 | }
324 | });
325 | ```
326 |
327 | Once you no longer need to listen to `sysNotificationClick` events de-register the listener functions with:
328 |
329 | ```js
330 | Notification.removeAllListeners('press');
331 | ```
332 |
333 | ### Manage Scheduled Notifications
334 |
335 | Sometimes you'll need to get the scheduled notifications (which has `delay` or `sendAt` set up) that you had created before. You can use `Notification.getIDs()` to retrieve an array of IDs of available (i.e. will be send in the future) scheduled notifications.
336 |
337 | ```js
338 | Notification.getIDs().then(function(ids) {
339 | console.log(ids); // Array of ids
340 | });
341 | ```
342 |
343 | and use `Notification.find(notificationID)` to get data of an notification.
344 |
345 | ```js
346 | Notification.find(notificationID).then(function(notification) {
347 | console.log(notification);
348 | });
349 | ```
350 |
351 | or just cancel it with `Notification.delete(notificationID)`:
352 |
353 | ```js
354 | Notification.delete(notificationID);
355 | ```
356 |
357 | Want to cancel all scheduled notifications set by your app? Sure:
358 |
359 | ```js
360 | Notification.deleteAll();
361 | ```
362 |
363 | > To update a scheduled notification, just use `Notification.create()` with the same id.
364 |
365 | ### Clearing Notifications
366 |
367 | When you want to clear a notification from the system statusbar, just use:
368 |
369 | ```js
370 | Notification.clearAll();
371 | ```
372 |
373 | or:
374 |
375 | ```js
376 | Notification.clear(notificationID);
377 | ```
378 |
--------------------------------------------------------------------------------
/android/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/android/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | android
4 | Project android created by Buildship.
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.buildship.core.gradleprojectbuilder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javanature
21 | org.eclipse.buildship.core.gradleprojectnature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | import groovy.json.JsonSlurper
2 |
3 | def computeVersionName() {
4 | // dynamically retrieve version from package.json
5 | def slurper = new JsonSlurper()
6 | def json = slurper.parse(file('../package.json'), "utf-8")
7 | return json.version
8 | }
9 |
10 | buildscript {
11 | repositories {
12 | google()
13 | jcenter()
14 | }
15 |
16 | dependencies {
17 | classpath 'com.android.tools.build:gradle:3.4.0'
18 | }
19 | }
20 |
21 | apply plugin: 'com.android.library'
22 |
23 | def safeExtGet(prop, fallback) {
24 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
25 | }
26 |
27 | def DEFAULT_COMPILE_SDK_VERSION = 23
28 | def DEFAULT_MIN_SDK_VERSION = 16
29 | def DEFAULT_SUPPORT_LIB_VERSION = '26.1.0'
30 | def DEFAULT_TARGET_SDK_VERSION = 23
31 |
32 | android {
33 | compileSdkVersion safeExtGet('compileSdkVersion', DEFAULT_COMPILE_SDK_VERSION)
34 |
35 | defaultConfig {
36 | minSdkVersion safeExtGet('minSdkVersion', DEFAULT_MIN_SDK_VERSION)
37 | targetSdkVersion safeExtGet('targetSdkVersion', DEFAULT_TARGET_SDK_VERSION)
38 | versionCode 1
39 | versionName computeVersionName()
40 | }
41 | lintOptions {
42 | abortOnError false
43 | }
44 | }
45 |
46 | repositories {
47 | mavenCentral()
48 | }
49 |
50 | dependencies {
51 | implementation 'androidx.appcompat:appcompat:1.0.0'
52 | implementation 'com.google.code.gson:gson:+'
53 | implementation 'com.facebook.react:react-native:+'
54 | }
55 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/staltz/react-native-android-local-notification/02e6064012e57b1eb958889868f73094ee1fff6a/android/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/android/src/main/java/com/staltz/reactnativeandroidlocalnotification/Notification.java:
--------------------------------------------------------------------------------
1 | package com.staltz.reactnativeandroidlocalnotification;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.BitmapFactory;
5 | import android.os.Build;
6 | import android.os.SystemClock;
7 | import android.app.PendingIntent;
8 | import android.app.AlarmManager;
9 | import android.app.NotificationManager;
10 | import android.content.Context;
11 | import android.content.Intent;
12 | import android.content.SharedPreferences;
13 | import androidx.annotation.Nullable;
14 | import android.net.Uri;
15 |
16 | import java.lang.System;
17 | import java.net.URL;
18 |
19 | import com.google.gson.Gson;
20 |
21 | import android.util.Base64;
22 | import androidx.core.app.NotificationCompat;
23 | import android.text.Html;
24 | import android.util.Log;
25 | import android.graphics.Color;
26 |
27 | /**
28 | * An object-oriented Wrapper class around the system notification class.
29 | *
30 | * Each instance is an representation of a single, or a set of scheduled
31 | * notifications. It handles operations like showing, canceling and clearing.
32 | */
33 | public class Notification {
34 | private Context context;
35 | private int id;
36 | private NotificationAttributes attributes;
37 |
38 | /**
39 | * Constructor.
40 | */
41 | public Notification(Context context, int id, @Nullable NotificationAttributes attributes) {
42 | this.context = context;
43 | this.id = id;
44 | this.attributes = attributes;
45 | }
46 |
47 | /**
48 | * Public context getter.
49 | */
50 | public Context getContext() {
51 | return context;
52 | }
53 |
54 | /**
55 | * Public attributes getter.
56 | */
57 | public NotificationAttributes getAttributes() {
58 | return attributes;
59 | }
60 |
61 | /**
62 | * Create the notification, show it now or set the schedule.
63 | */
64 | public Notification create() {
65 | setAlarmAndSaveOrShow();
66 |
67 | Log.i("ReactSystemNotification", "Notification Created: " + id);
68 |
69 | return this;
70 | }
71 |
72 | /**
73 | * Update the notification, resets its schedule.
74 | */
75 | public Notification update(NotificationAttributes notificationAttributes) {
76 | delete();
77 | attributes = notificationAttributes;
78 | setAlarmAndSaveOrShow();
79 |
80 | return this;
81 | }
82 |
83 | /**
84 | * Clear the notification from the status bar.
85 | */
86 | public Notification clear() {
87 | getSysNotificationManager().cancel(id);
88 |
89 | Log.i("ReactSystemNotification", "Notification Cleared: " + id);
90 |
91 | return this;
92 | }
93 |
94 | /**
95 | * Cancel the notification.
96 | */
97 | public Notification delete() {
98 | getSysNotificationManager().cancel(id);
99 |
100 | if (attributes.delayed || attributes.scheduled) {
101 | cancelAlarm();
102 | }
103 |
104 | deleteFromPreferences();
105 |
106 | Log.i("ReactSystemNotification", "Notification Deleted: " + id);
107 |
108 | return this;
109 | }
110 |
111 | /**
112 | * Build the notification.
113 | */
114 | public android.app.Notification build() {
115 | androidx.core.app.NotificationCompat.Builder notificationBuilder = new androidx.core.app.NotificationCompat.Builder(
116 | context);
117 |
118 | notificationBuilder.setContentTitle(attributes.subject).setContentText(attributes.message)
119 | .setSmallIcon(
120 | context.getResources().getIdentifier(attributes.smallIcon, "mipmap", context.getPackageName()))
121 | .setAutoCancel(attributes.autoClear).setContentIntent(getContentIntent());
122 |
123 | if (attributes.priority != null) {
124 | notificationBuilder.setPriority(attributes.priority);
125 | }
126 |
127 | if (attributes.largeIcon != null) {
128 | int largeIconId = context.getResources().getIdentifier(attributes.largeIcon, "drawable",
129 | context.getPackageName());
130 | Bitmap largeIcon = BitmapFactory.decodeResource(context.getResources(), largeIconId);
131 | notificationBuilder.setLargeIcon(largeIcon);
132 | }
133 |
134 | if (attributes.group != null) {
135 | notificationBuilder.setGroup(attributes.group);
136 | notificationBuilder.setGroupSummary(true);
137 | }
138 |
139 | if (attributes.inboxStyle) {
140 |
141 | androidx.core.app.NotificationCompat.InboxStyle inboxStyle = new androidx.core.app.NotificationCompat.InboxStyle();
142 |
143 | if (attributes.inboxStyleBigContentTitle != null) {
144 | inboxStyle.setBigContentTitle(attributes.inboxStyleBigContentTitle);
145 | }
146 | if (attributes.inboxStyleSummaryText != null) {
147 | inboxStyle.setSummaryText(attributes.inboxStyleSummaryText);
148 | }
149 | if (attributes.inboxStyleLines != null) {
150 | for (int i = 0; i < attributes.inboxStyleLines.size(); i++) {
151 | inboxStyle.addLine(Html.fromHtml(attributes.inboxStyleLines.get(i)));
152 | }
153 | }
154 | notificationBuilder.setStyle(inboxStyle);
155 |
156 | Log.i("ReactSystemNotification", "set inbox style!!");
157 |
158 | } else {
159 |
160 | int defaults = 0;
161 | if ("default".equals(attributes.sound)) {
162 | defaults = defaults | android.app.Notification.DEFAULT_SOUND;
163 | }
164 | if ("default".equals(attributes.vibrate)) {
165 | defaults = defaults | android.app.Notification.DEFAULT_VIBRATE;
166 | }
167 | if ("default".equals(attributes.lights)) {
168 | defaults = defaults | android.app.Notification.DEFAULT_LIGHTS;
169 | }
170 | notificationBuilder.setDefaults(defaults);
171 |
172 | }
173 |
174 | if (attributes.onlyAlertOnce != null) {
175 | notificationBuilder.setOnlyAlertOnce(attributes.onlyAlertOnce);
176 | }
177 |
178 | if (attributes.tickerText != null) {
179 | notificationBuilder.setTicker(attributes.tickerText);
180 | }
181 |
182 | if (attributes.when != null) {
183 | notificationBuilder.setWhen(attributes.when);
184 | notificationBuilder.setShowWhen(true);
185 | }
186 |
187 | // if bigText is not null, it have priority over bigStyleImageBase64
188 | if (attributes.bigText != null) {
189 | notificationBuilder
190 | .setStyle(new androidx.core.app.NotificationCompat.BigTextStyle().bigText(attributes.bigText));
191 | } else if (attributes.bigStyleUrlImage != null && !attributes.bigStyleUrlImage.equals("")) {
192 |
193 | Bitmap bigPicture = null;
194 |
195 | try {
196 |
197 | Log.i("ReactSystemNotification", "start to get image from URL : " + attributes.bigStyleUrlImage);
198 | URL url = new URL(attributes.bigStyleUrlImage);
199 | bigPicture = BitmapFactory.decodeStream(url.openStream());
200 | Log.i("ReactSystemNotification", "finishing to get image from URL");
201 |
202 | } catch (Exception e) {
203 | Log.e("ReactSystemNotification", "Error when getting image from URL" + e.getStackTrace());
204 | }
205 |
206 | if (bigPicture != null) {
207 | notificationBuilder.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(bigPicture));
208 | }
209 | } else if (attributes.bigStyleImageBase64 != null) {
210 |
211 | Bitmap bigPicture = null;
212 |
213 | try {
214 |
215 | Log.i("ReactSystemNotification", "start to convert bigStyleImageBase64 to bitmap");
216 | // Convert base64 image to Bitmap
217 | byte[] bitmapAsBytes = Base64.decode(attributes.bigStyleImageBase64.getBytes(), Base64.DEFAULT);
218 | bigPicture = BitmapFactory.decodeByteArray(bitmapAsBytes, 0, bitmapAsBytes.length);
219 | Log.i("ReactSystemNotification", "finished to convert bigStyleImageBase64 to bitmap");
220 |
221 | } catch (Exception e) {
222 | Log.e("ReactSystemNotification", "Error when converting base 64 to Bitmap" + e.getStackTrace());
223 | }
224 |
225 | if (bigPicture != null) {
226 | notificationBuilder.setStyle(new NotificationCompat.BigPictureStyle().bigPicture(bigPicture));
227 | }
228 | }
229 |
230 | if (attributes.color != null) {
231 | notificationBuilder.setColor(Color.parseColor(attributes.color));
232 | }
233 |
234 | if (attributes.subText != null) {
235 | notificationBuilder.setSubText(attributes.subText);
236 | }
237 |
238 | if (attributes.progress != null) {
239 | if (attributes.progress < 0 || attributes.progress > 1000) {
240 | notificationBuilder.setProgress(1000, 100, true);
241 | } else {
242 | notificationBuilder.setProgress(1000, attributes.progress, false);
243 | }
244 | }
245 |
246 | if (attributes.number != null) {
247 | notificationBuilder.setNumber(attributes.number);
248 | }
249 |
250 | if (attributes.localOnly != null) {
251 | notificationBuilder.setLocalOnly(attributes.localOnly);
252 | }
253 |
254 | if (attributes.sound != null) {
255 | notificationBuilder.setSound(Uri.parse(attributes.sound));
256 | }
257 |
258 | return notificationBuilder.build();
259 | }
260 |
261 | /**
262 | * Show the notification now.
263 | */
264 | public void show() {
265 | getSysNotificationManager().notify(id, build());
266 |
267 | Log.i("ReactSystemNotification", "Notification Show: " + id);
268 | }
269 |
270 | /**
271 | * Setup alarm or show the notification.
272 | */
273 | public void setAlarmAndSaveOrShow() {
274 | if (attributes.delayed) {
275 | setDelay();
276 | saveAttributesToPreferences();
277 |
278 | } else if (attributes.scheduled) {
279 | setSchedule();
280 | saveAttributesToPreferences();
281 |
282 | } else {
283 | show();
284 | }
285 | }
286 |
287 | /**
288 | * Schedule the delayed notification.
289 | */
290 | public void setDelay() {
291 | PendingIntent pendingIntent = getScheduleNotificationIntent();
292 |
293 | long futureInMillis = SystemClock.elapsedRealtime() + attributes.delay;
294 | getAlarmManager().set(AlarmManager.ELAPSED_REALTIME_WAKEUP, futureInMillis, pendingIntent);
295 |
296 | Log.i("ReactSystemNotification",
297 | "Notification Delay Alarm Set: " + id + ", Repeat Type: " + attributes.repeatType + ", Current Time: "
298 | + System.currentTimeMillis() + ", Delay: " + attributes.delay);
299 | }
300 |
301 | /**
302 | * Schedule the notification.
303 | */
304 | public void setSchedule() {
305 | PendingIntent pendingIntent = getScheduleNotificationIntent();
306 |
307 | if (attributes.repeatType == null) {
308 | getAlarmManager().set(AlarmManager.RTC_WAKEUP, attributes.sendAt, pendingIntent);
309 | Log.i("ReactSystemNotification", "Set One-Time Alarm: " + id);
310 |
311 | } else {
312 | switch (attributes.repeatType) {
313 | case "time":
314 | getAlarmManager().setRepeating(AlarmManager.RTC_WAKEUP, attributes.sendAt, attributes.repeatTime,
315 | pendingIntent);
316 | Log.i("ReactSystemNotification", "Set " + attributes.repeatTime + "ms Alarm: " + id);
317 | break;
318 |
319 | case "minute":
320 | getAlarmManager().setRepeating(AlarmManager.RTC_WAKEUP, attributes.sendAt, 60000, pendingIntent);
321 | Log.i("ReactSystemNotification", "Set Minute Alarm: " + id);
322 | break;
323 |
324 | case "hour":
325 | getAlarmManager().setRepeating(AlarmManager.RTC_WAKEUP, attributes.sendAt, AlarmManager.INTERVAL_HOUR,
326 | pendingIntent);
327 | Log.i("ReactSystemNotification", "Set Hour Alarm: " + id);
328 | break;
329 |
330 | case "halfDay":
331 | getAlarmManager().setRepeating(AlarmManager.RTC_WAKEUP, attributes.sendAt,
332 | AlarmManager.INTERVAL_HALF_DAY, pendingIntent);
333 | Log.i("ReactSystemNotification", "Set Half-Day Alarm: " + id);
334 | break;
335 |
336 | case "day":
337 | case "week":
338 | case "month":
339 | case "year":
340 | getAlarmManager().setRepeating(AlarmManager.RTC_WAKEUP, attributes.sendAt, AlarmManager.INTERVAL_DAY,
341 | pendingIntent);
342 | Log.i("ReactSystemNotification", "Set Day Alarm: " + id + ", Type: " + attributes.repeatType);
343 | break;
344 |
345 | default:
346 | getAlarmManager().set(AlarmManager.RTC_WAKEUP, attributes.sendAt, pendingIntent);
347 | Log.i("ReactSystemNotification", "Set One-Time Alarm: " + id);
348 | break;
349 | }
350 | }
351 |
352 | Log.i("ReactSystemNotification",
353 | "Notification Schedule Alarm Set: " + id + ", Repeat Type: " + attributes.repeatType
354 | + ", Current Time: " + System.currentTimeMillis() + ", First Send At: " + attributes.sendAt);
355 | }
356 |
357 | /**
358 | * Cancel the delayed notification.
359 | */
360 | public void cancelAlarm() {
361 | PendingIntent pendingIntent = getScheduleNotificationIntent();
362 | getAlarmManager().cancel(pendingIntent);
363 |
364 | Log.i("ReactSystemNotification", "Notification Alarm Canceled: " + id);
365 | }
366 |
367 | public void saveAttributesToPreferences() {
368 | SharedPreferences.Editor editor = getSharedPreferences().edit();
369 |
370 | String attributesJSONString = new Gson().toJson(attributes);
371 |
372 | editor.putString(Integer.toString(id), attributesJSONString);
373 |
374 | if (Build.VERSION.SDK_INT < 9) {
375 | editor.commit();
376 | } else {
377 | editor.apply();
378 | }
379 |
380 | Log.i("ReactSystemNotification", "Notification Saved To Pref: " + id + ": " + attributesJSONString);
381 | }
382 |
383 | public void loadAttributesFromPreferences() {
384 | String attributesJSONString = getSharedPreferences().getString(Integer.toString(id), null);
385 | this.attributes = (NotificationAttributes) new Gson().fromJson(attributesJSONString,
386 | NotificationAttributes.class);
387 |
388 | Log.i("ReactSystemNotification", "Notification Loaded From Pref: " + id + ": " + attributesJSONString);
389 | }
390 |
391 | public void deleteFromPreferences() {
392 | SharedPreferences.Editor editor = getSharedPreferences().edit();
393 |
394 | editor.remove(Integer.toString(id));
395 |
396 | if (Build.VERSION.SDK_INT < 9) {
397 | editor.commit();
398 | } else {
399 | editor.apply();
400 | }
401 |
402 | Log.i("ReactSystemNotification", "Notification Deleted From Pref: " + id);
403 | }
404 |
405 | private NotificationManager getSysNotificationManager() {
406 | return (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
407 | }
408 |
409 | private AlarmManager getAlarmManager() {
410 | return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
411 | }
412 |
413 | private SharedPreferences getSharedPreferences() {
414 | return RCTNotificationManager.getSharedPreferences(context);
415 | }
416 |
417 | private PendingIntent getContentIntent() {
418 | Intent intent = new Intent(context, NotificationEventReceiver.class);
419 |
420 | intent.putExtra(NotificationEventReceiver.NOTIFICATION_ID, id);
421 | intent.putExtra(NotificationEventReceiver.ACTION, attributes.action);
422 | intent.putExtra(NotificationEventReceiver.PAYLOAD, attributes.payload);
423 |
424 | return PendingIntent.getBroadcast(context, id, intent, PendingIntent.FLAG_UPDATE_CURRENT);
425 | }
426 |
427 | private PendingIntent getScheduleNotificationIntent() {
428 | Intent notificationIntent = new Intent(context, NotificationPublisher.class);
429 | notificationIntent.putExtra(NotificationPublisher.NOTIFICATION_ID, id);
430 |
431 | return PendingIntent.getBroadcast(context, id, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
432 | }
433 | }
434 |
--------------------------------------------------------------------------------
/android/src/main/java/com/staltz/reactnativeandroidlocalnotification/NotificationAttributes.java:
--------------------------------------------------------------------------------
1 | package com.staltz.reactnativeandroidlocalnotification;
2 |
3 | import com.facebook.react.bridge.ReadableArray;
4 | import com.facebook.react.bridge.ReadableMap;
5 | import com.facebook.react.bridge.WritableArray;
6 | import com.facebook.react.bridge.WritableMap;
7 |
8 | import java.util.ArrayList;
9 |
10 | public class NotificationAttributes {
11 | public Integer id;
12 | public String subject;
13 | public String message;
14 | public String action;
15 | public String payload;
16 |
17 | public Boolean delayed;
18 | public Integer delay;
19 |
20 | public Boolean scheduled;
21 | public Long sendAt;
22 | public Integer sendAtYear;
23 | public Integer sendAtMonth;
24 | public Integer sendAtDay;
25 | public Integer sendAtWeekDay;
26 | public Integer sendAtHour;
27 | public Integer sendAtMinute;
28 |
29 | public String repeatEvery;
30 | public String repeatType;
31 | public Integer repeatTime;
32 | public Integer repeatCount;
33 | public Long endAt;
34 |
35 | public Integer priority;
36 | public String smallIcon;
37 | public String largeIcon;
38 | public String sound;
39 | public String vibrate;
40 | public String lights;
41 | public Boolean autoClear;
42 | public Boolean onlyAlertOnce;
43 | public String tickerText;
44 | public Long when;
45 | public String bigText;
46 | public String bigStyleUrlImage;
47 | public String bigStyleImageBase64;
48 | public String subText;
49 | public Integer progress;
50 | public Integer lifetime;
51 | public Integer progressEnd;
52 | public String color;
53 | public Integer number;
54 | public String category;
55 | public Boolean localOnly;
56 |
57 | public Boolean inboxStyle;
58 | public String inboxStyleBigContentTitle;
59 | public String inboxStyleSummaryText;
60 | public ArrayList inboxStyleLines;
61 |
62 | public String group;
63 |
64 | public void loadFromReadableMap(ReadableMap readableMap) {
65 | if (readableMap.hasKey("id"))
66 | id = readableMap.getInt("id");
67 | if (readableMap.hasKey("subject"))
68 | subject = readableMap.getString("subject");
69 | if (readableMap.hasKey("message"))
70 | message = readableMap.getString("message");
71 | if (readableMap.hasKey("action"))
72 | action = readableMap.getString("action");
73 | if (readableMap.hasKey("payload"))
74 | payload = readableMap.getString("payload");
75 |
76 | if (readableMap.hasKey("delayed"))
77 | delayed = readableMap.getBoolean("delayed");
78 | if (readableMap.hasKey("delay"))
79 | delay = readableMap.getInt("delay");
80 |
81 | if (readableMap.hasKey("scheduled"))
82 | scheduled = readableMap.getBoolean("scheduled");
83 | if (readableMap.hasKey("sendAt"))
84 | sendAt = Long.parseLong(readableMap.getString("sendAt"));
85 | if (readableMap.hasKey("sendAtYear"))
86 | sendAtYear = readableMap.getInt("sendAtYear");
87 | if (readableMap.hasKey("sendAtMonth"))
88 | sendAtMonth = readableMap.getInt("sendAtMonth");
89 | if (readableMap.hasKey("sendAtDay"))
90 | sendAtDay = readableMap.getInt("sendAtDay");
91 | if (readableMap.hasKey("sendAtWeekDay"))
92 | sendAtWeekDay = readableMap.getInt("sendAtWeekDay");
93 | if (readableMap.hasKey("sendAtHour"))
94 | sendAtHour = readableMap.getInt("sendAtHour");
95 | if (readableMap.hasKey("sendAtMinute"))
96 | sendAtMinute = readableMap.getInt("sendAtMinute");
97 |
98 | if (readableMap.hasKey("repeatEvery"))
99 | repeatEvery = readableMap.getString("repeatEvery");
100 | if (readableMap.hasKey("repeatType"))
101 | repeatType = readableMap.getString("repeatType");
102 | if (readableMap.hasKey("repeatTime"))
103 | repeatTime = readableMap.getInt("repeatTime");
104 | if (readableMap.hasKey("repeatCount"))
105 | repeatCount = readableMap.getInt("repeatCount");
106 | if (readableMap.hasKey("endAt"))
107 | endAt = Long.parseLong(readableMap.getString("endAt"));
108 |
109 | if (readableMap.hasKey("priority"))
110 | priority = readableMap.getInt("priority");
111 | if (readableMap.hasKey("smallIcon"))
112 | smallIcon = readableMap.getString("smallIcon");
113 | if (readableMap.hasKey("largeIcon"))
114 | largeIcon = readableMap.getString("largeIcon");
115 | if (readableMap.hasKey("sound"))
116 | sound = readableMap.getString("sound");
117 | if (readableMap.hasKey("vibrate"))
118 | vibrate = readableMap.getString("vibrate");
119 | if (readableMap.hasKey("lights"))
120 | lights = readableMap.getString("lights");
121 | if (readableMap.hasKey("autoClear"))
122 | autoClear = readableMap.getBoolean("autoClear");
123 | else
124 | autoClear = true;
125 | if (readableMap.hasKey("onlyAlertOnce"))
126 | onlyAlertOnce = readableMap.getBoolean("onlyAlertOnce");
127 | if (readableMap.hasKey("tickerText"))
128 | tickerText = readableMap.getString("tickerText");
129 | if (readableMap.hasKey("when"))
130 | when = Long.parseLong(readableMap.getString("when"));
131 | if (readableMap.hasKey("bigText"))
132 | bigText = readableMap.getString("bigText");
133 | if (readableMap.hasKey("bigStyleUrlImage"))
134 | bigStyleUrlImage = readableMap.getString("bigStyleUrlImage");
135 | if (readableMap.hasKey("bigStyleImageBase64"))
136 | bigStyleImageBase64 = readableMap.getString("bigStyleImageBase64");
137 | if (readableMap.hasKey("subText"))
138 | subText = readableMap.getString("subText");
139 | if (readableMap.hasKey("progress"))
140 | progress = readableMap.getInt("progress");
141 | if (readableMap.hasKey("progressEnd"))
142 | progressEnd = readableMap.getInt("progressEnd");
143 | if (readableMap.hasKey("lifetime"))
144 | lifetime = readableMap.getInt("lifetime");
145 |
146 | if (readableMap.hasKey("color"))
147 | color = readableMap.getString("color");
148 | if (readableMap.hasKey("number"))
149 | number = readableMap.getInt("number");
150 | if (readableMap.hasKey("category"))
151 | category = readableMap.getString("category");
152 | if (readableMap.hasKey("localOnly"))
153 | localOnly = readableMap.getBoolean("localOnly");
154 | if (readableMap.hasKey("group"))
155 | group = readableMap.getString("group");
156 |
157 | if (readableMap.hasKey("inboxStyle")) {
158 | inboxStyle = true;
159 | ReadableMap inboxStyleMap = readableMap.getMap("inboxStyle");
160 |
161 | inboxStyleBigContentTitle = inboxStyleMap.getString("bigContentTitle");
162 | inboxStyleSummaryText = inboxStyleMap.getString("summaryText");
163 |
164 | ReadableArray inboxLines = inboxStyleMap.getArray("lines");
165 | if (inboxLines != null) {
166 | inboxStyleLines = new ArrayList<>();
167 | for (int i = 0; i < inboxLines.size(); i++) {
168 | inboxStyleLines.add(inboxLines.getString(i));
169 | }
170 | }
171 | } else {
172 | inboxStyle = false;
173 | }
174 |
175 | }
176 |
177 | public ReadableMap asReadableMap() {
178 | WritableMap writableMap = new com.facebook.react.bridge.WritableNativeMap();
179 |
180 | if (id != null)
181 | writableMap.putInt("id", id);
182 | if (subject != null)
183 | writableMap.putString("subject", subject);
184 | if (message != null)
185 | writableMap.putString("message", message);
186 | if (action != null)
187 | writableMap.putString("action", action);
188 | if (payload != null)
189 | writableMap.putString("payload", payload);
190 |
191 | if (delayed != null)
192 | writableMap.putBoolean("delayed", delayed);
193 | if (delay != null)
194 | writableMap.putInt("delay", delay);
195 |
196 | if (scheduled != null)
197 | writableMap.putBoolean("scheduled", scheduled);
198 | if (sendAt != null)
199 | writableMap.putString("sendAt", Long.toString(sendAt));
200 | if (sendAtYear != null)
201 | writableMap.putInt("sendAtYear", sendAtYear);
202 | if (sendAtMonth != null)
203 | writableMap.putInt("sendAtMonth", sendAtMonth);
204 | if (sendAtDay != null)
205 | writableMap.putInt("sendAtDay", sendAtDay);
206 | if (sendAtWeekDay != null)
207 | writableMap.putInt("sendAtWeekDay", sendAtWeekDay);
208 | if (sendAtHour != null)
209 | writableMap.putInt("sendAtHour", sendAtHour);
210 | if (sendAtMinute != null)
211 | writableMap.putInt("sendAtMinute", sendAtMinute);
212 |
213 | if (repeatEvery != null)
214 | writableMap.putString("repeatEvery", repeatEvery);
215 | if (repeatType != null)
216 | writableMap.putString("repeatType", repeatType);
217 | if (repeatTime != null)
218 | writableMap.putInt("repeatTime", repeatTime);
219 | if (repeatCount != null)
220 | writableMap.putInt("repeatCount", repeatCount);
221 | if (endAt != null)
222 | writableMap.putString("endAt", Long.toString(endAt));
223 |
224 | if (priority != null)
225 | writableMap.putInt("priority", priority);
226 | if (smallIcon != null)
227 | writableMap.putString("smallIcon", smallIcon);
228 | if (largeIcon != null)
229 | writableMap.putString("largeIcon", largeIcon);
230 | if (sound != null)
231 | writableMap.putString("sound", sound);
232 | if (vibrate != null)
233 | writableMap.putString("vibrate", vibrate);
234 | if (lights != null)
235 | writableMap.putString("lights", lights);
236 | if (autoClear != null)
237 | writableMap.putBoolean("autoClear", autoClear);
238 | if (onlyAlertOnce != null)
239 | writableMap.putBoolean("onlyAlertOnce", onlyAlertOnce);
240 | if (tickerText != null)
241 | writableMap.putString("tickerText", tickerText);
242 | if (when != null)
243 | writableMap.putString("when", Long.toString(when));
244 | if (bigText != null)
245 | writableMap.putString("bigText", bigText);
246 | if (bigStyleImageBase64 != null)
247 | writableMap.putString("bigStyleImageBase64", bigStyleImageBase64);
248 | if (bigStyleUrlImage != null)
249 | writableMap.putString("bigStyleImageBase64", bigStyleUrlImage);
250 | if (subText != null)
251 | writableMap.putString("subText", subText);
252 | if (progress != null)
253 | writableMap.putInt("progress", progress);
254 | if (color != null)
255 | writableMap.putString("color", color);
256 | if (number != null)
257 | writableMap.putInt("number", number);
258 | if (category != null)
259 | writableMap.putString("category", category);
260 | if (localOnly != null)
261 | writableMap.putBoolean("localOnly", localOnly);
262 | if (group != null)
263 | writableMap.putString("group", group);
264 |
265 | if (progressEnd != null)
266 | writableMap.putInt("progressEnd", progressEnd);
267 | if (lifetime != null)
268 | writableMap.putInt("lifetime", lifetime);
269 |
270 | if (inboxStyle) {
271 |
272 | WritableMap inboxStyle = new com.facebook.react.bridge.WritableNativeMap();
273 | if (inboxStyleBigContentTitle != null)
274 | inboxStyle.putString("bigContentTitle", inboxStyleBigContentTitle);
275 | if (inboxStyleSummaryText != null)
276 | inboxStyle.putString("summaryText", inboxStyleSummaryText);
277 |
278 | if (inboxStyleLines != null) {
279 | WritableArray inboxLines = new com.facebook.react.bridge.WritableNativeArray();
280 | for (int i = 0; i < inboxStyleLines.size(); i++) {
281 | inboxLines.pushString(inboxStyleLines.get(i));
282 | }
283 | inboxStyle.putArray("lines", inboxLines);
284 | }
285 |
286 | writableMap.putMap("inboxStyle", inboxStyle);
287 | }
288 |
289 | return writableMap;
290 | }
291 | }
292 |
--------------------------------------------------------------------------------
/android/src/main/java/com/staltz/reactnativeandroidlocalnotification/NotificationEventReceiver.java:
--------------------------------------------------------------------------------
1 | package com.staltz.reactnativeandroidlocalnotification;
2 |
3 | import android.content.ComponentName;
4 | import android.os.Build;
5 | import android.os.Bundle;
6 | import android.app.ActivityManager;
7 | import android.app.ActivityManager.RunningAppProcessInfo;
8 | import android.content.Intent;
9 | import android.content.Context;
10 | import android.content.BroadcastReceiver;
11 |
12 | import java.util.List;
13 |
14 | import android.util.Log;
15 |
16 | /**
17 | * Handles user's interaction on notifications.
18 | *
19 | * Sends broadcast to the application, launches the app if needed.
20 | */
21 | public class NotificationEventReceiver extends BroadcastReceiver {
22 | final static String NOTIFICATION_ID = "id";
23 | final static String ACTION = "action";
24 | final static String PAYLOAD = "payload";
25 |
26 | public void onReceive(Context context, Intent intent) {
27 | Bundle extras = intent.getExtras();
28 |
29 | Log.i("ReactSystemNotification", "NotificationEventReceiver: Recived: " + extras.getString(ACTION)
30 | + ", Notification ID: " + extras.getInt(NOTIFICATION_ID) + ", payload: " + extras.getString(PAYLOAD));
31 |
32 | // If the application is not running or is not in foreground, start it with the
33 | // notification
34 | // passed in
35 | if (!applicationIsRunning(context)) {
36 | String packageName = context.getApplicationContext().getPackageName();
37 | Intent launchIntent = context.getPackageManager().getLaunchIntentForPackage(packageName);
38 |
39 | launchIntent.putExtra("initialSysNotificationId", extras.getInt(NOTIFICATION_ID));
40 | launchIntent.putExtra("initialSysNotificationAction", extras.getString(ACTION));
41 | launchIntent.putExtra("initialSysNotificationPayload", extras.getString(PAYLOAD));
42 | launchIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
43 |
44 | context.startActivity(launchIntent);
45 | Log.i("ReactSystemNotification", "NotificationEventReceiver: Launching: " + packageName);
46 | } else {
47 | sendBroadcast(context, extras); // If the application is already running in foreground, send a brodcast too
48 | }
49 | }
50 |
51 | private void sendBroadcast(Context context, Bundle extras) {
52 | Intent brodcastIntent = new Intent("NotificationEvent");
53 |
54 | brodcastIntent.putExtra("id", extras.getInt(NOTIFICATION_ID));
55 | brodcastIntent.putExtra("action", extras.getString(ACTION));
56 | brodcastIntent.putExtra("payload", extras.getString(PAYLOAD));
57 |
58 | context.sendBroadcast(brodcastIntent);
59 | Log.v("ReactSystemNotification",
60 | "NotificationEventReceiver: Broadcast Sent: NotificationEvent: " + extras.getString(ACTION)
61 | + ", Notification ID: " + extras.getInt(NOTIFICATION_ID) + ", payload: "
62 | + extras.getString(PAYLOAD));
63 | }
64 |
65 | private boolean applicationIsRunning(Context context) {
66 | ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
67 |
68 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.KITKAT_WATCH) {
69 | List processInfos = activityManager.getRunningAppProcesses();
70 | for (RunningAppProcessInfo processInfo : processInfos) {
71 | if (processInfo.processName.equals(context.getApplicationContext().getPackageName())) {
72 | if (processInfo.importance == RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
73 | for (String d : processInfo.pkgList) {
74 | Log.v("ReactSystemNotification", "NotificationEventReceiver: ok: " + d);
75 | return true;
76 | }
77 | }
78 | }
79 | }
80 | } else {
81 | List taskInfo = activityManager.getRunningTasks(1);
82 | ComponentName componentInfo = taskInfo.get(0).topActivity;
83 | if (componentInfo.getPackageName().equals(context.getPackageName())) {
84 | return true;
85 | }
86 | }
87 |
88 | return false;
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/android/src/main/java/com/staltz/reactnativeandroidlocalnotification/NotificationModule.java:
--------------------------------------------------------------------------------
1 | package com.staltz.reactnativeandroidlocalnotification;
2 |
3 | import android.os.Bundle;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 | import android.content.BroadcastReceiver;
8 | import android.app.Activity;
9 |
10 | import com.facebook.react.modules.core.DeviceEventManagerModule;
11 | import com.facebook.react.bridge.ReactApplicationContext;
12 | import com.facebook.react.bridge.ReactContextBaseJavaModule;
13 | import com.facebook.react.bridge.ReactMethod;
14 | import com.facebook.react.bridge.Arguments;
15 | import com.facebook.react.bridge.Callback;
16 | import com.facebook.react.bridge.ReadableMap;
17 | import com.facebook.react.bridge.WritableMap;
18 | import com.facebook.react.bridge.ReadableArray;
19 | import com.facebook.react.bridge.WritableArray;
20 | import com.facebook.react.bridge.WritableNativeArray;
21 |
22 | import java.util.ArrayList;
23 | import android.util.Log;
24 |
25 | /**
26 | * The main React native module.
27 | *
28 | * Provides JS accessible API, bridge Java and JavaScript.
29 | */
30 | public class NotificationModule extends ReactContextBaseJavaModule {
31 |
32 | public Context mContext = null;
33 | public RCTNotificationManager mNotificationManager = null;
34 |
35 | @Override
36 | public String getName() {
37 | return "NotificationModule";
38 | }
39 |
40 | /**
41 | * Constructor.
42 | */
43 | public NotificationModule(ReactApplicationContext reactContext) {
44 | super(reactContext);
45 |
46 | this.mContext = reactContext;
47 | this.mNotificationManager = new RCTNotificationManager(reactContext);
48 |
49 | listenNotificationEvent();
50 | }
51 |
52 | /**
53 | * React method to create or update a notification.
54 | */
55 | @ReactMethod
56 | public void rCreate(Integer notificationID, ReadableMap notificationAttributes, Callback errorCallback,
57 | Callback successCallback) {
58 | try {
59 | NotificationAttributes a = getNotificationAttributesFromReadableMap(notificationAttributes);
60 | Notification n = mNotificationManager.createOrUpdate(notificationID, a);
61 |
62 | successCallback.invoke(n.getAttributes().asReadableMap());
63 |
64 | } catch (Exception e) {
65 | errorCallback.invoke(e.getMessage());
66 | Log.e("ReactSystemNotification", "NotificationModule: rCreate Error: " + Log.getStackTraceString(e));
67 | }
68 | }
69 |
70 | /**
71 | * React method to get all notification ids.
72 | */
73 | @ReactMethod
74 | public void rGetIDs(Callback errorCallback, Callback successCallback) {
75 | try {
76 | ArrayList ids = mNotificationManager.getIDs();
77 | WritableArray rids = new WritableNativeArray();
78 |
79 | for (Integer id : ids) {
80 | rids.pushInt(id);
81 | }
82 |
83 | successCallback.invoke((ReadableArray) rids);
84 |
85 | } catch (Exception e) {
86 | errorCallback.invoke(e.getMessage());
87 | Log.e("ReactSystemNotification", "NotificationModule: rGetIDs Error: " + Log.getStackTraceString(e));
88 | }
89 | }
90 |
91 | /**
92 | * React method to get data of a notification.
93 | */
94 | @ReactMethod
95 | public void rFind(Integer notificationID, Callback errorCallback, Callback successCallback) {
96 | try {
97 | Notification n = mNotificationManager.find(notificationID);
98 | successCallback.invoke(n.getAttributes().asReadableMap());
99 |
100 | } catch (Exception e) {
101 | errorCallback.invoke(e.getMessage());
102 | Log.e("ReactSystemNotification", "NotificationModule: rFind Error: " + Log.getStackTraceString(e));
103 | }
104 | }
105 |
106 | /**
107 | * React method to delete (i.e. cancel a scheduled) notification.
108 | */
109 | @ReactMethod
110 | public void rDelete(int notificationID, Callback errorCallback, Callback successCallback) {
111 | try {
112 | Notification n = mNotificationManager.delete(notificationID);
113 |
114 | successCallback.invoke(n.getAttributes().asReadableMap());
115 |
116 | } catch (Exception e) {
117 | errorCallback.invoke(e.getMessage());
118 | Log.e("ReactSystemNotification", "NotificationModule: rDelete Error: " + Log.getStackTraceString(e));
119 | }
120 | }
121 |
122 | /**
123 | * React method to delete (i.e. cancel a scheduled) notification.
124 | */
125 | @ReactMethod
126 | public void rDeleteAll(Callback errorCallback, Callback successCallback) {
127 | try {
128 | ArrayList ids = mNotificationManager.getIDs();
129 |
130 | for (Integer id : ids) {
131 | try {
132 | mNotificationManager.delete(id);
133 | } catch (Exception e) {
134 | Log.e("ReactSystemNotification",
135 | "NotificationModule: rDeleteAll Error: " + Log.getStackTraceString(e));
136 | }
137 | }
138 |
139 | successCallback.invoke();
140 |
141 | } catch (Exception e) {
142 | errorCallback.invoke(e.getMessage());
143 | Log.e("ReactSystemNotification", "NotificationModule: rDeleteAll Error: " + Log.getStackTraceString(e));
144 | }
145 | }
146 |
147 | /**
148 | * React method to clear a notification.
149 | */
150 | @ReactMethod
151 | public void rClear(int notificationID, Callback errorCallback, Callback successCallback) {
152 | try {
153 | Notification n = mNotificationManager.clear(notificationID);
154 |
155 | successCallback.invoke(n.getAttributes().asReadableMap());
156 |
157 | } catch (Exception e) {
158 | errorCallback.invoke(e.getMessage());
159 | Log.e("ReactSystemNotification", "NotificationModule: rClear Error: " + Log.getStackTraceString(e));
160 | }
161 | }
162 |
163 | /**
164 | * React method to clear all notifications of this app.
165 | */
166 | @ReactMethod
167 | public void rClearAll(Callback errorCallback, Callback successCallback) {
168 | try {
169 | mNotificationManager.clearAll();
170 | successCallback.invoke();
171 |
172 | } catch (Exception e) {
173 | errorCallback.invoke(e.getMessage());
174 | Log.e("ReactSystemNotification", "NotificationModule: rClearAll Error: " + Log.getStackTraceString(e));
175 | }
176 | }
177 |
178 | @ReactMethod
179 | public void rGetApplicationName(Callback errorCallback, Callback successCallback) {
180 | try {
181 | int stringId = getReactApplicationContext().getApplicationInfo().labelRes;
182 | successCallback.invoke(getReactApplicationContext().getString(stringId));
183 |
184 | } catch (Exception e) {
185 | errorCallback.invoke(e.getMessage());
186 | Log.e("ReactSystemNotification",
187 | "NotificationModule: rGetApplicationName Error: " + Log.getStackTraceString(e));
188 | }
189 | }
190 |
191 | /**
192 | * Emit JavaScript events.
193 | */
194 | private void sendEvent(String eventName, Object params) {
195 | getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit(eventName,
196 | params);
197 |
198 | Log.i("ReactSystemNotification", "NotificationModule: sendEvent (to JS): " + eventName);
199 | }
200 |
201 | @ReactMethod
202 | public void getInitialSysNotification(Callback cb) {
203 | final Activity activity = getCurrentActivity();
204 |
205 | if (activity == null) {
206 | return;
207 | }
208 |
209 | Intent intent = activity.getIntent();
210 | Bundle extras = intent.getExtras();
211 |
212 | if (extras != null) {
213 | Integer initialSysNotificationId = extras.getInt("initialSysNotificationId");
214 | if (initialSysNotificationId != null) {
215 | cb.invoke(initialSysNotificationId, extras.getString("initialSysNotificationAction"),
216 | extras.getString("initialSysNotificationPayload"));
217 | return;
218 | }
219 | }
220 | }
221 |
222 | @ReactMethod
223 | public void removeInitialSysNotification() {
224 | final Activity activity = getCurrentActivity();
225 |
226 | if (activity == null) {
227 | return;
228 | }
229 |
230 | activity.getIntent().removeExtra("initialSysNotificationId");
231 | activity.getIntent().removeExtra("initialSysNotificationAction");
232 | activity.getIntent().removeExtra("initialSysNotificationPayload");
233 | }
234 |
235 | private NotificationAttributes getNotificationAttributesFromReadableMap(ReadableMap readableMap) {
236 | NotificationAttributes notificationAttributes = new NotificationAttributes();
237 |
238 | notificationAttributes.loadFromReadableMap(readableMap);
239 |
240 | return notificationAttributes;
241 | }
242 |
243 | private void listenNotificationEvent() {
244 | IntentFilter intentFilter = new IntentFilter("NotificationEvent");
245 |
246 | getReactApplicationContext().registerReceiver(new BroadcastReceiver() {
247 | @Override
248 | public void onReceive(Context context, Intent intent) {
249 |
250 | Bundle extras = intent.getExtras();
251 |
252 | WritableMap params = Arguments.createMap();
253 | params.putInt("notificationID", extras.getInt(NotificationEventReceiver.NOTIFICATION_ID));
254 | params.putString("action", extras.getString(NotificationEventReceiver.ACTION));
255 | params.putString("payload", extras.getString(NotificationEventReceiver.PAYLOAD));
256 |
257 | sendEvent("sysModuleNotificationClick", params);
258 | }
259 | }, intentFilter);
260 | }
261 |
262 | }
263 |
--------------------------------------------------------------------------------
/android/src/main/java/com/staltz/reactnativeandroidlocalnotification/NotificationPackage.java:
--------------------------------------------------------------------------------
1 | package com.staltz.reactnativeandroidlocalnotification;
2 |
3 | import com.facebook.react.ReactPackage;
4 | import com.facebook.react.bridge.JavaScriptModule;
5 | import com.facebook.react.bridge.NativeModule;
6 | import com.facebook.react.bridge.ReactApplicationContext;
7 | import com.facebook.react.uimanager.ViewManager;
8 | import java.util.ArrayList;
9 | import java.util.Collections;
10 | import java.util.List;
11 |
12 | /**
13 | * The React package.
14 | */
15 | public class NotificationPackage implements ReactPackage {
16 | public NotificationPackage() {
17 | }
18 |
19 | @Override
20 | public List createNativeModules(ReactApplicationContext reactContext) {
21 | List modules = new ArrayList<>();
22 |
23 | modules.add(new NotificationModule(reactContext));
24 | return modules;
25 | }
26 |
27 | @Override
28 | public List createViewManagers(ReactApplicationContext reactContext) {
29 | return Collections.emptyList();
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/android/src/main/java/com/staltz/reactnativeandroidlocalnotification/NotificationPublisher.java:
--------------------------------------------------------------------------------
1 | package com.staltz.reactnativeandroidlocalnotification;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import java.lang.System;
8 | import java.util.Calendar;
9 | import android.util.Log;
10 |
11 | /**
12 | * Publisher for scheduled notifications.
13 | */
14 | public class NotificationPublisher extends BroadcastReceiver {
15 |
16 | final static String NOTIFICATION_ID = "notificationId";
17 |
18 | @Override
19 | public void onReceive(Context context, Intent intent) {
20 | int id = intent.getIntExtra(NOTIFICATION_ID, 0);
21 | long currentTime = System.currentTimeMillis();
22 | Log.i("ReactSystemNotification",
23 | "NotificationPublisher: Prepare To Publish: " + id + ", Now Time: " + currentTime);
24 |
25 | RCTNotificationManager notificationManager = new RCTNotificationManager(context);
26 | Notification notification = notificationManager.find(id);
27 |
28 | if (notification.getAttributes() != null) {
29 |
30 | // Delete notifications that are out-dated
31 | if (notification.getAttributes().endAt != null && notification.getAttributes().endAt < currentTime) {
32 | notification.cancelAlarm();
33 | notification.deleteFromPreferences();
34 |
35 | // Show and delete one-time notifications
36 | } else if (notification.getAttributes().repeatType == null) {
37 | notification.show();
38 | notification.cancelAlarm();
39 | notification.deleteFromPreferences();
40 |
41 | // Special conditions for weekly based notifications
42 | } else if (notification.getAttributes().repeatType.equals("week")) {
43 | Calendar calendar = Calendar.getInstance();
44 | int day = calendar.get(Calendar.DAY_OF_WEEK);
45 | day = day - 1;
46 | if (notification.getAttributes().sendAtWeekDay == day)
47 | notification.show();
48 |
49 | // Special conditions for monthly based notifications
50 | } else if (notification.getAttributes().repeatType.equals("month")) {
51 | Calendar calendar = Calendar.getInstance();
52 | int day = calendar.get(Calendar.DAY_OF_MONTH);
53 | if (notification.getAttributes().sendAtDay == day)
54 | notification.show();
55 |
56 | // Special conditions for yearly based notifications
57 | } else if (notification.getAttributes().repeatType.equals("year")) {
58 | Calendar calendar = Calendar.getInstance();
59 | int day = calendar.get(Calendar.DAY_OF_MONTH);
60 | int month = calendar.get(Calendar.MONTH);
61 | if (notification.getAttributes().sendAtDay == day && notification.getAttributes().sendAtMonth == month)
62 | notification.show();
63 |
64 | // Other repeating notifications - just show them
65 | } else {
66 | notification.show();
67 | }
68 |
69 | if (notification.getAttributes().delayed || !notification.getAttributes().scheduled) {
70 | notification.deleteFromPreferences();
71 | }
72 |
73 | } else {
74 | notification.cancelAlarm();
75 | notification.deleteFromPreferences();
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/android/src/main/java/com/staltz/reactnativeandroidlocalnotification/RCTNotificationManager.java:
--------------------------------------------------------------------------------
1 | package com.staltz.reactnativeandroidlocalnotification;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.app.NotificationManager;
6 | import java.util.ArrayList;
7 | import java.util.Set;
8 |
9 | import android.util.Log;
10 |
11 | /**
12 | * A high level notification manager
13 | *
14 | * Warps the system notification API to make managing direct and scheduled
15 | * notification easy.
16 | */
17 | public class RCTNotificationManager {
18 | final static String PREFERENCES_KEY = "ReactNativeSystemNotification";
19 | public Context context = null;
20 | public SharedPreferences sharedPreferences = null;
21 |
22 | /**
23 | * Constructor.
24 | */
25 | public RCTNotificationManager(Context context) {
26 | this.context = context;
27 | this.sharedPreferences = getSharedPreferences(context);
28 | }
29 |
30 | static SharedPreferences getSharedPreferences(Context context) {
31 | return context.getSharedPreferences(PREFERENCES_KEY, Context.MODE_PRIVATE);
32 | }
33 |
34 | /**
35 | * Create a notification.
36 | */
37 | public Notification create(Integer notificationID, NotificationAttributes notificationAttributes) {
38 | Notification notification = new Notification(context, notificationID, notificationAttributes);
39 |
40 | notification.create();
41 |
42 | return notification;
43 | }
44 |
45 | /**
46 | * Create or update (if exists) a notification.
47 | */
48 | public Notification createOrUpdate(Integer notificationID, NotificationAttributes notificationAttributes) {
49 | if (getIDs().contains(notificationID)) {
50 | Notification notification = find(notificationID);
51 |
52 | notification.update(notificationAttributes);
53 | return notification;
54 |
55 | } else {
56 | return create(notificationID, notificationAttributes);
57 | }
58 | }
59 |
60 | /**
61 | * Get all notification ids.
62 | */
63 | public ArrayList getIDs() {
64 | Set keys = sharedPreferences.getAll().keySet();
65 | ArrayList ids = new ArrayList<>();
66 |
67 | for (String key : keys) {
68 | try {
69 | ids.add(Integer.parseInt(key));
70 | // TODO: Delete out-dated notifications BTW
71 | } catch (Exception e) {
72 | Log.e("ReactSystemNotification", "RCTNotificationManager: getIDs Error: " + Log.getStackTraceString(e));
73 | }
74 | }
75 |
76 | return ids;
77 | }
78 |
79 | /**
80 | * Get a notification by its id.
81 | */
82 | public Notification find(Integer notificationID) {
83 | Notification notification = new Notification(context, notificationID, null);
84 |
85 | if (notification.getAttributes() == null)
86 | notification.loadAttributesFromPreferences();
87 |
88 | return notification;
89 | }
90 |
91 | /**
92 | * Delete a notification by its id.
93 | */
94 | public Notification delete(Integer notificationID) {
95 | return find(notificationID).delete();
96 | }
97 |
98 | /**
99 | * Clear a notification by its id.
100 | */
101 | public Notification clear(Integer notificationID) {
102 | return find(notificationID).clear();
103 | }
104 |
105 | /**
106 | * Clear all notifications.
107 | */
108 | public void clearAll() {
109 | NotificationManager systemNotificationManager = (NotificationManager) context
110 | .getSystemService(Context.NOTIFICATION_SERVICE);
111 | systemNotificationManager.cancelAll();
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/android/src/main/java/com/staltz/reactnativeandroidlocalnotification/SystemBootEventReceiver.java:
--------------------------------------------------------------------------------
1 | package com.staltz.reactnativeandroidlocalnotification;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import java.util.ArrayList;
8 |
9 | import android.util.Log;
10 |
11 | /**
12 | * Set alarms for scheduled notification after system reboot.
13 | */
14 | public class SystemBootEventReceiver extends BroadcastReceiver {
15 |
16 | @Override
17 | public void onReceive(Context context, Intent intent) {
18 | Log.i("ReactSystemNotification", "SystemBootEventReceiver: Setting system alarms");
19 |
20 | if (intent.getAction().equals("android.intent.action.BOOT_COMPLETED")) {
21 | RCTNotificationManager notificationManager = new RCTNotificationManager(context);
22 |
23 | ArrayList ids = notificationManager.getIDs();
24 |
25 | for (Integer id : ids) {
26 | try {
27 | Notification notification = notificationManager.find(id);
28 |
29 | if (notification.getAttributes() != null) {
30 | notification.cancelAlarm();
31 | notification.setAlarmAndSaveOrShow();
32 | Log.i("ReactSystemNotification",
33 | "SystemBootEventReceiver: Alarm set for: " + notification.getAttributes().id);
34 | }
35 | } catch (Exception e) {
36 | Log.e("ReactSystemNotification", "SystemBootEventReceiver: onReceive Error: " + e.getMessage());
37 | }
38 | }
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var React = require('react-native');
4 | var { DeviceEventEmitter } = React;
5 |
6 | var NotificationModule = require('react-native').NativeModules.NotificationModule;
7 |
8 | // Warp the native module so we can do some pre/post processing to have a cleaner API.
9 | var Notification = {
10 | create: function(attributes = {}) {
11 | return new Promise(function(resolve, reject) {
12 | NotificationModule.rGetApplicationName(function(e) {}, function(applicationName) {
13 |
14 | // Set defaults
15 | if (!attributes.subject) attributes.subject = applicationName;
16 | attributes = encodeNativeNotification(attributes);
17 |
18 | NotificationModule.rCreate(attributes.id, attributes, reject, function(notification) {
19 | resolve(decodeNativeNotification(notification));
20 | });
21 | });
22 | });
23 | },
24 |
25 | getIDs: function() {
26 | return new Promise(function(resolve, reject) {
27 | NotificationModule.rGetIDs(reject, resolve);
28 | });
29 | },
30 |
31 | find: function(id) {
32 | return new Promise(function(resolve, reject) {
33 | NotificationModule.rFind(id, reject, function(notification) {
34 | resolve(decodeNativeNotification(notification));
35 | });
36 | });
37 | },
38 |
39 | delete: function(id) {
40 | return new Promise(function(resolve, reject) {
41 | NotificationModule.rDelete(id, reject, function(notification) {
42 | resolve(decodeNativeNotification(notification));
43 | });
44 | });
45 | },
46 |
47 | deleteAll: function() {
48 | return new Promise(function(resolve, reject) {
49 | NotificationModule.rDeleteAll(reject, resolve);
50 | });
51 | },
52 |
53 | clear: function(id) {
54 | return new Promise(function(resolve, reject) {
55 | NotificationModule.rClear(id, reject, function(notification) {
56 | resolve(decodeNativeNotification(notification));
57 | });
58 | });
59 | },
60 |
61 | clearAll: function() {
62 | return new Promise(function(resolve, reject) {
63 | NotificationModule.rClearAll(reject, resolve);
64 | });
65 | },
66 |
67 | addListener: function(type, listener) {
68 | switch (type) {
69 | case 'press':
70 | case 'click':
71 | DeviceEventEmitter.addListener('sysNotificationClick', listener);
72 |
73 | NotificationModule.getInitialSysNotification(function(initialSysNotificationId,
74 | initialSysNotificationAction,
75 | initialSysNotificationPayload) {
76 | if (initialSysNotificationId) {
77 | var event = {
78 | action: initialSysNotificationAction,
79 | payload: JSON.parse(initialSysNotificationPayload)
80 | }
81 |
82 | listener(event);
83 |
84 | NotificationModule.removeInitialSysNotification();
85 | }
86 | });
87 |
88 | break;
89 | }
90 | },
91 |
92 | removeAllListeners: function (type) {
93 | switch (type) {
94 | case 'press':
95 | case 'click':
96 | DeviceEventEmitter.removeAllListeners('sysNotificationClick');
97 | break;
98 | }
99 | },
100 |
101 | module: NotificationModule
102 | }
103 |
104 | module.exports = Notification;
105 |
106 | // Encode the JS notification to pass into the native model
107 | function encodeNativeNotification(attributes) {
108 | if (typeof attributes === 'string') attributes = JSON.parse(attributes);
109 | // Set defaults
110 | if (!attributes.smallIcon) attributes.smallIcon = 'ic_launcher';
111 | if (!attributes.id) attributes.id = parseInt(Math.random() * 100000);
112 | if (!attributes.action) attributes.action = 'DEFAULT';
113 | if (!attributes.payload) attributes.payload = {};
114 | if (attributes.autoClear === undefined) attributes.autoClear = true;
115 | if (attributes.tickerText === undefined) {
116 | if (attributes.subject) {
117 | attributes.tickerText = attributes.subject + ': ' + attributes.message;
118 | } else {
119 | attributes.tickerText = attributes.message;
120 | }
121 | }
122 |
123 | if (attributes.priority === undefined) attributes.priority = 1;
124 | if (attributes.sound === undefined) attributes.sound = 'default';
125 | if (attributes.vibrate === undefined) attributes.vibrate = 'default';
126 | if (attributes.lights === undefined) attributes.lights = 'default';
127 |
128 | attributes.delayed = (attributes.delay !== undefined);
129 | attributes.scheduled = (attributes.sendAt !== undefined);
130 |
131 | // Ensure date are Dates
132 | if (attributes.sendAt && typeof attributes.sendAt !== 'object') attributes.sendAt = new Date(attributes.sendAt);
133 | if (attributes.endAt && typeof attributes.endAt !== 'object') attributes.endAt = new Date(attributes.endAt);
134 | if (attributes.when && typeof attributes.when !== 'object') attributes.when = new Date(attributes.when);
135 |
136 | // Unfold sendAt
137 | if (attributes.sendAt !== undefined) {
138 | attributes.sendAtYear = attributes.sendAt.getFullYear();
139 | attributes.sendAtMonth = attributes.sendAt.getMonth() + 1;
140 | attributes.sendAtDay = attributes.sendAt.getDate();
141 | attributes.sendAtWeekDay = attributes.sendAt.getDay();
142 | attributes.sendAtHour = attributes.sendAt.getHours();
143 | attributes.sendAtMinute = attributes.sendAt.getMinutes();
144 | }
145 |
146 | // Convert date objects into number
147 | if (attributes.sendAt) attributes.sendAt = attributes.sendAt.getTime();
148 | if (attributes.endAt) attributes.endAt = attributes.endAt.getTime();
149 | if (attributes.when) attributes.when = attributes.when.getTime();
150 |
151 | // Prepare scheduled notifications
152 | if (attributes.sendAt !== undefined) {
153 |
154 | // Set repeatType for custom repeat time
155 | if (typeof attributes.repeatEvery === 'number') {
156 | attributes.repeatType = 'time';
157 | attributes.repeatTime = attributes.repeatEvery;
158 | } else if (typeof attributes.repeatEvery === 'string') {
159 | attributes.repeatType = attributes.repeatEvery;
160 | }
161 |
162 | // Naitve module only recognizes the endAt attribute, so we need to
163 | // convert repeatCount to the endAt time base on repeatEvery
164 | if (attributes.repeatCount) {
165 | if (typeof attributes.repeatEvery === 'number') {
166 | attributes.endAt = parseInt(attributes.sendAt + attributes.repeatEvery * attributes.repeatCount + (attributes.repeatEvery / 2));
167 |
168 | } else if (typeof attributes.repeatEvery === 'string') {
169 | switch (attributes.repeatEvery) {
170 | case 'minute':
171 | attributes.endAt = attributes.sendAt + 60000 * attributes.repeatCount + 1000 * 30;
172 | break;
173 |
174 | case 'hour':
175 | attributes.endAt = attributes.sendAt + 60000 * 60 * attributes.repeatCount + 60000 * 30;
176 | break;
177 |
178 | case 'halfDay':
179 | attributes.endAt = attributes.sendAt + 60000 * 60 * 12 * attributes.repeatCount + 60000 * 60 * 6;
180 | break;
181 |
182 | case 'day':
183 | attributes.endAt = attributes.sendAt + 60000 * 60 * 24 * attributes.repeatCount + 60000 * 60 * 12;
184 | break;
185 |
186 | case 'week':
187 | attributes.endAt = attributes.sendAt + 60000 * 60 * 24 * 7 * attributes.repeatCount + 60000 * 60 * 24 * 3;
188 | break;
189 |
190 | case 'month':
191 | attributes.endAt = attributes.sendAt + 60000 * 60 * 24 * 30 * attributes.repeatCount + 60000 * 60 * 24 * 15;
192 | break;
193 |
194 | case 'year':
195 | attributes.endAt = attributes.sendAt + 60000 * 60 * 24 * 365 * attributes.repeatCount + 60000 * 60 * 24 * 100;
196 | break;
197 | }
198 | }
199 | }
200 | }
201 |
202 | // Convert long numbers into string before passing them into native modle,
203 | // incase of integer overflow
204 | if (attributes.sendAt) attributes.sendAt = attributes.sendAt.toString();
205 | if (attributes.endAt) attributes.endAt = attributes.endAt.toString();
206 | if (attributes.when) attributes.when = attributes.when.toString();
207 | if (attributes.repeatEvery) attributes.repeatEvery = attributes.repeatEvery.toString();
208 |
209 | // Convert float into integer
210 | if (attributes.progress) attributes.progress = attributes.progress * 1000;
211 |
212 | // Stringify the payload
213 | attributes.payload = JSON.stringify(attributes.payload);
214 |
215 | return attributes;
216 | }
217 |
218 | // Decode the notification data from the native module to pass into JS
219 | function decodeNativeNotification(attributes) {
220 | // Convert dates back to date object
221 | if (attributes.sendAt) attributes.sendAt = new Date(parseInt(attributes.sendAt));
222 | if (attributes.endAt) attributes.endAt = new Date(parseInt(attributes.endAt));
223 | if (attributes.when) attributes.when = new Date(parseInt(attributes.when));
224 |
225 | // Parse possible integer
226 | if (parseInt(attributes.repeatEvery).toString() === attributes.repeatEvery) attributes.repeatEvery = parseInt(attributes.repeatEvery);
227 |
228 | // Convert integer into float
229 | if (attributes.progress) attributes.progress = attributes.progress / 1000;
230 |
231 | // Parse the payload
232 | if (attributes.payload) attributes.payload = JSON.parse(attributes.payload);
233 |
234 | return attributes;
235 | }
236 |
237 | DeviceEventEmitter.addListener('sysModuleNotificationClick', function(e) {
238 | var event = {
239 | action: e.action,
240 | payload: JSON.parse(e.payload)
241 | }
242 |
243 | DeviceEventEmitter.emit('sysNotificationClick', event);
244 | });
245 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-native-android-local-notification",
3 | "version": "3.0.0",
4 | "description": "Local-only notifications for React Native Android projects",
5 | "main": "index.js",
6 | "files": [
7 | "android/src",
8 | "android/build.gradle",
9 | "index.js"
10 | ],
11 | "repository": {
12 | "type": "git",
13 | "url": "git+https://github.com/staltz/react-native-android-local-notification.git"
14 | },
15 | "nativePackage": true,
16 | "keywords": [
17 | "notification",
18 | "android",
19 | "local",
20 | "simple"
21 | ],
22 | "author": "Layman ",
23 | "contributors": [
24 | "Andre Staltz "
25 | ],
26 | "license": "MIT",
27 | "bugs": {
28 | "url": "https://github.com/staltz/react-native-android-local-notification/issues"
29 | },
30 | "homepage": "https://github.com/staltz/react-native-android-local-notification#readme"
31 | }
--------------------------------------------------------------------------------