├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── android ├── build.gradle └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── remobile │ └── localNotifications │ ├── ClearReceiver.java │ ├── ClickActivity.java │ ├── LocalNotification.java │ ├── RCTLocalNotificationsPackage.java │ ├── RestoreReceiver.java │ ├── TriggerReceiver.java │ └── notification │ ├── AbstractClearReceiver.java │ ├── AbstractClickActivity.java │ ├── AbstractRestoreReceiver.java │ ├── AbstractTriggerReceiver.java │ ├── AssetUtil.java │ ├── Builder.java │ ├── ClearReceiver.java │ ├── ClickActivity.java │ ├── Manager.java │ ├── Notification.java │ ├── Options.java │ └── TriggerReceiver.java ├── index.js ├── ios ├── RCTLocalNotifications.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata └── RCTLocalNotifications │ ├── APPLocalNotification.h │ ├── APPLocalNotification.m │ ├── APPLocalNotificationOptions.h │ ├── APPLocalNotificationOptions.m │ ├── AppDelegate+APPRegisterUserNotificationSettings.h │ ├── AppDelegate+APPRegisterUserNotificationSettings.m │ ├── UIApplication+APPLocalNotification.h │ ├── UIApplication+APPLocalNotification.m │ ├── UILocalNotification+APPLocalNotification.h │ └── UILocalNotification+APPLocalNotification.m ├── libs ├── local-notification-core.js ├── local-notification-util.js └── local-notification.js ├── package.json └── screencasts ├── 1.png └── 2.png /.gitignore: -------------------------------------------------------------------------------- 1 | *.[aod] 2 | *.DS_Store 3 | .DS_Store 4 | *Thumbs.db 5 | *.iml 6 | .gradle 7 | .idea 8 | node_modules 9 | npm-debug.log 10 | /android/build 11 | /ios/**/*xcuserdata* 12 | /ios/**/*xcshareddata* 13 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | .DS_Store 3 | *Thumbs.db 4 | .gradle 5 | .idea 6 | *.iml 7 | npm-debug.log 8 | node_modules 9 | /android/build 10 | /ios/**/*xcuserdata* 11 | /ios/**/*xcshareddata* 12 | 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2015-2016 YunJiang.Fang <42550564@qq.com> 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # React Native LocalNotifications (remobile) 2 | A cordova local-notifications for react-native, supprt for ios and android 3 | 4 | ## Installation 5 | ```sh 6 | npm install @remobile/react-native-local-notifications --save 7 | ``` 8 | ### Installation (iOS) 9 | * Drag RCTLocalNotifications.xcodeproj to your project on Xcode. 10 | * Click on your main project file (the one that represents the .xcodeproj) select Build Phases and drag libRCTLocalNotifications.a from the Products folder inside the RCTLocalNotifications.xcodeproj. 11 | * Look for Header Search Paths and make sure it contains $(SRCROOT)/../../../react-native/React as recursive. 12 | * Look for Header Search Paths and make sure it contains $(SRCROOT)/../../react-native-cordova/ios/RCTCordova. 13 | * Look for Header Search Paths and make sure it contains $(SRCROOT)/../../../../ios/${your main project}. 14 | 15 | * register didReceiveLocalNotification (in AppDelegate.m) 16 | 17 | ```obj-c 18 | // repost all remote and local notification using the default NSNotificationCenter so multiple plugins may respond 19 | - (void) application:(UIApplication*)application 20 | didReceiveLocalNotification:(UILocalNotification*)notification 21 | { 22 | // re-post ( broadcast ) 23 | [[NSNotificationCenter defaultCenter] postNotificationName:@"CDVLocalNotification" object:notification]; 24 | } 25 | ``` 26 | 27 | ### Installation (Android) 28 | ```gradle 29 | ... 30 | include ':react-native-local-notifications' 31 | project(':react-native-local-notifications').projectDir = new File(settingsDir, '../node_modules/@remobile/react-native-local-notifications/android') 32 | ``` 33 | 34 | * In `android/app/build.gradle` 35 | 36 | ```gradle 37 | ... 38 | dependencies { 39 | ... 40 | compile project(':react-native-local-notifications') 41 | } 42 | ``` 43 | 44 | * register module (in MainApplication.java) 45 | 46 | ```java 47 | ...... 48 | import com.remobile.localNotifications.RCTLocalNotificationsPackage; // <--- import 49 | 50 | ...... 51 | 52 | @Override 53 | protected List getPackages() { 54 | ...... 55 | new RCTLocalNotificationsPackage(), // <------ add here 56 | ...... 57 | } 58 | 59 | ``` 60 | 61 | ### Screencasts 62 | ![image](https://github.com/remobile/react-native-local-notifications/blob/master/screencasts/1.png) 63 | ![image](https://github.com/remobile/react-native-local-notifications/blob/master/screencasts/2.png) 64 | 65 | ## Usage 66 | 67 | ### Example 68 | ```js 69 | 'use strict'; 70 | 71 | var React = require('react'); 72 | var ReactNative = require('react-native'); 73 | var { 74 | AppRegistry, 75 | StyleSheet, 76 | Text, 77 | View, 78 | } = ReactNative; 79 | 80 | 81 | var Button = require('@remobile/react-native-simple-button'); 82 | var LocalNotification = require('@remobile/react-native-local-notifications'); 83 | 84 | module.exports = React.createClass({ 85 | test() { 86 | var now = new Date().getTime(), 87 | _n_sec_from_now = new Date(now + 10*1000); 88 | 89 | LocalNotification.schedule({ 90 | id: 10, 91 | title: "Meeting in 15 minutes!", 92 | text: "Jour fixe Produktionsbesprechung", 93 | at: _n_sec_from_now, 94 | data: { meetingId:"#123FG8" } 95 | }); 96 | LocalNotification.on("click", function (notification) { 97 | if (notification.id == 10) { 98 | joinMeeting(notification.data.meetingId); 99 | } 100 | }); 101 | 102 | // Notification has reached its trigger time (Tomorrow at 8:45 AM) 103 | LocalNotification.on("trigger", function (notification) { 104 | if (notification.id != 10) 105 | return; 106 | // After 5 seconds update notification's title 107 | setTimeout(function () { 108 | LocalNotification.update({ 109 | id: 10, 110 | title: "Meeting in 10 minutes!" 111 | }); 112 | }, 5000); 113 | }); 114 | }, 115 | render: function() { 116 | return ( 117 | 118 | 121 | 122 | ); 123 | } 124 | }); 125 | 126 | var styles = StyleSheet.create({ 127 | container: { 128 | flex: 1, 129 | justifyContent: 'center', 130 | alignItems: 'center', 131 | backgroundColor: '#fff', 132 | paddingVertical: 20, 133 | }, 134 | }); 135 | ``` 136 | 137 | ### HELP 138 | * look https://github.com/katzer/cordova-plugin-local-notifications/wiki 139 | 140 | 141 | ### thanks 142 | * this project come from https://github.com/katzer/cordova-plugin-local-notifications 143 | 144 | ### see detail use 145 | * https://github.com/remobile/react-native-template 146 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 22 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | sourceSets { 20 | main { 21 | jniLibs.srcDirs = ['libs'] 22 | } 23 | } 24 | } 25 | 26 | dependencies { 27 | compile fileTree(dir: 'libs', include: ['*.jar']) 28 | compile 'com.android.support:appcompat-v7:23.0.1' 29 | compile 'com.facebook.react:react-native:+' 30 | compile project(':react-native-cordova') 31 | } 32 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/ClearReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications; 25 | 26 | import com.remobile.localNotifications.notification.Notification; 27 | 28 | 29 | /** 30 | * The clear intent receiver is triggered when the user clears a 31 | * notification manually. It un-persists the cleared notification from the 32 | * shared preferences. 33 | */ 34 | public class ClearReceiver extends com.remobile.localNotifications.notification.ClearReceiver { 35 | 36 | /** 37 | * Called when a local notification was cleared from outside of the app. 38 | * 39 | * @param notification 40 | * Wrapper around the local notification 41 | */ 42 | @Override 43 | public void onClear (Notification notification) { 44 | super.onClear(notification); 45 | LocalNotification.fireEvent("clear", notification); 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/ClickActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications; 25 | 26 | import com.remobile.localNotifications.notification.Builder; 27 | import com.remobile.localNotifications.notification.Notification; 28 | import com.remobile.localNotifications.notification.TriggerReceiver; 29 | 30 | /** 31 | * The receiver activity is triggered when a notification is clicked by a user. 32 | * The activity calls the background callback and brings the launch intent 33 | * up to foreground. 34 | */ 35 | public class ClickActivity extends com.remobile.localNotifications.notification.ClickActivity { 36 | 37 | /** 38 | * Called when local notification was clicked by the user. 39 | * 40 | * @param notification 41 | * Wrapper around the local notification 42 | */ 43 | @Override 44 | public void onClick(Notification notification) { 45 | LocalNotification.fireEvent("click", notification); 46 | 47 | super.onClick(notification); 48 | 49 | if (notification.getOptions().isOngoing()) 50 | return; 51 | 52 | String event = notification.isRepeating() ? "clear" : "cancel"; 53 | LocalNotification.fireEvent(event, notification); 54 | } 55 | 56 | /** 57 | * Build notification specified by options. 58 | * 59 | * @param builder 60 | * Notification builder 61 | */ 62 | @Override 63 | public Notification buildNotification (Builder builder) { 64 | return builder 65 | .setTriggerReceiver(TriggerReceiver.class) 66 | .build(); 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/LocalNotification.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications; 25 | 26 | import android.app.Activity; 27 | 28 | import org.json.JSONArray; 29 | import org.json.JSONException; 30 | import org.json.JSONObject; 31 | 32 | import java.util.ArrayList; 33 | import java.util.HashMap; 34 | import java.util.List; 35 | import java.util.Map; 36 | 37 | import com.facebook.common.logging.FLog; 38 | import com.facebook.react.modules.core.DeviceEventManagerModule; 39 | import com.remobile.localNotifications.notification.Manager; 40 | import com.remobile.localNotifications.notification.Notification; 41 | import com.remobile.cordova.*; 42 | import com.facebook.react.bridge.*; 43 | 44 | /** 45 | * This plugin utilizes the Android AlarmManager in combination with local 46 | * notifications. When a local notification is scheduled the alarm manager takes 47 | * care of firing the event. When the event is processed, a notification is put 48 | * in the Android notification center and status bar. 49 | */ 50 | public class LocalNotification extends CordovaPlugin { 51 | private static final String LOG_TAG = "LocalNotification"; 52 | private static LocalNotification self = null; 53 | 54 | // Indicates if the device is ready (to receive events) 55 | private static Boolean deviceready = false; 56 | 57 | // To inform the user about the state of the app in callbacks 58 | protected static Boolean isInBackground = true; 59 | 60 | // Queues all events before deviceready 61 | private static Map eventQueue = new HashMap(); 62 | 63 | /** 64 | * Constructor. 65 | */ 66 | public LocalNotification(ReactApplicationContext reactContext) { 67 | super(reactContext); 68 | self = this; 69 | } 70 | 71 | @Override 72 | public String getName() { 73 | return "LocalNotification"; 74 | } 75 | 76 | @ReactMethod 77 | public void schedule(ReadableArray args, Callback success, Callback error) { 78 | executeReactMethod("schedule", args, success, error); 79 | } 80 | @ReactMethod 81 | public void update(ReadableArray args, Callback success, Callback error) { 82 | executeReactMethod("update", args, success, error); 83 | } 84 | @ReactMethod 85 | public void cancel(ReadableArray args, Callback success, Callback error) { 86 | executeReactMethod("cancel", args, success, error); 87 | } 88 | @ReactMethod 89 | public void cancelAll(ReadableArray args, Callback success, Callback error) { 90 | executeReactMethod("cancelAll", args, success, error); 91 | } 92 | @ReactMethod 93 | public void clear(ReadableArray args, Callback success, Callback error) { 94 | executeReactMethod("clear", args, success, error); 95 | } 96 | @ReactMethod 97 | public void clearAll(ReadableArray args, Callback success, Callback error) { 98 | executeReactMethod("clearAll", args, success, error); 99 | } 100 | @ReactMethod 101 | public void isPresent(ReadableArray args, Callback success, Callback error) { 102 | executeReactMethod("isPresent", args, success, error); 103 | } 104 | @ReactMethod 105 | public void isScheduled(ReadableArray args, Callback success, Callback error) { 106 | executeReactMethod("isScheduled", args, success, error); 107 | } 108 | @ReactMethod 109 | public void isTriggered(ReadableArray args, Callback success, Callback error) { 110 | executeReactMethod("isTriggered", args, success, error); 111 | } 112 | @ReactMethod 113 | public void getAllIds(ReadableArray args, Callback success, Callback error) { 114 | executeReactMethod("getAllIds", args, success, error); 115 | } 116 | @ReactMethod 117 | public void getScheduledIds(ReadableArray args, Callback success, Callback error) { 118 | executeReactMethod("getScheduledIds", args, success, error); 119 | } 120 | @ReactMethod 121 | public void getTriggeredIds(ReadableArray args, Callback success, Callback error) { 122 | executeReactMethod("getTriggeredIds", args, success, error); 123 | } 124 | @ReactMethod 125 | public void getSingle(ReadableArray args, Callback success, Callback error) { 126 | executeReactMethod("getSingle", args, success, error); 127 | } 128 | @ReactMethod 129 | public void getSingleScheduled(ReadableArray args, Callback success, Callback error) { 130 | executeReactMethod("getSingleScheduled", args, success, error); 131 | } 132 | @ReactMethod 133 | public void getSingleTriggered(ReadableArray args, Callback success, Callback error) { 134 | executeReactMethod("getSingleTriggered", args, success, error); 135 | } 136 | @ReactMethod 137 | public void getAll(ReadableArray args, Callback success, Callback error) { 138 | executeReactMethod("getAll", args, success, error); 139 | } 140 | @ReactMethod 141 | public void getScheduled(ReadableArray args, Callback success, Callback error) { 142 | executeReactMethod("getScheduled", args, success, error); 143 | } 144 | @ReactMethod 145 | public void getTriggered(ReadableArray args, Callback success, Callback error) { 146 | executeReactMethod("getTriggered", args, success, error); 147 | } 148 | @ReactMethod 149 | public void deviceready(ReadableArray args, Callback success, Callback error) { 150 | executeReactMethod("deviceready", args, success, error); 151 | } 152 | 153 | /** 154 | * Called when the system is about to start resuming a previous activity. 155 | * 156 | * Flag indicating if multitasking is turned on for app 157 | */ 158 | @Override 159 | public void onPause() { 160 | isInBackground = true; 161 | } 162 | 163 | /** 164 | * Called when the activity will start interacting with the user. 165 | * 166 | * Flag indicating if multitasking is turned on for app 167 | */ 168 | @Override 169 | public void onResume() { 170 | isInBackground = false; 171 | deviceready(); 172 | } 173 | 174 | /** 175 | * The final call you receive before your activity is destroyed. 176 | */ 177 | @Override 178 | public void onDestroy() { 179 | deviceready = false; 180 | isInBackground = true; 181 | } 182 | 183 | /** 184 | * Executes the request. 185 | * 186 | * This method is called from the WebView thread. To do a non-trivial 187 | * amount of work, use: 188 | * cordova.getThreadPool().execute(runnable); 189 | * 190 | * To run on the UI thread, use: 191 | * cordova.getActivity().runOnUiThread(runnable); 192 | * 193 | * @param action 194 | * The action to execute. 195 | * @param args 196 | * The exec() arguments in JSON form. 197 | * @param command 198 | * The callback context used when calling back into JavaScript. 199 | * @return 200 | * Whether the action was valid. 201 | */ 202 | public boolean execute (final String action, final JSONArray args, 203 | final CallbackContext command) throws JSONException { 204 | 205 | Notification.setDefaultTriggerReceiver(TriggerReceiver.class); 206 | 207 | cordova.getThreadPool().execute(new Runnable() { 208 | public void run() { 209 | if (action.equals("schedule")) { 210 | schedule(args); 211 | command.success(); 212 | } 213 | else if (action.equals("update")) { 214 | update(args); 215 | command.success(); 216 | } 217 | else if (action.equals("cancel")) { 218 | cancel(args); 219 | command.success(); 220 | } 221 | else if (action.equals("cancelAll")) { 222 | cancelAll(); 223 | command.success(); 224 | } 225 | else if (action.equals("clear")) { 226 | clear(args); 227 | command.success(); 228 | } 229 | else if (action.equals("clearAll")) { 230 | clearAll(); 231 | command.success(); 232 | } 233 | else if (action.equals("isPresent")) { 234 | isPresent(args.optInt(0), command); 235 | } 236 | else if (action.equals("isScheduled")) { 237 | isScheduled(args.optInt(0), command); 238 | } 239 | else if (action.equals("isTriggered")) { 240 | isTriggered(args.optInt(0), command); 241 | } 242 | else if (action.equals("getAllIds")) { 243 | getAllIds(command); 244 | } 245 | else if (action.equals("getScheduledIds")) { 246 | getScheduledIds(command); 247 | } 248 | else if (action.equals("getTriggeredIds")) { 249 | getTriggeredIds(command); 250 | } 251 | else if (action.equals("getSingle")) { 252 | getSingle(args, command); 253 | } 254 | else if (action.equals("getSingleScheduled")) { 255 | getSingleScheduled(args, command); 256 | } 257 | else if (action.equals("getSingleTriggered")) { 258 | getSingleTriggered(args, command); 259 | } 260 | else if (action.equals("getAll")) { 261 | getAll(args, command); 262 | } 263 | else if (action.equals("getScheduled")) { 264 | getScheduled(args, command); 265 | } 266 | else if (action.equals("getTriggered")) { 267 | getTriggered(args, command); 268 | } 269 | else if (action.equals("deviceready")) { 270 | deviceready(); 271 | } 272 | } 273 | }); 274 | 275 | return true; 276 | } 277 | 278 | /** 279 | * Schedule multiple local notifications. 280 | * 281 | * @param notifications 282 | * Properties for each local notification 283 | */ 284 | private void schedule (JSONArray notifications) { 285 | for (int i = 0; i < notifications.length(); i++) { 286 | JSONObject options = notifications.optJSONObject(i); 287 | 288 | Notification notification = 289 | getNotificationMgr().schedule(options, TriggerReceiver.class); 290 | 291 | fireEvent("schedule", notification); 292 | } 293 | } 294 | 295 | /** 296 | * Update multiple local notifications. 297 | * 298 | * @param updates 299 | * Notification properties including their IDs 300 | */ 301 | private void update (JSONArray updates) { 302 | for (int i = 0; i < updates.length(); i++) { 303 | JSONObject update = updates.optJSONObject(i); 304 | int id = update.optInt("id", 0); 305 | 306 | Notification notification = 307 | getNotificationMgr().update(id, update, TriggerReceiver.class); 308 | 309 | if (notification == null) 310 | continue; 311 | 312 | fireEvent("update", notification); 313 | } 314 | } 315 | 316 | /** 317 | * Cancel multiple local notifications. 318 | * 319 | * @param ids 320 | * Set of local notification IDs 321 | */ 322 | private void cancel (JSONArray ids) { 323 | for (int i = 0; i < ids.length(); i++) { 324 | int id = ids.optInt(i, 0); 325 | 326 | Notification notification = 327 | getNotificationMgr().cancel(id); 328 | 329 | if (notification == null) 330 | continue; 331 | 332 | fireEvent("cancel", notification); 333 | } 334 | } 335 | 336 | /** 337 | * Cancel all scheduled notifications. 338 | */ 339 | private void cancelAll() { 340 | getNotificationMgr().cancelAll(); 341 | fireEvent("cancelall"); 342 | } 343 | 344 | /** 345 | * Clear multiple local notifications without canceling them. 346 | * 347 | * @param ids 348 | * Set of local notification IDs 349 | */ 350 | private void clear(JSONArray ids){ 351 | for (int i = 0; i < ids.length(); i++) { 352 | int id = ids.optInt(i, 0); 353 | 354 | Notification notification = 355 | getNotificationMgr().clear(id); 356 | 357 | if (notification == null) 358 | continue; 359 | 360 | fireEvent("clear", notification); 361 | } 362 | } 363 | 364 | /** 365 | * Clear all triggered notifications without canceling them. 366 | */ 367 | private void clearAll() { 368 | getNotificationMgr().clearAll(); 369 | fireEvent("clearall"); 370 | } 371 | 372 | /** 373 | * If a notification with an ID is present. 374 | * 375 | * @param id 376 | * Notification ID 377 | * @param command 378 | * The callback context used when calling back into JavaScript. 379 | */ 380 | private void isPresent (int id, CallbackContext command) { 381 | boolean exist = getNotificationMgr().exist(id); 382 | 383 | PluginResult result = new PluginResult( 384 | PluginResult.Status.OK, exist); 385 | 386 | command.sendPluginResult(result); 387 | } 388 | 389 | /** 390 | * If a notification with an ID is scheduled. 391 | * 392 | * @param id 393 | * Notification ID 394 | * @param command 395 | * The callback context used when calling back into JavaScript. 396 | */ 397 | private void isScheduled (int id, CallbackContext command) { 398 | boolean exist = getNotificationMgr().exist( 399 | id, Notification.Type.SCHEDULED); 400 | 401 | PluginResult result = new PluginResult( 402 | PluginResult.Status.OK, exist); 403 | 404 | command.sendPluginResult(result); 405 | } 406 | 407 | /** 408 | * If a notification with an ID is triggered. 409 | * 410 | * @param id 411 | * Notification ID 412 | * @param command 413 | * The callback context used when calling back into JavaScript. 414 | */ 415 | private void isTriggered (int id, CallbackContext command) { 416 | boolean exist = getNotificationMgr().exist( 417 | id, Notification.Type.TRIGGERED); 418 | 419 | PluginResult result = new PluginResult( 420 | PluginResult.Status.OK, exist); 421 | 422 | command.sendPluginResult(result); 423 | } 424 | 425 | /** 426 | * Set of IDs from all existent notifications. 427 | * 428 | * @param command 429 | * The callback context used when calling back into JavaScript. 430 | */ 431 | private void getAllIds (CallbackContext command) { 432 | List ids = getNotificationMgr().getIds(); 433 | 434 | command.success(new JSONArray(ids)); 435 | } 436 | 437 | /** 438 | * Set of IDs from all scheduled notifications. 439 | * 440 | * @param command 441 | * The callback context used when calling back into JavaScript. 442 | */ 443 | private void getScheduledIds (CallbackContext command) { 444 | List ids = getNotificationMgr().getIdsByType( 445 | Notification.Type.SCHEDULED); 446 | 447 | command.success(new JSONArray(ids)); 448 | } 449 | 450 | /** 451 | * Set of IDs from all triggered notifications. 452 | * 453 | * @param command 454 | * The callback context used when calling back into JavaScript. 455 | */ 456 | private void getTriggeredIds (CallbackContext command) { 457 | List ids = getNotificationMgr().getIdsByType( 458 | Notification.Type.TRIGGERED); 459 | 460 | command.success(new JSONArray(ids)); 461 | } 462 | 463 | /** 464 | * Options from local notification. 465 | * 466 | * @param ids 467 | * Set of local notification IDs 468 | * @param command 469 | * The callback context used when calling back into JavaScript. 470 | */ 471 | private void getSingle (JSONArray ids, CallbackContext command) { 472 | getOptions(ids.optString(0), Notification.Type.ALL, command); 473 | } 474 | 475 | /** 476 | * Options from scheduled notification. 477 | * 478 | * @param ids 479 | * Set of local notification IDs 480 | * @param command 481 | * The callback context used when calling back into JavaScript. 482 | */ 483 | private void getSingleScheduled (JSONArray ids, CallbackContext command) { 484 | getOptions(ids.optString(0), Notification.Type.SCHEDULED, command); 485 | } 486 | 487 | /** 488 | * Options from triggered notification. 489 | * 490 | * @param ids 491 | * Set of local notification IDs 492 | * @param command 493 | * The callback context used when calling back into JavaScript. 494 | */ 495 | private void getSingleTriggered (JSONArray ids, CallbackContext command) { 496 | getOptions(ids.optString(0), Notification.Type.TRIGGERED, command); 497 | } 498 | 499 | /** 500 | * Set of options from local notification. 501 | * 502 | * @param ids 503 | * Set of local notification IDs 504 | * @param command 505 | * The callback context used when calling back into JavaScript. 506 | */ 507 | private void getAll (JSONArray ids, CallbackContext command) { 508 | getOptions(ids, Notification.Type.ALL, command); 509 | } 510 | 511 | /** 512 | * Set of options from scheduled notifications. 513 | * 514 | * @param ids 515 | * Set of local notification IDs 516 | * @param command 517 | * The callback context used when calling back into JavaScript. 518 | */ 519 | private void getScheduled (JSONArray ids, CallbackContext command) { 520 | getOptions(ids, Notification.Type.SCHEDULED, command); 521 | } 522 | 523 | /** 524 | * Set of options from triggered notifications. 525 | * 526 | * @param ids 527 | * Set of local notification IDs 528 | * @param command 529 | * The callback context used when calling back into JavaScript. 530 | */ 531 | private void getTriggered (JSONArray ids, CallbackContext command) { 532 | getOptions(ids, Notification.Type.TRIGGERED, command); 533 | } 534 | 535 | /** 536 | * Options from local notification. 537 | * 538 | * @param id 539 | * Set of local notification IDs 540 | * @param type 541 | * The local notification life cycle type 542 | * @param command 543 | * The callback context used when calling back into JavaScript. 544 | */ 545 | private void getOptions (String id, Notification.Type type, 546 | CallbackContext command) { 547 | 548 | JSONArray ids = new JSONArray().put(id); 549 | PluginResult result; 550 | 551 | List options = 552 | getNotificationMgr().getOptionsBy(type, toList(ids)); 553 | 554 | if (options.isEmpty()) { 555 | // Status.NO_RESULT led to no callback invocation :( 556 | // Status.OK led to no NPE and crash 557 | result = new PluginResult(PluginResult.Status.NO_RESULT); 558 | } else { 559 | result = new PluginResult(PluginResult.Status.OK, options.get(0)); 560 | } 561 | 562 | command.sendPluginResult(result); 563 | } 564 | 565 | /** 566 | * Set of options from local notifications. 567 | * 568 | * @param ids 569 | * Set of local notification IDs 570 | * @param type 571 | * The local notification life cycle type 572 | * @param command 573 | * The callback context used when calling back into JavaScript. 574 | */ 575 | private void getOptions (JSONArray ids, Notification.Type type, 576 | CallbackContext command) { 577 | 578 | List options; 579 | 580 | if (ids.length() == 0) { 581 | options = getNotificationMgr().getOptionsByType(type); 582 | } else { 583 | options = getNotificationMgr().getOptionsBy(type, toList(ids)); 584 | } 585 | 586 | command.success(new JSONArray(options)); 587 | } 588 | 589 | /** 590 | * Call all pending callbacks after the deviceready event has been fired. 591 | */ 592 | private static synchronized void deviceready () { 593 | isInBackground = false; 594 | deviceready = true; 595 | 596 | for (Map.Entry entry : eventQueue.entrySet()) { 597 | sendJavascript(entry.getKey(), entry.getValue()); 598 | } 599 | 600 | eventQueue.clear(); 601 | } 602 | 603 | /** 604 | * Fire given event on JS side. Does inform all event listeners. 605 | * 606 | * @param event 607 | * The event name 608 | */ 609 | private void fireEvent (String event) { 610 | fireEvent(event, null); 611 | } 612 | 613 | /** 614 | * Fire given event on JS side. Does inform all event listeners. 615 | * 616 | * @param event 617 | * The event name 618 | * @param notification 619 | * Optional local notification to pass the id and properties. 620 | */ 621 | static void fireEvent (String event, Notification notification) { 622 | String state = getApplicationState(); 623 | String params = "\"" + state + "\""; 624 | 625 | if (notification != null) { 626 | params = notification.toString() + "," + params; 627 | } 628 | 629 | sendJavascript(event, params); 630 | } 631 | 632 | /** 633 | * Use this instead of deprecated sendJavascript 634 | * 635 | * @param event,params 636 | * JS code snippet as string 637 | */ 638 | private static synchronized void sendJavascript(final String event, final String params) { 639 | 640 | if (!deviceready) { 641 | eventQueue.put(event, params); 642 | return; 643 | } 644 | self.getReactApplicationContext().getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class).emit("RCT-LOCAL-NOTIFICATION-"+event, params); 645 | } 646 | 647 | /** 648 | * Convert JSON array of integers to List. 649 | * 650 | * @param ary 651 | * Array of integers 652 | */ 653 | private List toList (JSONArray ary) { 654 | ArrayList list = new ArrayList(); 655 | 656 | for (int i = 0; i < ary.length(); i++) { 657 | list.add(ary.optInt(i)); 658 | } 659 | 660 | return list; 661 | } 662 | 663 | /** 664 | * Current application state. 665 | * 666 | * @return 667 | * "background" or "foreground" 668 | */ 669 | static String getApplicationState () { 670 | return isInBackground ? "background" : "foreground"; 671 | } 672 | 673 | /** 674 | * Notification manager instance. 675 | */ 676 | private Manager getNotificationMgr() { 677 | return Manager.getInstance(cordova.getActivity()); 678 | } 679 | } 680 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/RCTLocalNotificationsPackage.java: -------------------------------------------------------------------------------- 1 | package com.remobile.localNotifications; 2 | 3 | import java.util.Arrays; 4 | import java.util.Collections; 5 | import java.util.List; 6 | 7 | import com.facebook.react.ReactPackage; 8 | import com.facebook.react.bridge.JavaScriptModule; 9 | import com.facebook.react.bridge.NativeModule; 10 | import com.facebook.react.bridge.ReactApplicationContext; 11 | import com.facebook.react.uimanager.ViewManager; 12 | 13 | public class RCTLocalNotificationsPackage implements ReactPackage { 14 | @Override 15 | public List createNativeModules(ReactApplicationContext reactContext) { 16 | return Arrays.asList( 17 | new LocalNotification(reactContext) 18 | ); 19 | } 20 | 21 | public List> createJSModules() { 22 | return Collections.emptyList(); 23 | } 24 | 25 | @Override 26 | public List createViewManagers(ReactApplicationContext reactContext) { 27 | return Arrays.asList(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/RestoreReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications; 25 | 26 | import com.remobile.localNotifications.notification.AbstractRestoreReceiver; 27 | import com.remobile.localNotifications.notification.Builder; 28 | import com.remobile.localNotifications.notification.Notification; 29 | 30 | /** 31 | * This class is triggered upon reboot of the device. It needs to re-register 32 | * the alarms with the AlarmManager since these alarms are lost in case of 33 | * reboot. 34 | */ 35 | public class RestoreReceiver extends AbstractRestoreReceiver { 36 | 37 | /** 38 | * Called when a local notification need to be restored. 39 | * 40 | * @param notification 41 | * Wrapper around the local notification 42 | */ 43 | @Override 44 | public void onRestore (Notification notification) { 45 | if (notification.isScheduled()) { 46 | notification.schedule(); 47 | } 48 | } 49 | 50 | /** 51 | * Build notification specified by options. 52 | * 53 | * @param builder 54 | * Notification builder 55 | */ 56 | @Override 57 | public Notification buildNotification (Builder builder) { 58 | return builder 59 | .setTriggerReceiver(TriggerReceiver.class) 60 | .setClearReceiver(ClearReceiver.class) 61 | .setClickActivity(ClickActivity.class) 62 | .build(); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/TriggerReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications; 25 | 26 | import com.remobile.localNotifications.notification.Builder; 27 | import com.remobile.localNotifications.notification.Notification; 28 | 29 | /** 30 | * The alarm receiver is triggered when a scheduled alarm is fired. This class 31 | * reads the information in the intent and displays this information in the 32 | * Android notification bar. The notification uses the default notification 33 | * sound and it vibrates the phone. 34 | */ 35 | public class TriggerReceiver extends com.remobile.localNotifications.notification.TriggerReceiver { 36 | 37 | /** 38 | * Called when a local notification was triggered. Does present the local 39 | * notification, re-schedule the alarm if necessary and fire trigger event. 40 | * 41 | * @param notification 42 | * Wrapper around the local notification 43 | * @param updated 44 | * If an update has triggered or the original 45 | */ 46 | @Override 47 | public void onTrigger (Notification notification, boolean updated) { 48 | super.onTrigger(notification, updated); 49 | 50 | if (!updated) { 51 | LocalNotification.fireEvent("trigger", notification); 52 | } 53 | } 54 | 55 | /** 56 | * Build notification specified by options. 57 | * 58 | * @param builder 59 | * Notification builder 60 | */ 61 | @Override 62 | public Notification buildNotification (Builder builder) { 63 | return builder 64 | .setTriggerReceiver(TriggerReceiver.class) 65 | .setClickActivity(ClickActivity.class) 66 | .setClearReceiver(ClearReceiver.class) 67 | .build(); 68 | } 69 | 70 | } 71 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/notification/AbstractClearReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications.notification; 25 | 26 | import android.content.BroadcastReceiver; 27 | import android.content.Context; 28 | import android.content.Intent; 29 | import android.os.Bundle; 30 | 31 | import org.json.JSONException; 32 | import org.json.JSONObject; 33 | 34 | /** 35 | * Abstract delete receiver for local notifications. Creates the local 36 | * notification and calls the event functions for further proceeding. 37 | */ 38 | abstract public class AbstractClearReceiver extends BroadcastReceiver { 39 | 40 | /** 41 | * Called when the notification was cleared from the notification center. 42 | * 43 | * @param context 44 | * Application context 45 | * @param intent 46 | * Received intent with content data 47 | */ 48 | @Override 49 | public void onReceive(Context context, Intent intent) { 50 | Bundle bundle = intent.getExtras(); 51 | JSONObject options; 52 | 53 | try { 54 | String data = bundle.getString(Options.EXTRA); 55 | options = new JSONObject(data); 56 | } catch (JSONException e) { 57 | e.printStackTrace(); 58 | return; 59 | } 60 | 61 | Notification notification = 62 | new Builder(context, options).build(); 63 | 64 | onClear(notification); 65 | } 66 | 67 | /** 68 | * Called when a local notification was cleared from outside of the app. 69 | * 70 | * @param notification 71 | * Wrapper around the local notification 72 | */ 73 | abstract public void onClear (Notification notification); 74 | 75 | } 76 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/notification/AbstractClickActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications.notification; 25 | 26 | import android.app.Activity; 27 | import android.content.Context; 28 | import android.content.Intent; 29 | import android.os.Bundle; 30 | 31 | import org.json.JSONException; 32 | import org.json.JSONObject; 33 | 34 | /** 35 | * Abstract content receiver activity for local notifications. Creates the 36 | * local notification and calls the event functions for further proceeding. 37 | */ 38 | abstract public class AbstractClickActivity extends Activity { 39 | 40 | /** 41 | * Called when local notification was clicked to launch the main intent. 42 | * 43 | * @param state 44 | * Saved instance state 45 | */ 46 | @Override 47 | public void onCreate (Bundle state) { 48 | super.onCreate(state); 49 | 50 | Intent intent = getIntent(); 51 | Bundle bundle = intent.getExtras(); 52 | Context context = getApplicationContext(); 53 | 54 | try { 55 | String data = bundle.getString(Options.EXTRA); 56 | JSONObject options = new JSONObject(data); 57 | 58 | Builder builder = 59 | new Builder(context, options); 60 | 61 | Notification notification = 62 | buildNotification(builder); 63 | 64 | onClick(notification); 65 | } catch (JSONException e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | 70 | /** 71 | * Fixes "Unable to resume activity" error. 72 | * Theme_NoDisplay: Activities finish themselves before being resumed. 73 | */ 74 | @Override 75 | protected void onResume() { 76 | super.onResume(); 77 | finish(); 78 | } 79 | 80 | /** 81 | * Called when local notification was clicked by the user. 82 | * 83 | * @param notification 84 | * Wrapper around the local notification 85 | */ 86 | abstract public void onClick (Notification notification); 87 | 88 | /** 89 | * Build notification specified by options. 90 | * 91 | * @param builder 92 | * Notification builder 93 | */ 94 | abstract public Notification buildNotification (Builder builder); 95 | 96 | /** 97 | * Launch main intent from package. 98 | */ 99 | public void launchApp() { 100 | Context context = getApplicationContext(); 101 | String pkgName = context.getPackageName(); 102 | 103 | Intent intent = context 104 | .getPackageManager() 105 | .getLaunchIntentForPackage(pkgName); 106 | 107 | intent.addFlags( 108 | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT | Intent.FLAG_ACTIVITY_SINGLE_TOP); 109 | 110 | context.startActivity(intent); 111 | } 112 | 113 | } 114 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/notification/AbstractRestoreReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications.notification; 25 | 26 | import android.content.BroadcastReceiver; 27 | import android.content.Context; 28 | import android.content.Intent; 29 | 30 | import org.json.JSONObject; 31 | 32 | import java.util.List; 33 | 34 | /** 35 | * This class is triggered upon reboot of the device. It needs to re-register 36 | * the alarms with the AlarmManager since these alarms are lost in case of 37 | * reboot. 38 | */ 39 | abstract public class AbstractRestoreReceiver extends BroadcastReceiver { 40 | 41 | /** 42 | * Called on device reboot. 43 | * 44 | * @param context 45 | * Application context 46 | * @param intent 47 | * Received intent with content data 48 | */ 49 | @Override 50 | public void onReceive (Context context, Intent intent) { 51 | Manager notificationMgr = 52 | Manager.getInstance(context); 53 | 54 | List options = 55 | notificationMgr.getOptions(); 56 | 57 | for (JSONObject data : options) { 58 | Builder builder = new Builder(context, data); 59 | 60 | Notification notification = 61 | buildNotification(builder); 62 | 63 | onRestore(notification); 64 | } 65 | } 66 | 67 | /** 68 | * Called when a local notification need to be restored. 69 | * 70 | * @param notification 71 | * Wrapper around the local notification 72 | */ 73 | abstract public void onRestore (Notification notification); 74 | 75 | /** 76 | * Build notification specified by options. 77 | * 78 | * @param builder 79 | * Notification builder 80 | */ 81 | abstract public Notification buildNotification (Builder builder); 82 | 83 | } 84 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/notification/AbstractTriggerReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications.notification; 25 | 26 | import android.content.BroadcastReceiver; 27 | import android.content.Context; 28 | import android.content.Intent; 29 | import android.os.Bundle; 30 | 31 | import org.json.JSONException; 32 | import org.json.JSONObject; 33 | 34 | import java.util.Calendar; 35 | 36 | /** 37 | * Abstract broadcast receiver for local notifications. Creates the 38 | * notification options and calls the event functions for further proceeding. 39 | */ 40 | abstract public class AbstractTriggerReceiver extends BroadcastReceiver { 41 | 42 | /** 43 | * Called when an alarm was triggered. 44 | * 45 | * @param context 46 | * Application context 47 | * @param intent 48 | * Received intent with content data 49 | */ 50 | @Override 51 | public void onReceive(Context context, Intent intent) { 52 | Bundle bundle = intent.getExtras(); 53 | Options options; 54 | 55 | try { 56 | String data = bundle.getString(Options.EXTRA); 57 | JSONObject dict = new JSONObject(data); 58 | 59 | options = new Options(context).parse(dict); 60 | } catch (JSONException e) { 61 | e.printStackTrace(); 62 | return; 63 | } 64 | 65 | if (options == null) 66 | return; 67 | 68 | if (isFirstAlarmInFuture(options)) 69 | return; 70 | 71 | Builder builder = new Builder(options); 72 | Notification notification = buildNotification(builder); 73 | boolean updated = notification.isUpdate(false); 74 | 75 | onTrigger(notification, updated); 76 | } 77 | 78 | /** 79 | * Called when a local notification was triggered. 80 | * 81 | * @param notification 82 | * Wrapper around the local notification 83 | * @param updated 84 | * If an update has triggered or the original 85 | */ 86 | abstract public void onTrigger (Notification notification, boolean updated); 87 | 88 | /** 89 | * Build notification specified by options. 90 | * 91 | * @param builder 92 | * Notification builder 93 | */ 94 | abstract public Notification buildNotification (Builder builder); 95 | 96 | /* 97 | * If you set a repeating alarm at 11:00 in the morning and it 98 | * should trigger every morning at 08:00 o'clock, it will 99 | * immediately fire. E.g. Android tries to make up for the 100 | * 'forgotten' reminder for that day. Therefore we ignore the event 101 | * if Android tries to 'catch up'. 102 | */ 103 | private Boolean isFirstAlarmInFuture (Options options) { 104 | Notification notification = new Builder(options).build(); 105 | 106 | if (!notification.isRepeating()) 107 | return false; 108 | 109 | Calendar now = Calendar.getInstance(); 110 | Calendar alarm = Calendar.getInstance(); 111 | 112 | alarm.setTime(notification.getOptions().getTriggerDate()); 113 | 114 | int alarmHour = alarm.get(Calendar.HOUR_OF_DAY); 115 | int alarmMin = alarm.get(Calendar.MINUTE); 116 | int currentHour = now.get(Calendar.HOUR_OF_DAY); 117 | int currentMin = now.get(Calendar.MINUTE); 118 | 119 | return (currentHour != alarmHour && currentMin != alarmMin); 120 | } 121 | 122 | } 123 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/notification/AssetUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications.notification; 25 | 26 | import android.content.Context; 27 | import android.content.res.AssetManager; 28 | import android.content.res.Resources; 29 | import android.graphics.Bitmap; 30 | import android.graphics.BitmapFactory; 31 | import android.media.RingtoneManager; 32 | import android.net.Uri; 33 | import android.os.StrictMode; 34 | import android.util.Log; 35 | 36 | import java.io.File; 37 | import java.io.FileNotFoundException; 38 | import java.io.FileOutputStream; 39 | import java.io.IOException; 40 | import java.io.InputStream; 41 | import java.io.OutputStream; 42 | import java.net.HttpURLConnection; 43 | import java.net.MalformedURLException; 44 | import java.net.URL; 45 | import java.util.UUID; 46 | 47 | /** 48 | * Util class to map unified asset URIs to native URIs. URIs like file:/// 49 | * map to absolute paths while file:// point relatively to the www folder 50 | * within the asset resources. And res:// means a resource from the native 51 | * res folder. Remote assets are accessible via http:// for example. 52 | */ 53 | class AssetUtil { 54 | 55 | // Name of the storage folder 56 | private static final String STORAGE_FOLDER = "/localnotification"; 57 | 58 | // Placeholder URI for default sound 59 | private static final String DEFAULT_SOUND = "res://platform_default"; 60 | 61 | // Ref to the context passed through the constructor to access the 62 | // resources and app directory. 63 | private final Context context; 64 | 65 | /** 66 | * Constructor 67 | * 68 | * @param context 69 | * Application context 70 | */ 71 | private AssetUtil(Context context) { 72 | this.context = context; 73 | } 74 | 75 | /** 76 | * Static method to retrieve class instance. 77 | * 78 | * @param context 79 | * Application context 80 | */ 81 | static AssetUtil getInstance(Context context) { 82 | return new AssetUtil(context); 83 | } 84 | 85 | /** 86 | * Parse path path to native URI. 87 | * 88 | * @param path 89 | * Path to path file 90 | */ 91 | Uri parseSound (String path) { 92 | 93 | if (path == null || path.isEmpty()) 94 | return Uri.EMPTY; 95 | 96 | if (path.equalsIgnoreCase(DEFAULT_SOUND)) { 97 | return RingtoneManager.getDefaultUri(RingtoneManager 98 | .TYPE_NOTIFICATION); 99 | } 100 | 101 | return parse(path); 102 | } 103 | 104 | /** 105 | * The URI for a path. 106 | * 107 | * @param path 108 | * The given path 109 | */ 110 | Uri parse (String path) { 111 | 112 | if (path.startsWith("res:")) { 113 | return getUriForResourcePath(path); 114 | } else if (path.startsWith("file:///")) { 115 | return getUriFromPath(path); 116 | } else if (path.startsWith("file://")) { 117 | return getUriFromAsset(path); 118 | } else if (path.startsWith("http")){ 119 | return getUriFromRemote(path); 120 | } 121 | 122 | return Uri.EMPTY; 123 | } 124 | 125 | /** 126 | * URI for a file. 127 | * 128 | * @param path 129 | * Absolute path like file:///... 130 | * 131 | * @return 132 | * URI pointing to the given path 133 | */ 134 | private Uri getUriFromPath(String path) { 135 | String absPath = path.replaceFirst("file://", ""); 136 | File file = new File(absPath); 137 | 138 | if (!file.exists()) { 139 | Log.e("Asset", "File not found: " + file.getAbsolutePath()); 140 | return Uri.EMPTY; 141 | } 142 | 143 | return Uri.fromFile(file); 144 | } 145 | 146 | /** 147 | * URI for an asset. 148 | * 149 | * @param path 150 | * Asset path like file://... 151 | * 152 | * @return 153 | * URI pointing to the given path 154 | */ 155 | private Uri getUriFromAsset(String path) { 156 | String resPath = path.replaceFirst("file:/", "www"); 157 | String fileName = resPath.substring(resPath.lastIndexOf('/') + 1); 158 | File file = getTmpFile(fileName); 159 | 160 | if (file == null) { 161 | Log.e("Asset", "Missing external cache dir"); 162 | return Uri.EMPTY; 163 | } 164 | 165 | try { 166 | AssetManager assets = context.getAssets(); 167 | FileOutputStream outStream = new FileOutputStream(file); 168 | InputStream inputStream = assets.open(resPath); 169 | 170 | copyFile(inputStream, outStream); 171 | 172 | outStream.flush(); 173 | outStream.close(); 174 | 175 | return Uri.fromFile(file); 176 | 177 | } catch (Exception e) { 178 | Log.e("Asset", "File not found: assets/" + resPath); 179 | e.printStackTrace(); 180 | } 181 | 182 | return Uri.EMPTY; 183 | } 184 | 185 | /** 186 | * The URI for a resource. 187 | * 188 | * @param path 189 | * The given relative path 190 | * 191 | * @return 192 | * URI pointing to the given path 193 | */ 194 | private Uri getUriForResourcePath(String path) { 195 | String resPath = path.replaceFirst("res://", ""); 196 | int resId = getResIdForDrawable(resPath); 197 | File file = getTmpFile(); 198 | 199 | if (resId == 0) { 200 | Log.e("Asset", "File not found: " + resPath); 201 | return Uri.EMPTY; 202 | } 203 | 204 | if (file == null) { 205 | Log.e("Asset", "Missing external cache dir"); 206 | return Uri.EMPTY; 207 | } 208 | 209 | try { 210 | Resources res = context.getResources(); 211 | FileOutputStream outStream = new FileOutputStream(file); 212 | InputStream inputStream = res.openRawResource(resId); 213 | copyFile(inputStream, outStream); 214 | 215 | outStream.flush(); 216 | outStream.close(); 217 | 218 | return Uri.fromFile(file); 219 | 220 | } catch (Exception e) { 221 | e.printStackTrace(); 222 | } 223 | 224 | return Uri.EMPTY; 225 | } 226 | 227 | /** 228 | * Uri from remote located content. 229 | * 230 | * @param path 231 | * Remote address 232 | * 233 | * @return 234 | * Uri of the downloaded file 235 | */ 236 | private Uri getUriFromRemote(String path) { 237 | File file = getTmpFile(); 238 | 239 | if (file == null) { 240 | Log.e("Asset", "Missing external cache dir"); 241 | return Uri.EMPTY; 242 | } 243 | 244 | try { 245 | URL url = new URL(path); 246 | HttpURLConnection connection = (HttpURLConnection) url.openConnection(); 247 | 248 | StrictMode.ThreadPolicy policy = 249 | new StrictMode.ThreadPolicy.Builder().permitAll().build(); 250 | 251 | StrictMode.setThreadPolicy(policy); 252 | 253 | connection.setRequestProperty("Connection", "close"); 254 | connection.setConnectTimeout(5000); 255 | connection.connect(); 256 | 257 | InputStream input = connection.getInputStream(); 258 | FileOutputStream outStream = new FileOutputStream(file); 259 | 260 | copyFile(input, outStream); 261 | 262 | outStream.flush(); 263 | outStream.close(); 264 | 265 | return Uri.fromFile(file); 266 | 267 | } catch (MalformedURLException e) { 268 | Log.e("Asset", "Incorrect URL"); 269 | e.printStackTrace(); 270 | } catch (FileNotFoundException e) { 271 | Log.e("Asset", "Failed to create new File from HTTP Content"); 272 | e.printStackTrace(); 273 | } catch (IOException e) { 274 | Log.e("Asset", "No Input can be created from http Stream"); 275 | e.printStackTrace(); 276 | } 277 | 278 | return Uri.EMPTY; 279 | } 280 | 281 | /** 282 | * Copy content from input stream into output stream. 283 | * 284 | * @param in 285 | * The input stream 286 | * @param out 287 | * The output stream 288 | */ 289 | private void copyFile(InputStream in, OutputStream out) throws IOException { 290 | byte[] buffer = new byte[1024]; 291 | int read; 292 | 293 | while ((read = in.read(buffer)) != -1) { 294 | out.write(buffer, 0, read); 295 | } 296 | } 297 | 298 | /** 299 | * Resource ID for drawable. 300 | * 301 | * @param resPath 302 | * Resource path as string 303 | */ 304 | int getResIdForDrawable(String resPath) { 305 | int resId = getResIdForDrawable(getPkgName(), resPath); 306 | 307 | if (resId == 0) { 308 | resId = getResIdForDrawable("android", resPath); 309 | } 310 | 311 | return resId; 312 | } 313 | 314 | /** 315 | * Resource ID for drawable. 316 | * 317 | * @param clsName 318 | * Relative package or global android name space 319 | * @param resPath 320 | * Resource path as string 321 | */ 322 | int getResIdForDrawable(String clsName, String resPath) { 323 | String drawable = getBaseName(resPath); 324 | int resId = 0; 325 | 326 | try { 327 | Class cls = Class.forName(clsName + ".R$drawable"); 328 | 329 | resId = (Integer) cls.getDeclaredField(drawable).get(Integer.class); 330 | } catch (Exception ignore) {} 331 | 332 | return resId; 333 | } 334 | 335 | /** 336 | * Convert drawable resource to bitmap. 337 | * 338 | * @param drawable 339 | * Drawable resource name 340 | */ 341 | Bitmap getIconFromDrawable (String drawable) { 342 | Resources res = context.getResources(); 343 | int iconId; 344 | 345 | iconId = getResIdForDrawable(getPkgName(), drawable); 346 | 347 | if (iconId == 0) { 348 | iconId = getResIdForDrawable("android", drawable); 349 | } 350 | 351 | if (iconId == 0) { 352 | iconId = android.R.drawable.screen_background_dark_transparent; 353 | } 354 | 355 | return BitmapFactory.decodeResource(res, iconId); 356 | } 357 | 358 | /** 359 | * Convert URI to Bitmap. 360 | * 361 | * @param uri 362 | * Internal image URI 363 | */ 364 | Bitmap getIconFromUri (Uri uri) throws IOException { 365 | InputStream input = context.getContentResolver().openInputStream(uri); 366 | 367 | return BitmapFactory.decodeStream(input); 368 | } 369 | 370 | /** 371 | * Extract name of drawable resource from path. 372 | * 373 | * @param resPath 374 | * Resource path as string 375 | */ 376 | private String getBaseName (String resPath) { 377 | String drawable = resPath; 378 | 379 | if (drawable.contains("/")) { 380 | drawable = drawable.substring(drawable.lastIndexOf('/') + 1); 381 | } 382 | 383 | if (resPath.contains(".")) { 384 | drawable = drawable.substring(0, drawable.lastIndexOf('.')); 385 | } 386 | 387 | return drawable; 388 | } 389 | 390 | /** 391 | * Returns a file located under the external cache dir of that app. 392 | * 393 | * @return 394 | * File with a random UUID name 395 | */ 396 | private File getTmpFile () { 397 | // If random UUID is not be enough see 398 | // https://github.com/LukePulverenti/cordova-plugin-local-notifications/blob/267170db14044cbeff6f4c3c62d9b766b7a1dd62/src/android/notification/AssetUtil.java#L255 399 | return getTmpFile(UUID.randomUUID().toString()); 400 | } 401 | 402 | /** 403 | * Returns a file located under the external cache dir of that app. 404 | * 405 | * @param name 406 | * The name of the file 407 | * @return 408 | * File with the provided name 409 | */ 410 | private File getTmpFile (String name) { 411 | File dir = context.getExternalCacheDir(); 412 | 413 | if (dir == null) { 414 | Log.e("Asset", "Missing external cache dir"); 415 | return null; 416 | } 417 | 418 | String storage = dir.toString() + STORAGE_FOLDER; 419 | 420 | //noinspection ResultOfMethodCallIgnored 421 | new File(storage).mkdir(); 422 | 423 | return new File(storage, name); 424 | } 425 | 426 | /** 427 | * Package name specified by context. 428 | */ 429 | private String getPkgName () { 430 | return context.getPackageName(); 431 | } 432 | 433 | } 434 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/notification/Builder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications.notification; 25 | 26 | import android.app.PendingIntent; 27 | import android.content.Context; 28 | import android.content.Intent; 29 | import android.net.Uri; 30 | import android.support.v4.app.NotificationCompat; 31 | 32 | import org.json.JSONObject; 33 | 34 | import java.util.Random; 35 | 36 | /** 37 | * Builder class for local notifications. Build fully configured local 38 | * notification specified by JSON object passed from JS side. 39 | */ 40 | public class Builder { 41 | 42 | // Application context passed by constructor 43 | private final Context context; 44 | 45 | // Notification options passed by JS 46 | private final Options options; 47 | 48 | // Receiver to handle the trigger event 49 | private Class triggerReceiver; 50 | 51 | // Receiver to handle the clear event 52 | private Class clearReceiver = ClearReceiver.class; 53 | 54 | // Activity to handle the click event 55 | private Class clickActivity = ClickActivity.class; 56 | 57 | /** 58 | * Constructor 59 | * 60 | * @param context 61 | * Application context 62 | * @param options 63 | * Notification options 64 | */ 65 | public Builder(Context context, JSONObject options) { 66 | this.context = context; 67 | this.options = new Options(context).parse(options); 68 | } 69 | 70 | /** 71 | * Constructor 72 | * 73 | * @param options 74 | * Notification options 75 | */ 76 | public Builder(Options options) { 77 | this.context = options.getContext(); 78 | this.options = options; 79 | } 80 | 81 | /** 82 | * Set trigger receiver. 83 | * 84 | * @param receiver 85 | * Broadcast receiver 86 | */ 87 | public Builder setTriggerReceiver(Class receiver) { 88 | this.triggerReceiver = receiver; 89 | return this; 90 | } 91 | 92 | /** 93 | * Set clear receiver. 94 | * 95 | * @param receiver 96 | * Broadcast receiver 97 | */ 98 | public Builder setClearReceiver(Class receiver) { 99 | this.clearReceiver = receiver; 100 | return this; 101 | } 102 | 103 | /** 104 | * Set click activity. 105 | * 106 | * @param activity 107 | * Activity 108 | */ 109 | public Builder setClickActivity(Class activity) { 110 | this.clickActivity = activity; 111 | return this; 112 | } 113 | 114 | /** 115 | * Creates the notification with all its options passed through JS. 116 | */ 117 | public Notification build() { 118 | Uri sound = options.getSoundUri(); 119 | int smallIcon = options.getSmallIcon(); 120 | NotificationCompat.Builder builder; 121 | 122 | builder = new NotificationCompat.Builder(context) 123 | .setDefaults(0) 124 | .setContentTitle(options.getTitle()) 125 | .setContentText(options.getText()) 126 | .setNumber(options.getBadgeNumber()) 127 | .setTicker(options.getText()) 128 | .setAutoCancel(options.isAutoClear()) 129 | .setOngoing(options.isOngoing()) 130 | .setLights(options.getLedColor(), 500, 500); 131 | 132 | if (sound != null) { 133 | builder.setSound(sound); 134 | } 135 | 136 | if (smallIcon == 0) { 137 | builder.setSmallIcon(options.getIcon()); 138 | } else { 139 | builder.setSmallIcon(options.getSmallIcon()); 140 | builder.setLargeIcon(options.getIconBitmap()); 141 | } 142 | 143 | applyDeleteReceiver(builder); 144 | applyContentReceiver(builder); 145 | 146 | return new Notification(context, options, builder, triggerReceiver); 147 | } 148 | 149 | /** 150 | * Set intent to handle the delete event. Will clean up some persisted 151 | * preferences. 152 | * 153 | * @param builder 154 | * Local notification builder instance 155 | */ 156 | private void applyDeleteReceiver(NotificationCompat.Builder builder) { 157 | 158 | if (clearReceiver == null) 159 | return; 160 | 161 | Intent intent = new Intent(context, clearReceiver) 162 | .setAction(options.getIdStr()) 163 | .putExtra(Options.EXTRA, options.toString()); 164 | 165 | PendingIntent deleteIntent = PendingIntent.getBroadcast( 166 | context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); 167 | 168 | builder.setDeleteIntent(deleteIntent); 169 | } 170 | 171 | /** 172 | * Set intent to handle the click event. Will bring the app to 173 | * foreground. 174 | * 175 | * @param builder 176 | * Local notification builder instance 177 | */ 178 | private void applyContentReceiver(NotificationCompat.Builder builder) { 179 | 180 | if (clickActivity == null) 181 | return; 182 | 183 | Intent intent = new Intent(context, clickActivity) 184 | .putExtra(Options.EXTRA, options.toString()) 185 | .setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY); 186 | 187 | int reqCode = new Random().nextInt(); 188 | 189 | PendingIntent contentIntent = PendingIntent.getActivity( 190 | context, reqCode, intent, PendingIntent.FLAG_UPDATE_CURRENT); 191 | 192 | builder.setContentIntent(contentIntent); 193 | } 194 | 195 | } 196 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/notification/ClearReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications.notification; 25 | 26 | /** 27 | * The clear intent receiver is triggered when the user clears a 28 | * notification manually. It un-persists the cleared notification from the 29 | * shared preferences. 30 | */ 31 | public class ClearReceiver extends AbstractClearReceiver { 32 | 33 | /** 34 | * Called when a local notification was cleared from outside of the app. 35 | * 36 | * @param notification 37 | * Wrapper around the local notification 38 | */ 39 | @Override 40 | public void onClear (Notification notification) { 41 | notification.clear(); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/notification/ClickActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications.notification; 25 | 26 | /** 27 | * The receiver activity is triggered when a notification is clicked by a user. 28 | * The activity calls the background callback and brings the launch intent 29 | * up to foreground. 30 | */ 31 | public class ClickActivity extends AbstractClickActivity { 32 | 33 | /** 34 | * Called when local notification was clicked by the user. Will 35 | * move the app to foreground. 36 | * 37 | * @param notification 38 | * Wrapper around the local notification 39 | */ 40 | @Override 41 | public void onClick(Notification notification) { 42 | launchApp(); 43 | 44 | if (notification.isRepeating()) { 45 | notification.clear(); 46 | } else { 47 | notification.cancel(); 48 | } 49 | } 50 | 51 | /** 52 | * Build notification specified by options. 53 | * 54 | * @param builder 55 | * Notification builder 56 | */ 57 | public Notification buildNotification (Builder builder) { 58 | return builder.build(); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/notification/Manager.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications.notification; 25 | 26 | import android.app.NotificationManager; 27 | import android.content.Context; 28 | import android.content.SharedPreferences; 29 | 30 | import org.json.JSONException; 31 | import org.json.JSONObject; 32 | 33 | import java.util.ArrayList; 34 | import java.util.Iterator; 35 | import java.util.List; 36 | import java.util.Map; 37 | import java.util.Set; 38 | 39 | import static com.remobile.localNotifications.notification.Notification.PREF_KEY; 40 | 41 | /** 42 | * Central way to access all or single local notifications set by specific 43 | * state like triggered or scheduled. Offers shortcut ways to schedule, 44 | * cancel or clear local notifications. 45 | */ 46 | public class Manager { 47 | 48 | // Context passed through constructor and used for notification builder. 49 | private Context context; 50 | 51 | /** 52 | * Constructor 53 | * 54 | * @param context 55 | * Application context 56 | */ 57 | private Manager(Context context){ 58 | this.context = context; 59 | } 60 | 61 | /** 62 | * Static method to retrieve class instance. 63 | * 64 | * @param context 65 | * Application context 66 | */ 67 | public static Manager getInstance(Context context) { 68 | return new Manager(context); 69 | } 70 | 71 | /** 72 | * Schedule local notification specified by JSON object. 73 | * 74 | * @param options 75 | * JSON object with set of options 76 | * @param receiver 77 | * Receiver to handle the trigger event 78 | */ 79 | public Notification schedule (JSONObject options, Class receiver) { 80 | return schedule(new Options(context).parse(options), receiver); 81 | } 82 | 83 | /** 84 | * Schedule local notification specified by options object. 85 | * 86 | * @param options 87 | * Set of notification options 88 | * @param receiver 89 | * Receiver to handle the trigger event 90 | */ 91 | public Notification schedule (Options options, Class receiver) { 92 | Notification notification = new Builder(options) 93 | .setTriggerReceiver(receiver) 94 | .build(); 95 | 96 | notification.schedule(); 97 | 98 | return notification; 99 | } 100 | 101 | /** 102 | * Clear local notification specified by ID. 103 | * 104 | * @param id 105 | * The notification ID 106 | * @param updates 107 | * JSON object with notification options 108 | * @param receiver 109 | * Receiver to handle the trigger event 110 | */ 111 | public Notification update (int id, JSONObject updates, Class receiver) { 112 | Notification notification = get(id); 113 | 114 | if (notification == null) 115 | return null; 116 | 117 | notification.cancel(); 118 | 119 | JSONObject options = mergeJSONObjects( 120 | notification.getOptions().getDict(), updates); 121 | 122 | try { 123 | options.putOpt("updated", true); 124 | } catch (JSONException ignore) {} 125 | 126 | return schedule(options, receiver); 127 | } 128 | 129 | /** 130 | * Clear local notification specified by ID. 131 | * 132 | * @param id 133 | * The notification ID 134 | */ 135 | public Notification clear (int id) { 136 | Notification notification = get(id); 137 | 138 | if (notification != null) { 139 | notification.clear(); 140 | } 141 | 142 | return notification; 143 | } 144 | 145 | /** 146 | * Clear local notification specified by ID. 147 | * 148 | * @param id 149 | * The notification ID 150 | */ 151 | public Notification cancel (int id) { 152 | Notification notification = get(id); 153 | 154 | if (notification != null) { 155 | notification.cancel(); 156 | } 157 | 158 | return notification; 159 | } 160 | 161 | /** 162 | * Clear all local notifications. 163 | */ 164 | public void clearAll () { 165 | List notifications = getAll(); 166 | 167 | for (Notification notification : notifications) { 168 | notification.clear(); 169 | } 170 | 171 | getNotMgr().cancelAll(); 172 | } 173 | 174 | /** 175 | * Cancel all local notifications. 176 | */ 177 | public void cancelAll () { 178 | List notifications = getAll(); 179 | 180 | for (Notification notification : notifications) { 181 | notification.cancel(); 182 | } 183 | 184 | getNotMgr().cancelAll(); 185 | } 186 | 187 | /** 188 | * All local notifications IDs. 189 | */ 190 | public List getIds() { 191 | Set keys = getPrefs().getAll().keySet(); 192 | ArrayList ids = new ArrayList(); 193 | 194 | for (String key : keys) { 195 | try { 196 | ids.add(Integer.parseInt(key)); 197 | } catch (NumberFormatException e) { 198 | e.printStackTrace(); 199 | } 200 | } 201 | 202 | return ids; 203 | } 204 | 205 | /** 206 | * All local notification IDs for given type. 207 | * 208 | * @param type 209 | * The notification life cycle type 210 | */ 211 | public List getIdsByType(Notification.Type type) { 212 | List notifications = getAll(); 213 | ArrayList ids = new ArrayList(); 214 | 215 | for (Notification notification : notifications) { 216 | if (notification.getType() == type) { 217 | ids.add(notification.getId()); 218 | } 219 | } 220 | 221 | return ids; 222 | } 223 | 224 | /** 225 | * List of local notifications with matching ID. 226 | * 227 | * @param ids 228 | * Set of notification IDs 229 | */ 230 | public List getByIds(List ids) { 231 | ArrayList notifications = new ArrayList(); 232 | 233 | for (int id : ids) { 234 | Notification notification = get(id); 235 | 236 | if (notification != null) { 237 | notifications.add(notification); 238 | } 239 | } 240 | 241 | return notifications; 242 | } 243 | 244 | /** 245 | * List of all local notification. 246 | */ 247 | public List getAll() { 248 | return getByIds(getIds()); 249 | } 250 | 251 | /** 252 | * List of local notifications from given type. 253 | * 254 | * @param type 255 | * The notification life cycle type 256 | */ 257 | public List getByType(Notification.Type type) { 258 | List notifications = getAll(); 259 | ArrayList list = new ArrayList(); 260 | 261 | if (type == Notification.Type.ALL) 262 | return notifications; 263 | 264 | for (Notification notification : notifications) { 265 | if (notification.getType() == type) { 266 | list.add(notification); 267 | } 268 | } 269 | 270 | return list; 271 | } 272 | 273 | /** 274 | * List of local notifications with matching ID from given type. 275 | * 276 | * @param type 277 | * The notification life cycle type 278 | * @param ids 279 | * Set of notification IDs 280 | */ 281 | @SuppressWarnings("UnusedDeclaration") 282 | public List getBy(Notification.Type type, List ids) { 283 | ArrayList notifications = new ArrayList(); 284 | 285 | for (int id : ids) { 286 | Notification notification = get(id); 287 | 288 | if (notification != null && notification.isScheduled()) { 289 | notifications.add(notification); 290 | } 291 | } 292 | 293 | return notifications; 294 | } 295 | 296 | /** 297 | * If a notification with an ID exists. 298 | * 299 | * @param id 300 | * Notification ID 301 | */ 302 | public boolean exist (int id) { 303 | return get(id) != null; 304 | } 305 | 306 | /** 307 | * If a notification with an ID and type exists. 308 | * 309 | * @param id 310 | * Notification ID 311 | * @param type 312 | * Notification type 313 | */ 314 | public boolean exist (int id, Notification.Type type) { 315 | Notification notification = get(id); 316 | 317 | return notification != null && notification.getType() == type; 318 | } 319 | 320 | /** 321 | * List of properties from all local notifications. 322 | */ 323 | public List getOptions() { 324 | return getOptionsById(getIds()); 325 | } 326 | 327 | /** 328 | * List of properties from local notifications with matching ID. 329 | * 330 | * @param ids 331 | * Set of notification IDs 332 | */ 333 | public List getOptionsById(List ids) { 334 | ArrayList options = new ArrayList(); 335 | 336 | for (int id : ids) { 337 | Notification notification = get(id); 338 | 339 | if (notification != null) { 340 | options.add(notification.getOptions().getDict()); 341 | } 342 | } 343 | 344 | return options; 345 | } 346 | 347 | /** 348 | * List of properties from all local notifications from given type. 349 | * 350 | * @param type 351 | * The notification life cycle type 352 | */ 353 | public List getOptionsByType(Notification.Type type) { 354 | ArrayList options = new ArrayList(); 355 | List notifications = getByType(type); 356 | 357 | for (Notification notification : notifications) { 358 | options.add(notification.getOptions().getDict()); 359 | } 360 | 361 | return options; 362 | } 363 | 364 | /** 365 | * List of properties from local notifications with matching ID from 366 | * given type. 367 | * 368 | * @param type 369 | * The notification life cycle type 370 | * @param ids 371 | * Set of notification IDs 372 | */ 373 | public List getOptionsBy(Notification.Type type, 374 | List ids) { 375 | 376 | if (type == Notification.Type.ALL) 377 | return getOptionsById(ids); 378 | 379 | ArrayList options = new ArrayList(); 380 | List notifications = getByIds(ids); 381 | 382 | for (Notification notification : notifications) { 383 | if (notification.getType() == type) { 384 | options.add(notification.getOptions().getDict()); 385 | } 386 | } 387 | 388 | return options; 389 | } 390 | 391 | /** 392 | * Get existent local notification. 393 | * 394 | * @param id 395 | * Notification ID 396 | */ 397 | public Notification get(int id) { 398 | Map alarms = getPrefs().getAll(); 399 | String notId = Integer.toString(id); 400 | JSONObject options; 401 | 402 | if (!alarms.containsKey(notId)) 403 | return null; 404 | 405 | 406 | try { 407 | String json = alarms.get(notId).toString(); 408 | options = new JSONObject(json); 409 | } catch (JSONException e) { 410 | e.printStackTrace(); 411 | return null; 412 | } 413 | 414 | Builder builder = new Builder(context, options); 415 | 416 | return builder.build(); 417 | } 418 | 419 | /** 420 | * Merge two JSON objects. 421 | * 422 | * @param obj1 423 | * JSON object 424 | * @param obj2 425 | * JSON object with new options 426 | */ 427 | private JSONObject mergeJSONObjects (JSONObject obj1, JSONObject obj2) { 428 | Iterator it = obj2.keys(); 429 | 430 | while (it.hasNext()) { 431 | try { 432 | String key = (String)it.next(); 433 | 434 | obj1.put(key, obj2.opt(key)); 435 | } catch (JSONException e) { 436 | e.printStackTrace(); 437 | } 438 | } 439 | 440 | return obj1; 441 | } 442 | 443 | /** 444 | * Shared private preferences for the application. 445 | */ 446 | private SharedPreferences getPrefs () { 447 | return context.getSharedPreferences(PREF_KEY, Context.MODE_PRIVATE); 448 | } 449 | 450 | /** 451 | * Notification manager for the application. 452 | */ 453 | private NotificationManager getNotMgr () { 454 | return (NotificationManager) context 455 | .getSystemService(Context.NOTIFICATION_SERVICE); 456 | } 457 | 458 | } 459 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/notification/Notification.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications.notification; 25 | 26 | 27 | import android.app.AlarmManager; 28 | import android.app.NotificationManager; 29 | import android.app.PendingIntent; 30 | import android.content.Context; 31 | import android.content.Intent; 32 | import android.content.SharedPreferences; 33 | import android.os.Build; 34 | import android.support.v4.app.NotificationCompat; 35 | 36 | import org.json.JSONException; 37 | import org.json.JSONObject; 38 | 39 | import java.util.Date; 40 | 41 | /** 42 | * Wrapper class around OS notification class. Handles basic operations 43 | * like show, delete, cancel for a single local notification instance. 44 | */ 45 | public class Notification { 46 | 47 | // Used to differ notifications by their life cycle state 48 | public enum Type { 49 | ALL, SCHEDULED, TRIGGERED 50 | } 51 | 52 | // Default receiver to handle the trigger event 53 | private static Class defaultReceiver = TriggerReceiver.class; 54 | 55 | // Key for private preferences 56 | static final String PREF_KEY = "LocalNotification"; 57 | 58 | // Application context passed by constructor 59 | private final Context context; 60 | 61 | // Notification options passed by JS 62 | private final Options options; 63 | 64 | // Builder with full configuration 65 | private final NotificationCompat.Builder builder; 66 | 67 | // Receiver to handle the trigger event 68 | private Class receiver = defaultReceiver; 69 | 70 | /** 71 | * Constructor 72 | * 73 | * @param context 74 | * Application context 75 | * @param options 76 | * Parsed notification options 77 | * @param builder 78 | * Pre-configured notification builder 79 | */ 80 | protected Notification (Context context, Options options, 81 | NotificationCompat.Builder builder, Class receiver) { 82 | 83 | this.context = context; 84 | this.options = options; 85 | this.builder = builder; 86 | 87 | this.receiver = receiver != null ? receiver : defaultReceiver; 88 | } 89 | 90 | /** 91 | * Get application context. 92 | */ 93 | public Context getContext () { 94 | return context; 95 | } 96 | 97 | /** 98 | * Get notification options. 99 | */ 100 | public Options getOptions () { 101 | return options; 102 | } 103 | 104 | /** 105 | * Get notification ID. 106 | */ 107 | public int getId () { 108 | return options.getId(); 109 | } 110 | 111 | /** 112 | * If it's a repeating notification. 113 | */ 114 | public boolean isRepeating () { 115 | return getOptions().getRepeatInterval() > 0; 116 | } 117 | 118 | /** 119 | * If the notification was in the past. 120 | */ 121 | public boolean wasInThePast () { 122 | return new Date().after(options.getTriggerDate()); 123 | } 124 | 125 | /** 126 | * If the notification is scheduled. 127 | */ 128 | public boolean isScheduled () { 129 | return isRepeating() || !wasInThePast(); 130 | } 131 | 132 | /** 133 | * If the notification is triggered. 134 | */ 135 | public boolean isTriggered () { 136 | return wasInThePast(); 137 | } 138 | 139 | /** 140 | * If the notification is an update. 141 | * 142 | * @param keepFlag 143 | * Set to false to remove the flag from the option map 144 | */ 145 | protected boolean isUpdate (boolean keepFlag) { 146 | boolean updated = options.getDict().optBoolean("updated", false); 147 | 148 | if (!keepFlag) { 149 | options.getDict().remove("updated"); 150 | } 151 | 152 | return updated; 153 | } 154 | 155 | /** 156 | * Notification type can be one of pending or scheduled. 157 | */ 158 | public Type getType () { 159 | return isTriggered() ? Type.TRIGGERED : Type.SCHEDULED; 160 | } 161 | 162 | /** 163 | * Schedule the local notification. 164 | */ 165 | public void schedule() { 166 | long triggerTime = options.getTriggerTime(); 167 | 168 | persist(); 169 | 170 | // Intent gets called when the Notification gets fired 171 | Intent intent = new Intent(context, receiver) 172 | .setAction(options.getIdStr()) 173 | .putExtra(Options.EXTRA, options.toString()); 174 | 175 | PendingIntent pi = PendingIntent.getBroadcast( 176 | context, 0, intent, PendingIntent.FLAG_CANCEL_CURRENT); 177 | 178 | if (isRepeating()) { 179 | getAlarmMgr().setRepeating(AlarmManager.RTC_WAKEUP, 180 | triggerTime, options.getRepeatInterval(), pi); 181 | } else { 182 | getAlarmMgr().set(AlarmManager.RTC_WAKEUP, triggerTime, pi); 183 | } 184 | } 185 | 186 | /** 187 | * Clear the local notification without canceling repeating alarms. 188 | */ 189 | public void clear () { 190 | 191 | if (!isRepeating() && wasInThePast()) 192 | unpersist(); 193 | 194 | if (!isRepeating()) 195 | getNotMgr().cancel(getId()); 196 | } 197 | 198 | /** 199 | * Cancel the local notification. 200 | * 201 | * Create an intent that looks similar, to the one that was registered 202 | * using schedule. Making sure the notification id in the action is the 203 | * same. Now we can search for such an intent using the 'getService' 204 | * method and cancel it. 205 | */ 206 | public void cancel() { 207 | Intent intent = new Intent(context, receiver) 208 | .setAction(options.getIdStr()); 209 | 210 | PendingIntent pi = PendingIntent. 211 | getBroadcast(context, 0, intent, 0); 212 | 213 | getAlarmMgr().cancel(pi); 214 | getNotMgr().cancel(options.getId()); 215 | 216 | unpersist(); 217 | } 218 | 219 | /** 220 | * Present the local notification to user. 221 | */ 222 | public void show () { 223 | // TODO Show dialog when in foreground 224 | showNotification(); 225 | } 226 | 227 | /** 228 | * Show as local notification when in background. 229 | */ 230 | @SuppressWarnings("deprecation") 231 | private void showNotification () { 232 | int id = getOptions().getId(); 233 | 234 | if (Build.VERSION.SDK_INT <= 15) { 235 | // Notification for HoneyComb to ICS 236 | getNotMgr().notify(id, builder.getNotification()); 237 | } else { 238 | // Notification for Jellybean and above 239 | getNotMgr().notify(id, builder.build()); 240 | } 241 | } 242 | 243 | /** 244 | * Count of triggers since schedule. 245 | */ 246 | public int getTriggerCountSinceSchedule() { 247 | long now = System.currentTimeMillis(); 248 | long triggerTime = options.getTriggerTime(); 249 | 250 | if (!wasInThePast()) 251 | return 0; 252 | 253 | if (!isRepeating()) 254 | return 1; 255 | 256 | return (int) ((now - triggerTime) / options.getRepeatInterval()); 257 | } 258 | 259 | /** 260 | * Encode options to JSON. 261 | */ 262 | public String toString() { 263 | JSONObject dict = options.getDict(); 264 | JSONObject json = new JSONObject(); 265 | 266 | try { 267 | json = new JSONObject(dict.toString()); 268 | } catch (JSONException e) { 269 | e.printStackTrace(); 270 | } 271 | 272 | json.remove("firstAt"); 273 | json.remove("updated"); 274 | json.remove("soundUri"); 275 | json.remove("iconUri"); 276 | 277 | return json.toString(); 278 | } 279 | 280 | /** 281 | * Persist the information of this notification to the Android Shared 282 | * Preferences. This will allow the application to restore the notification 283 | * upon device reboot, app restart, retrieve notifications, aso. 284 | */ 285 | private void persist () { 286 | SharedPreferences.Editor editor = getPrefs().edit(); 287 | 288 | editor.putString(options.getIdStr(), options.toString()); 289 | 290 | if (Build.VERSION.SDK_INT < 9) { 291 | editor.commit(); 292 | } else { 293 | editor.apply(); 294 | } 295 | } 296 | 297 | /** 298 | * Remove the notification from the Android shared Preferences. 299 | */ 300 | private void unpersist () { 301 | SharedPreferences.Editor editor = getPrefs().edit(); 302 | 303 | editor.remove(options.getIdStr()); 304 | 305 | if (Build.VERSION.SDK_INT < 9) { 306 | editor.commit(); 307 | } else { 308 | editor.apply(); 309 | } 310 | } 311 | 312 | /** 313 | * Shared private preferences for the application. 314 | */ 315 | private SharedPreferences getPrefs () { 316 | return context.getSharedPreferences(PREF_KEY, Context.MODE_PRIVATE); 317 | } 318 | 319 | /** 320 | * Notification manager for the application. 321 | */ 322 | private NotificationManager getNotMgr () { 323 | return (NotificationManager) context 324 | .getSystemService(Context.NOTIFICATION_SERVICE); 325 | } 326 | 327 | /** 328 | * Alarm manager for the application. 329 | */ 330 | private AlarmManager getAlarmMgr () { 331 | return (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); 332 | } 333 | 334 | /** 335 | * Set default receiver to handle the trigger event. 336 | * 337 | * @param receiver 338 | * broadcast receiver 339 | */ 340 | public static void setDefaultTriggerReceiver (Class receiver) { 341 | defaultReceiver = receiver; 342 | } 343 | 344 | } 345 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/notification/Options.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications.notification; 25 | 26 | import android.app.AlarmManager; 27 | import android.content.Context; 28 | import android.graphics.Bitmap; 29 | import android.net.Uri; 30 | 31 | import org.json.JSONException; 32 | import org.json.JSONObject; 33 | 34 | import java.util.Date; 35 | 36 | /** 37 | * Wrapper around the JSON object passed through JS which contains all 38 | * possible option values. Class provides simple readers and more advanced 39 | * methods to convert independent values into platform specific values. 40 | */ 41 | public class Options { 42 | 43 | // Key name for bundled extras 44 | static final String EXTRA = "NOTIFICATION_OPTIONS"; 45 | 46 | // The original JSON object 47 | private JSONObject options = new JSONObject(); 48 | 49 | // Repeat interval 50 | private long interval = 0; 51 | 52 | // Application context 53 | private final Context context; 54 | 55 | // Asset util instance 56 | private final AssetUtil assets; 57 | 58 | 59 | /** 60 | * Constructor 61 | * 62 | * @param context 63 | * Application context 64 | */ 65 | public Options(Context context){ 66 | this.context = context; 67 | this.assets = AssetUtil.getInstance(context); 68 | } 69 | 70 | /** 71 | * Parse given JSON properties. 72 | * 73 | * @param options 74 | * JSON properties 75 | */ 76 | public Options parse (JSONObject options) { 77 | this.options = options; 78 | 79 | parseInterval(); 80 | parseAssets(); 81 | 82 | return this; 83 | } 84 | 85 | /** 86 | * Parse repeat interval. 87 | */ 88 | private void parseInterval() { 89 | String every = options.optString("every").toLowerCase(); 90 | 91 | if (every.isEmpty()) { 92 | interval = 0; 93 | } else 94 | if (every.equals("second")) { 95 | interval = 1000; 96 | } else 97 | if (every.equals("minute")) { 98 | interval = AlarmManager.INTERVAL_FIFTEEN_MINUTES / 15; 99 | } else 100 | if (every.equals("hour")) { 101 | interval = AlarmManager.INTERVAL_HOUR; 102 | } else 103 | if (every.equals("day")) { 104 | interval = AlarmManager.INTERVAL_DAY; 105 | } else 106 | if (every.equals("week")) { 107 | interval = AlarmManager.INTERVAL_DAY * 7; 108 | } else 109 | if (every.equals("month")) { 110 | interval = AlarmManager.INTERVAL_DAY * 31; 111 | } else 112 | if (every.equals("quarter")) { 113 | interval = AlarmManager.INTERVAL_HOUR * 2190; 114 | } else 115 | if (every.equals("year")) { 116 | interval = AlarmManager.INTERVAL_DAY * 365; 117 | } else { 118 | try { 119 | interval = Integer.parseInt(every) * 60000; 120 | } catch (Exception e) { 121 | e.printStackTrace(); 122 | } 123 | } 124 | } 125 | 126 | /** 127 | * Parse asset URIs. 128 | */ 129 | private void parseAssets() { 130 | 131 | if (options.has("iconUri")) 132 | return; 133 | 134 | Uri iconUri = assets.parse(options.optString("icon", "icon")); 135 | Uri soundUri = assets.parseSound(options.optString("sound", null)); 136 | 137 | try { 138 | options.put("iconUri", iconUri.toString()); 139 | options.put("soundUri", soundUri.toString()); 140 | } catch (JSONException e) { 141 | e.printStackTrace(); 142 | } 143 | } 144 | 145 | /** 146 | * Application context. 147 | */ 148 | public Context getContext () { 149 | return context; 150 | } 151 | 152 | /** 153 | * Wrapped JSON object. 154 | */ 155 | JSONObject getDict () { 156 | return options; 157 | } 158 | 159 | /** 160 | * Text for the local notification. 161 | */ 162 | public String getText() { 163 | return options.optString("text", ""); 164 | } 165 | 166 | /** 167 | * Repeat interval (day, week, month, year, aso.) 168 | */ 169 | public long getRepeatInterval() { 170 | return interval; 171 | } 172 | 173 | /** 174 | * Badge number for the local notification. 175 | */ 176 | public int getBadgeNumber() { 177 | return options.optInt("badge", 0); 178 | } 179 | 180 | /** 181 | * ongoing flag for local notifications. 182 | */ 183 | public Boolean isOngoing() { 184 | return options.optBoolean("ongoing", false); 185 | } 186 | 187 | /** 188 | * autoClear flag for local notifications. 189 | */ 190 | public Boolean isAutoClear() { 191 | return options.optBoolean("autoClear", false); 192 | } 193 | 194 | /** 195 | * ID for the local notification as a number. 196 | */ 197 | public Integer getId() { 198 | return options.optInt("id", 0); 199 | } 200 | 201 | /** 202 | * ID for the local notification as a string. 203 | */ 204 | public String getIdStr() { 205 | return getId().toString(); 206 | } 207 | 208 | /** 209 | * Trigger date. 210 | */ 211 | public Date getTriggerDate() { 212 | return new Date(getTriggerTime()); 213 | } 214 | 215 | /** 216 | * Trigger date in milliseconds. 217 | */ 218 | public long getTriggerTime() { 219 | //return Math.max( 220 | // System.currentTimeMillis(), 221 | return options.optLong("at", 0) * 1000; 222 | //); 223 | } 224 | 225 | /** 226 | * Title for the local notification. 227 | */ 228 | public String getTitle() { 229 | String title = options.optString("title", ""); 230 | 231 | if (title.isEmpty()) { 232 | title = context.getApplicationInfo().loadLabel( 233 | context.getPackageManager()).toString(); 234 | } 235 | 236 | return title; 237 | } 238 | 239 | /** 240 | * @return 241 | * The notification color for LED 242 | */ 243 | public int getLedColor() { 244 | String hex = options.optString("led", "000000"); 245 | int aRGB = Integer.parseInt(hex,16); 246 | 247 | aRGB += 0xFF000000; 248 | 249 | return aRGB; 250 | } 251 | 252 | /** 253 | * Sound file path for the local notification. 254 | */ 255 | public Uri getSoundUri() { 256 | Uri uri = null; 257 | 258 | try{ 259 | uri = Uri.parse(options.optString("soundUri")); 260 | } catch (Exception e){ 261 | e.printStackTrace(); 262 | } 263 | 264 | return uri; 265 | } 266 | 267 | /** 268 | * Icon bitmap for the local notification. 269 | */ 270 | public Bitmap getIconBitmap() { 271 | Bitmap bmp; 272 | 273 | try { 274 | Uri uri = Uri.parse(options.optString("iconUri")); 275 | bmp = assets.getIconFromUri(uri); 276 | } catch (Exception e){ 277 | e.printStackTrace(); 278 | bmp = assets.getIconFromDrawable("icon"); 279 | } 280 | 281 | return bmp; 282 | } 283 | 284 | /** 285 | * Icon resource ID for the local notification. 286 | */ 287 | public int getIcon () { 288 | String icon = options.optString("icon", ""); 289 | 290 | int resId = assets.getResIdForDrawable(icon); 291 | 292 | if (resId == 0) { 293 | resId = getSmallIcon(); 294 | } 295 | 296 | if (resId == 0) { 297 | resId = android.R.drawable.ic_popup_reminder; 298 | } 299 | 300 | return resId; 301 | } 302 | 303 | /** 304 | * Small icon resource ID for the local notification. 305 | */ 306 | public int getSmallIcon () { 307 | String icon = options.optString("smallIcon", ""); 308 | 309 | return assets.getResIdForDrawable(icon); 310 | } 311 | 312 | /** 313 | * JSON object as string. 314 | */ 315 | public String toString() { 316 | return options.toString(); 317 | } 318 | 319 | } 320 | -------------------------------------------------------------------------------- /android/src/main/java/com/remobile/localNotifications/notification/TriggerReceiver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | package com.remobile.localNotifications.notification; 25 | 26 | /** 27 | * The alarm receiver is triggered when a scheduled alarm is fired. This class 28 | * reads the information in the intent and displays this information in the 29 | * Android notification bar. The notification uses the default notification 30 | * sound and it vibrates the phone. 31 | */ 32 | public class TriggerReceiver extends AbstractTriggerReceiver { 33 | 34 | /** 35 | * Called when a local notification was triggered. Does present the local 36 | * notification and re-schedule the alarm if necessary. 37 | * 38 | * @param notification 39 | * Wrapper around the local notification 40 | * @param updated 41 | * If an update has triggered or the original 42 | */ 43 | @Override 44 | public void onTrigger (Notification notification, boolean updated) { 45 | notification.show(); 46 | } 47 | 48 | /** 49 | * Build notification specified by options. 50 | * 51 | * @param builder 52 | * Notification builder 53 | */ 54 | @Override 55 | public Notification buildNotification (Builder builder) { 56 | return builder.build(); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const LocalNotification = require('./libs/local-notification.js'); 4 | 5 | module.exports = LocalNotification; 6 | -------------------------------------------------------------------------------- /ios/RCTLocalNotifications.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 32C0ED761C2A9835009E47A9 /* APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C0ED6E1C2A9835009E47A9 /* APPLocalNotification.m */; }; 11 | 32C0ED771C2A9835009E47A9 /* APPLocalNotificationOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C0ED701C2A9835009E47A9 /* APPLocalNotificationOptions.m */; }; 12 | 32C0ED781C2A9835009E47A9 /* UIApplication+APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C0ED721C2A9835009E47A9 /* UIApplication+APPLocalNotification.m */; }; 13 | 32C0ED791C2A9835009E47A9 /* UILocalNotification+APPLocalNotification.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C0ED741C2A9835009E47A9 /* UILocalNotification+APPLocalNotification.m */; }; 14 | 32C0EDF11C2B9565009E47A9 /* AppDelegate+APPRegisterUserNotificationSettings.m in Sources */ = {isa = PBXBuildFile; fileRef = 32C0EDF01C2B9565009E47A9 /* AppDelegate+APPRegisterUserNotificationSettings.m */; }; 15 | /* End PBXBuildFile section */ 16 | 17 | /* Begin PBXCopyFilesBuildPhase section */ 18 | 32308DD41C16B5E900A2ED29 /* CopyFiles */ = { 19 | isa = PBXCopyFilesBuildPhase; 20 | buildActionMask = 2147483647; 21 | dstPath = "include/$(PRODUCT_NAME)"; 22 | dstSubfolderSpec = 16; 23 | files = ( 24 | ); 25 | runOnlyForDeploymentPostprocessing = 0; 26 | }; 27 | /* End PBXCopyFilesBuildPhase section */ 28 | 29 | /* Begin PBXFileReference section */ 30 | 32308DD61C16B5E900A2ED29 /* libRCTLocalNotifications.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRCTLocalNotifications.a; sourceTree = BUILT_PRODUCTS_DIR; }; 31 | 32C0ED6D1C2A9835009E47A9 /* APPLocalNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APPLocalNotification.h; sourceTree = ""; }; 32 | 32C0ED6E1C2A9835009E47A9 /* APPLocalNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = APPLocalNotification.m; sourceTree = ""; }; 33 | 32C0ED6F1C2A9835009E47A9 /* APPLocalNotificationOptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = APPLocalNotificationOptions.h; sourceTree = ""; }; 34 | 32C0ED701C2A9835009E47A9 /* APPLocalNotificationOptions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = APPLocalNotificationOptions.m; sourceTree = ""; }; 35 | 32C0ED711C2A9835009E47A9 /* UIApplication+APPLocalNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UIApplication+APPLocalNotification.h"; sourceTree = ""; }; 36 | 32C0ED721C2A9835009E47A9 /* UIApplication+APPLocalNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UIApplication+APPLocalNotification.m"; sourceTree = ""; }; 37 | 32C0ED731C2A9835009E47A9 /* UILocalNotification+APPLocalNotification.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "UILocalNotification+APPLocalNotification.h"; sourceTree = ""; }; 38 | 32C0ED741C2A9835009E47A9 /* UILocalNotification+APPLocalNotification.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "UILocalNotification+APPLocalNotification.m"; sourceTree = ""; }; 39 | 32C0EDEF1C2B9565009E47A9 /* AppDelegate+APPRegisterUserNotificationSettings.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "AppDelegate+APPRegisterUserNotificationSettings.h"; sourceTree = ""; }; 40 | 32C0EDF01C2B9565009E47A9 /* AppDelegate+APPRegisterUserNotificationSettings.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AppDelegate+APPRegisterUserNotificationSettings.m"; sourceTree = ""; }; 41 | /* End PBXFileReference section */ 42 | 43 | /* Begin PBXFrameworksBuildPhase section */ 44 | 32308DD31C16B5E900A2ED29 /* Frameworks */ = { 45 | isa = PBXFrameworksBuildPhase; 46 | buildActionMask = 2147483647; 47 | files = ( 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | /* End PBXFrameworksBuildPhase section */ 52 | 53 | /* Begin PBXGroup section */ 54 | 32308DCD1C16B5E900A2ED29 = { 55 | isa = PBXGroup; 56 | children = ( 57 | 32308DD81C16B5E900A2ED29 /* RCTLocalNotifications */, 58 | 32308DD71C16B5E900A2ED29 /* Products */, 59 | ); 60 | sourceTree = ""; 61 | }; 62 | 32308DD71C16B5E900A2ED29 /* Products */ = { 63 | isa = PBXGroup; 64 | children = ( 65 | 32308DD61C16B5E900A2ED29 /* libRCTLocalNotifications.a */, 66 | ); 67 | name = Products; 68 | sourceTree = ""; 69 | }; 70 | 32308DD81C16B5E900A2ED29 /* RCTLocalNotifications */ = { 71 | isa = PBXGroup; 72 | children = ( 73 | 32C0EDEF1C2B9565009E47A9 /* AppDelegate+APPRegisterUserNotificationSettings.h */, 74 | 32C0EDF01C2B9565009E47A9 /* AppDelegate+APPRegisterUserNotificationSettings.m */, 75 | 32C0ED6D1C2A9835009E47A9 /* APPLocalNotification.h */, 76 | 32C0ED6E1C2A9835009E47A9 /* APPLocalNotification.m */, 77 | 32C0ED6F1C2A9835009E47A9 /* APPLocalNotificationOptions.h */, 78 | 32C0ED701C2A9835009E47A9 /* APPLocalNotificationOptions.m */, 79 | 32C0ED711C2A9835009E47A9 /* UIApplication+APPLocalNotification.h */, 80 | 32C0ED721C2A9835009E47A9 /* UIApplication+APPLocalNotification.m */, 81 | 32C0ED731C2A9835009E47A9 /* UILocalNotification+APPLocalNotification.h */, 82 | 32C0ED741C2A9835009E47A9 /* UILocalNotification+APPLocalNotification.m */, 83 | ); 84 | path = RCTLocalNotifications; 85 | sourceTree = ""; 86 | }; 87 | /* End PBXGroup section */ 88 | 89 | /* Begin PBXNativeTarget section */ 90 | 32308DD51C16B5E900A2ED29 /* RCTLocalNotifications */ = { 91 | isa = PBXNativeTarget; 92 | buildConfigurationList = 32308DEA1C16B5E900A2ED29 /* Build configuration list for PBXNativeTarget "RCTLocalNotifications" */; 93 | buildPhases = ( 94 | 32308DD21C16B5E900A2ED29 /* Sources */, 95 | 32308DD31C16B5E900A2ED29 /* Frameworks */, 96 | 32308DD41C16B5E900A2ED29 /* CopyFiles */, 97 | ); 98 | buildRules = ( 99 | ); 100 | dependencies = ( 101 | ); 102 | name = RCTLocalNotifications; 103 | productName = RCTLocalNotifications; 104 | productReference = 32308DD61C16B5E900A2ED29 /* libRCTLocalNotifications.a */; 105 | productType = "com.apple.product-type.library.static"; 106 | }; 107 | /* End PBXNativeTarget section */ 108 | 109 | /* Begin PBXProject section */ 110 | 32308DCE1C16B5E900A2ED29 /* Project object */ = { 111 | isa = PBXProject; 112 | attributes = { 113 | LastUpgradeCheck = 0640; 114 | ORGANIZATIONNAME = remobile; 115 | TargetAttributes = { 116 | 32308DD51C16B5E900A2ED29 = { 117 | CreatedOnToolsVersion = 6.4; 118 | }; 119 | }; 120 | }; 121 | buildConfigurationList = 32308DD11C16B5E900A2ED29 /* Build configuration list for PBXProject "RCTLocalNotifications" */; 122 | compatibilityVersion = "Xcode 3.2"; 123 | developmentRegion = English; 124 | hasScannedForEncodings = 0; 125 | knownRegions = ( 126 | en, 127 | ); 128 | mainGroup = 32308DCD1C16B5E900A2ED29; 129 | productRefGroup = 32308DD71C16B5E900A2ED29 /* Products */; 130 | projectDirPath = ""; 131 | projectRoot = ""; 132 | targets = ( 133 | 32308DD51C16B5E900A2ED29 /* RCTLocalNotifications */, 134 | ); 135 | }; 136 | /* End PBXProject section */ 137 | 138 | /* Begin PBXSourcesBuildPhase section */ 139 | 32308DD21C16B5E900A2ED29 /* Sources */ = { 140 | isa = PBXSourcesBuildPhase; 141 | buildActionMask = 2147483647; 142 | files = ( 143 | 32C0ED761C2A9835009E47A9 /* APPLocalNotification.m in Sources */, 144 | 32C0ED771C2A9835009E47A9 /* APPLocalNotificationOptions.m in Sources */, 145 | 32C0EDF11C2B9565009E47A9 /* AppDelegate+APPRegisterUserNotificationSettings.m in Sources */, 146 | 32C0ED781C2A9835009E47A9 /* UIApplication+APPLocalNotification.m in Sources */, 147 | 32C0ED791C2A9835009E47A9 /* UILocalNotification+APPLocalNotification.m in Sources */, 148 | ); 149 | runOnlyForDeploymentPostprocessing = 0; 150 | }; 151 | /* End PBXSourcesBuildPhase section */ 152 | 153 | /* Begin XCBuildConfiguration section */ 154 | 32308DE81C16B5E900A2ED29 /* Debug */ = { 155 | isa = XCBuildConfiguration; 156 | buildSettings = { 157 | ALWAYS_SEARCH_USER_PATHS = NO; 158 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 159 | CLANG_CXX_LIBRARY = "libc++"; 160 | CLANG_ENABLE_MODULES = YES; 161 | CLANG_ENABLE_OBJC_ARC = YES; 162 | CLANG_WARN_BOOL_CONVERSION = YES; 163 | CLANG_WARN_CONSTANT_CONVERSION = YES; 164 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 165 | CLANG_WARN_EMPTY_BODY = YES; 166 | CLANG_WARN_ENUM_CONVERSION = YES; 167 | CLANG_WARN_INT_CONVERSION = YES; 168 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 169 | CLANG_WARN_UNREACHABLE_CODE = YES; 170 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 171 | COPY_PHASE_STRIP = NO; 172 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 173 | ENABLE_STRICT_OBJC_MSGSEND = YES; 174 | GCC_C_LANGUAGE_STANDARD = gnu99; 175 | GCC_DYNAMIC_NO_PIC = NO; 176 | GCC_NO_COMMON_BLOCKS = YES; 177 | GCC_OPTIMIZATION_LEVEL = 0; 178 | GCC_PREPROCESSOR_DEFINITIONS = ( 179 | "DEBUG=1", 180 | "$(inherited)", 181 | ); 182 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 183 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 184 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 185 | GCC_WARN_UNDECLARED_SELECTOR = YES; 186 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 187 | GCC_WARN_UNUSED_FUNCTION = YES; 188 | GCC_WARN_UNUSED_VARIABLE = YES; 189 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 190 | MTL_ENABLE_DEBUG_INFO = YES; 191 | ONLY_ACTIVE_ARCH = YES; 192 | SDKROOT = iphoneos; 193 | }; 194 | name = Debug; 195 | }; 196 | 32308DE91C16B5E900A2ED29 /* Release */ = { 197 | isa = XCBuildConfiguration; 198 | buildSettings = { 199 | ALWAYS_SEARCH_USER_PATHS = NO; 200 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 201 | CLANG_CXX_LIBRARY = "libc++"; 202 | CLANG_ENABLE_MODULES = YES; 203 | CLANG_ENABLE_OBJC_ARC = YES; 204 | CLANG_WARN_BOOL_CONVERSION = YES; 205 | CLANG_WARN_CONSTANT_CONVERSION = YES; 206 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 207 | CLANG_WARN_EMPTY_BODY = YES; 208 | CLANG_WARN_ENUM_CONVERSION = YES; 209 | CLANG_WARN_INT_CONVERSION = YES; 210 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 211 | CLANG_WARN_UNREACHABLE_CODE = YES; 212 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 213 | COPY_PHASE_STRIP = NO; 214 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 215 | ENABLE_NS_ASSERTIONS = NO; 216 | ENABLE_STRICT_OBJC_MSGSEND = YES; 217 | GCC_C_LANGUAGE_STANDARD = gnu99; 218 | GCC_NO_COMMON_BLOCKS = YES; 219 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 220 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 221 | GCC_WARN_UNDECLARED_SELECTOR = YES; 222 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 223 | GCC_WARN_UNUSED_FUNCTION = YES; 224 | GCC_WARN_UNUSED_VARIABLE = YES; 225 | IPHONEOS_DEPLOYMENT_TARGET = 7.0; 226 | MTL_ENABLE_DEBUG_INFO = NO; 227 | SDKROOT = iphoneos; 228 | VALIDATE_PRODUCT = YES; 229 | }; 230 | name = Release; 231 | }; 232 | 32308DEB1C16B5E900A2ED29 /* Debug */ = { 233 | isa = XCBuildConfiguration; 234 | buildSettings = { 235 | HEADER_SEARCH_PATHS = ( 236 | "$(inherited)", 237 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 238 | "$(SRCROOT)/../../react-native-cordova/ios/RCTCordova", 239 | "$(SRCROOT)/../../../../../ios/JFBSample", 240 | ); 241 | OTHER_LDFLAGS = "-ObjC"; 242 | PRODUCT_NAME = "$(TARGET_NAME)"; 243 | SKIP_INSTALL = YES; 244 | }; 245 | name = Debug; 246 | }; 247 | 32308DEC1C16B5E900A2ED29 /* Release */ = { 248 | isa = XCBuildConfiguration; 249 | buildSettings = { 250 | HEADER_SEARCH_PATHS = ( 251 | "$(inherited)", 252 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 253 | "$(SRCROOT)/../../react-native-cordova/ios/RCTCordova", 254 | "$(SRCROOT)/../../../../../ios/JFBSample", 255 | ); 256 | OTHER_LDFLAGS = "-ObjC"; 257 | PRODUCT_NAME = "$(TARGET_NAME)"; 258 | SKIP_INSTALL = YES; 259 | }; 260 | name = Release; 261 | }; 262 | /* End XCBuildConfiguration section */ 263 | 264 | /* Begin XCConfigurationList section */ 265 | 32308DD11C16B5E900A2ED29 /* Build configuration list for PBXProject "RCTLocalNotifications" */ = { 266 | isa = XCConfigurationList; 267 | buildConfigurations = ( 268 | 32308DE81C16B5E900A2ED29 /* Debug */, 269 | 32308DE91C16B5E900A2ED29 /* Release */, 270 | ); 271 | defaultConfigurationIsVisible = 0; 272 | defaultConfigurationName = Release; 273 | }; 274 | 32308DEA1C16B5E900A2ED29 /* Build configuration list for PBXNativeTarget "RCTLocalNotifications" */ = { 275 | isa = XCConfigurationList; 276 | buildConfigurations = ( 277 | 32308DEB1C16B5E900A2ED29 /* Debug */, 278 | 32308DEC1C16B5E900A2ED29 /* Release */, 279 | ); 280 | defaultConfigurationIsVisible = 0; 281 | defaultConfigurationName = Release; 282 | }; 283 | /* End XCConfigurationList section */ 284 | }; 285 | rootObject = 32308DCE1C16B5E900A2ED29 /* Project object */; 286 | } 287 | -------------------------------------------------------------------------------- /ios/RCTLocalNotifications.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/RCTLocalNotifications/APPLocalNotification.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | #import 25 | #import "CDVPlugin.h" 26 | 27 | @interface APPLocalNotification : CDVPlugin 28 | 29 | // Execute all queued events 30 | - (void) deviceready:(CDVInvokedUrlCommand*)command; 31 | 32 | // Inform if the app has the permission to show notifications 33 | - (void) hasPermission:(CDVInvokedUrlCommand*)command; 34 | // Register permission to show notifications 35 | - (void) registerPermission:(CDVInvokedUrlCommand*)command; 36 | 37 | // Schedule set of notifications 38 | - (void) schedule:(CDVInvokedUrlCommand*)command; 39 | // Update set of notifications 40 | - (void) update:(CDVInvokedUrlCommand*)command; 41 | // Cancel set of notifications 42 | - (void) cancel:(CDVInvokedUrlCommand*)command; 43 | // Cancel all notifications 44 | - (void) cancelAll:(CDVInvokedUrlCommand*)command; 45 | // Clear set of notifications 46 | - (void) clear:(CDVInvokedUrlCommand*)command; 47 | // Clear all notifications 48 | - (void) clearAll:(CDVInvokedUrlCommand*)command; 49 | 50 | // If a notification with an ID is present 51 | - (void) isPresent:(CDVInvokedUrlCommand*)command; 52 | // If a notification with an ID is scheduled 53 | - (void) isScheduled:(CDVInvokedUrlCommand*)command; 54 | // If a notification with an ID is triggered 55 | - (void) isTriggered:(CDVInvokedUrlCommand*)command; 56 | 57 | // List all ids from all local notifications 58 | - (void) getAllIds:(CDVInvokedUrlCommand*)command; 59 | // List all ids from all pending notifications 60 | - (void) getScheduledIds:(CDVInvokedUrlCommand*)command; 61 | // List all ids from all triggered notifications 62 | - (void) getTriggeredIds:(CDVInvokedUrlCommand*)command; 63 | 64 | // Propertys for given local notification 65 | - (void) getSingle:(CDVInvokedUrlCommand*)command; 66 | // Propertya for given scheduled notification 67 | - (void) getSingleScheduled:(CDVInvokedUrlCommand*)command; 68 | // Propertys for given triggered notification 69 | - (void) getSingleTriggered:(CDVInvokedUrlCommand*)command; 70 | 71 | // Property list for given local notifications 72 | - (void) getAll:(CDVInvokedUrlCommand*)command; 73 | // Property list for given scheduled notifications 74 | - (void) getScheduled:(CDVInvokedUrlCommand*)command; 75 | // Property list for given triggered notifications 76 | - (void) getTriggered:(CDVInvokedUrlCommand*)command; 77 | 78 | @end 79 | -------------------------------------------------------------------------------- /ios/RCTLocalNotifications/APPLocalNotification.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | #import "APPLocalNotification.h" 25 | #import "APPLocalNotificationOptions.h" 26 | #import "UIApplication+APPLocalNotification.h" 27 | #import "UILocalNotification+APPLocalNotification.h" 28 | #import "AppDelegate+APPRegisterUserNotificationSettings.h" 29 | 30 | NSString* const CDVLocalNotification = @"CDVLocalNotification"; 31 | 32 | @interface APPLocalNotification () 33 | 34 | // Retrieves the application state 35 | @property (readonly, getter=applicationState) NSString* applicationState; 36 | // All events will be queued until deviceready has been fired 37 | @property (readwrite, assign) BOOL deviceready; 38 | // Event queue 39 | @property (readonly, nonatomic, strong) NSMutableDictionary* eventQueue; 40 | // Needed when calling `registerPermission` 41 | @property (nonatomic, strong) CDVInvokedUrlCommand* command; 42 | 43 | @end 44 | 45 | @implementation APPLocalNotification 46 | 47 | @synthesize deviceready, eventQueue; 48 | @synthesize bridge = _bridge; 49 | 50 | RCT_EXPORT_MODULE(LocalNotification) 51 | 52 | - (id)init { 53 | self = [super init]; 54 | if (self) { 55 | [self pluginInitialize]; 56 | } 57 | return self; 58 | } 59 | 60 | RCT_EXPORT_CORDOVA_METHOD(deviceready); 61 | RCT_EXPORT_CORDOVA_METHOD(hasPermission); 62 | RCT_EXPORT_CORDOVA_METHOD(registerPermission); 63 | RCT_EXPORT_CORDOVA_METHOD(schedule); 64 | RCT_EXPORT_CORDOVA_METHOD(update); 65 | RCT_EXPORT_CORDOVA_METHOD(cancel); 66 | RCT_EXPORT_CORDOVA_METHOD(cancelAll); 67 | RCT_EXPORT_CORDOVA_METHOD(clear); 68 | RCT_EXPORT_CORDOVA_METHOD(clearAll); 69 | RCT_EXPORT_CORDOVA_METHOD(isPresent); 70 | RCT_EXPORT_CORDOVA_METHOD(isScheduled); 71 | RCT_EXPORT_CORDOVA_METHOD(isTriggered); 72 | RCT_EXPORT_CORDOVA_METHOD(getAllIds); 73 | RCT_EXPORT_CORDOVA_METHOD(getScheduledIds); 74 | RCT_EXPORT_CORDOVA_METHOD(getTriggeredIds); 75 | RCT_EXPORT_CORDOVA_METHOD(getSingle); 76 | RCT_EXPORT_CORDOVA_METHOD(getSingleScheduled); 77 | RCT_EXPORT_CORDOVA_METHOD(getSingleTriggered); 78 | RCT_EXPORT_CORDOVA_METHOD(getAll); 79 | RCT_EXPORT_CORDOVA_METHOD(getScheduled); 80 | RCT_EXPORT_CORDOVA_METHOD(getTriggered); 81 | 82 | - (void) sendJSEvent:(NSString *)event withParams:(NSString *)params { 83 | [self.bridge.eventDispatcher sendAppEventWithName:[NSString stringWithFormat:@"RCT-LOCAL-NOTIFICATION-%@", event] body:params]; 84 | } 85 | 86 | #pragma mark - 87 | #pragma mark Interface 88 | 89 | /** 90 | * Execute all queued events. 91 | */ 92 | - (void) deviceready:(CDVInvokedUrlCommand*)command 93 | { 94 | deviceready = YES; 95 | 96 | for (NSString* event in eventQueue) { 97 | NSString *params = eventQueue[event]; 98 | [self sendJSEvent:event withParams:params]; 99 | } 100 | 101 | [eventQueue removeAllObjects]; 102 | } 103 | 104 | /** 105 | * Schedule a set of notifications. 106 | * 107 | * @param properties 108 | * A dict of properties for each notification 109 | */ 110 | - (void) schedule:(CDVInvokedUrlCommand*)command 111 | { 112 | NSArray* notifications = command.arguments; 113 | 114 | [self.commandDelegate runInBackground:^{ 115 | for (NSDictionary* options in notifications) { 116 | UILocalNotification* notification; 117 | 118 | notification = [[UILocalNotification alloc] 119 | initWithOptions:options]; 120 | 121 | [self scheduleLocalNotification:[notification copy]]; 122 | [self fireEvent:@"schedule" notification:notification]; 123 | 124 | if (notifications.count > 1) { 125 | [NSThread sleepForTimeInterval:0.01]; 126 | } 127 | } 128 | 129 | [self execCallback:command]; 130 | }]; 131 | } 132 | 133 | /** 134 | * Update a set of notifications. 135 | * 136 | * @param properties 137 | * A dict of properties for each notification 138 | */ 139 | - (void) update:(CDVInvokedUrlCommand*)command 140 | { 141 | NSArray* notifications = command.arguments; 142 | 143 | [self.commandDelegate runInBackground:^{ 144 | for (NSDictionary* options in notifications) { 145 | NSNumber* id = [options objectForKey:@"id"]; 146 | UILocalNotification* notification; 147 | 148 | notification = [self.app localNotificationWithId:id]; 149 | 150 | if (!notification) 151 | continue; 152 | 153 | [self updateLocalNotification:[notification copy] 154 | withOptions:options]; 155 | 156 | [self fireEvent:@"update" notification:notification]; 157 | 158 | if (notifications.count > 1) { 159 | [NSThread sleepForTimeInterval:0.01]; 160 | } 161 | } 162 | 163 | [self execCallback:command]; 164 | }]; 165 | } 166 | 167 | /** 168 | * Cancel a set of notifications. 169 | * 170 | * @param ids 171 | * The IDs of the notifications 172 | */ 173 | - (void) cancel:(CDVInvokedUrlCommand*)command 174 | { 175 | [self.commandDelegate runInBackground:^{ 176 | for (NSNumber* id in command.arguments) { 177 | UILocalNotification* notification; 178 | 179 | notification = [self.app localNotificationWithId:id]; 180 | 181 | if (!notification) 182 | continue; 183 | 184 | [self.app cancelLocalNotification:notification]; 185 | [self fireEvent:@"cancel" notification:notification]; 186 | } 187 | 188 | [self execCallback:command]; 189 | }]; 190 | } 191 | 192 | /** 193 | * Cancel all local notifications. 194 | */ 195 | - (void) cancelAll:(CDVInvokedUrlCommand*)command 196 | { 197 | [self.commandDelegate runInBackground:^{ 198 | [self cancelAllLocalNotifications]; 199 | [self fireEvent:@"cancelall"]; 200 | [self execCallback:command]; 201 | }]; 202 | } 203 | 204 | /** 205 | * Clear a set of notifications. 206 | * 207 | * @param ids 208 | * The IDs of the notifications 209 | */ 210 | - (void) clear:(CDVInvokedUrlCommand*)command 211 | { 212 | [self.commandDelegate runInBackground:^{ 213 | for (NSNumber* id in command.arguments) { 214 | UILocalNotification* notification; 215 | 216 | notification = [self.app localNotificationWithId:id]; 217 | 218 | if (!notification) 219 | continue; 220 | 221 | [self.app clearLocalNotification:notification]; 222 | [self fireEvent:@"clear" notification:notification]; 223 | } 224 | 225 | [self execCallback:command]; 226 | }]; 227 | } 228 | 229 | /** 230 | * Clear all local notifications. 231 | */ 232 | - (void) clearAll:(CDVInvokedUrlCommand*)command 233 | { 234 | [self.commandDelegate runInBackground:^{ 235 | [self clearAllLocalNotifications]; 236 | [self fireEvent:@"clearall"]; 237 | [self execCallback:command]; 238 | }]; 239 | } 240 | 241 | /** 242 | * If a notification by ID is present. 243 | * 244 | * @param id 245 | * The ID of the notification 246 | */ 247 | - (void) isPresent:(CDVInvokedUrlCommand *)command 248 | { 249 | [self isPresent:command type:NotifcationTypeAll]; 250 | } 251 | 252 | /** 253 | * If a notification by ID is scheduled. 254 | * 255 | * @param id 256 | * The ID of the notification 257 | */ 258 | - (void) isScheduled:(CDVInvokedUrlCommand*)command 259 | { 260 | [self isPresent:command type:NotifcationTypeScheduled]; 261 | } 262 | 263 | /** 264 | * Check if a notification with an ID is triggered. 265 | * 266 | * @param id 267 | * The ID of the notification 268 | */ 269 | - (void) isTriggered:(CDVInvokedUrlCommand*)command 270 | { 271 | [self isPresent:command type:NotifcationTypeTriggered]; 272 | } 273 | 274 | /** 275 | * Check if a notification with an ID exists. 276 | * 277 | * @param type 278 | * The notification life cycle type 279 | */ 280 | - (void) isPresent:(CDVInvokedUrlCommand*)command 281 | type:(APPLocalNotificationType)type; 282 | { 283 | [self.commandDelegate runInBackground:^{ 284 | NSNumber* id = [command argumentAtIndex:0]; 285 | BOOL exist; 286 | 287 | CDVPluginResult* result; 288 | 289 | if (type == NotifcationTypeAll) { 290 | exist = [self.app localNotificationExist:id]; 291 | } else { 292 | exist = [self.app localNotificationExist:id type:type]; 293 | } 294 | 295 | result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK 296 | messageAsBool:exist]; 297 | 298 | [self.commandDelegate sendPluginResult:result 299 | callbackId:command.callbackId]; 300 | }]; 301 | } 302 | 303 | /** 304 | * List all ids from all local notifications. 305 | */ 306 | - (void) getAllIds:(CDVInvokedUrlCommand*)command 307 | { 308 | [self getIds:command byType:NotifcationTypeAll]; 309 | } 310 | 311 | /** 312 | * List all ids from all pending notifications. 313 | */ 314 | - (void) getScheduledIds:(CDVInvokedUrlCommand*)command 315 | { 316 | [self getIds:command byType:NotifcationTypeScheduled]; 317 | } 318 | 319 | /** 320 | * List all ids from all triggered notifications. 321 | */ 322 | - (void) getTriggeredIds:(CDVInvokedUrlCommand*)command 323 | { 324 | [self getIds:command byType:NotifcationTypeTriggered]; 325 | } 326 | 327 | /** 328 | * List of ids for given local notifications. 329 | * 330 | * @param type 331 | * Notification life cycle type 332 | * @param ids 333 | * The IDs of the notifications 334 | */ 335 | - (void) getIds:(CDVInvokedUrlCommand*)command 336 | byType:(APPLocalNotificationType)type; 337 | { 338 | [self.commandDelegate runInBackground:^{ 339 | CDVPluginResult* result; 340 | NSArray* ids; 341 | 342 | if (type == NotifcationTypeAll) { 343 | ids = [self.app localNotificationIds]; 344 | } else { 345 | ids = [self.app localNotificationIdsByType:type]; 346 | } 347 | 348 | result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK 349 | messageAsArray:ids]; 350 | 351 | [self.commandDelegate sendPluginResult:result 352 | callbackId:command.callbackId]; 353 | }]; 354 | } 355 | 356 | /** 357 | * Propertys for given local notification. 358 | */ 359 | - (void) getSingle:(CDVInvokedUrlCommand*)command 360 | { 361 | [self getOption:command byType:NotifcationTypeAll]; 362 | } 363 | 364 | /** 365 | * Propertya for given scheduled notification. 366 | */ 367 | - (void) getSingleScheduled:(CDVInvokedUrlCommand*)command 368 | { 369 | [self getOption:command byType:NotifcationTypeScheduled]; 370 | } 371 | 372 | // Propertys for given triggered notification 373 | - (void) getSingleTriggered:(CDVInvokedUrlCommand*)command 374 | { 375 | [self getOption:command byType:NotifcationTypeTriggered]; 376 | } 377 | 378 | /** 379 | * Property list for given local notifications. 380 | * 381 | * @param ids 382 | * The IDs of the notifications 383 | */ 384 | - (void) getAll:(CDVInvokedUrlCommand*)command 385 | { 386 | [self getOptions:command byType:NotifcationTypeAll]; 387 | } 388 | 389 | /** 390 | * Property list for given scheduled notifications. 391 | * 392 | * @param ids 393 | * The IDs of the notifications 394 | */ 395 | - (void) getScheduled:(CDVInvokedUrlCommand*)command 396 | { 397 | [self getOptions:command byType:NotifcationTypeScheduled]; 398 | } 399 | 400 | /** 401 | * Property list for given triggered notifications. 402 | * 403 | * @param ids 404 | * The IDs of the notifications 405 | */ 406 | - (void) getTriggered:(CDVInvokedUrlCommand *)command 407 | { 408 | [self getOptions:command byType:NotifcationTypeTriggered]; 409 | } 410 | 411 | /** 412 | * Propertys for given triggered notification. 413 | * 414 | * @param type 415 | * Notification life cycle type 416 | * @param ids 417 | * The ID of the notification 418 | */ 419 | - (void) getOption:(CDVInvokedUrlCommand*)command 420 | byType:(APPLocalNotificationType)type; 421 | { 422 | [self.commandDelegate runInBackground:^{ 423 | NSArray* ids = command.arguments; 424 | NSArray* notifications; 425 | CDVPluginResult* result; 426 | 427 | if (type == NotifcationTypeAll) { 428 | notifications = [self.app localNotificationOptionsById:ids]; 429 | } 430 | else { 431 | notifications = [self.app localNotificationOptionsByType:type 432 | andId:ids]; 433 | } 434 | 435 | result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK 436 | messageAsDictionary:[notifications firstObject]]; 437 | 438 | [self.commandDelegate sendPluginResult:result 439 | callbackId:command.callbackId]; 440 | }]; 441 | } 442 | 443 | /** 444 | * Property list for given triggered notifications. 445 | * 446 | * @param type 447 | * Notification life cycle type 448 | * @param ids 449 | * The IDs of the notifications 450 | */ 451 | - (void) getOptions:(CDVInvokedUrlCommand*)command 452 | byType:(APPLocalNotificationType)type; 453 | { 454 | [self.commandDelegate runInBackground:^{ 455 | NSArray* ids = command.arguments; 456 | NSArray* notifications; 457 | CDVPluginResult* result; 458 | 459 | if (type == NotifcationTypeAll && ids.count == 0) { 460 | notifications = [self.app localNotificationOptions]; 461 | } 462 | else if (type == NotifcationTypeAll) { 463 | notifications = [self.app localNotificationOptionsById:ids]; 464 | } 465 | else if (ids.count == 0) { 466 | notifications = [self.app localNotificationOptionsByType:type]; 467 | } 468 | else { 469 | notifications = [self.app localNotificationOptionsByType:type 470 | andId:ids]; 471 | } 472 | 473 | result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK 474 | messageAsArray:notifications]; 475 | 476 | [self.commandDelegate sendPluginResult:result 477 | callbackId:command.callbackId]; 478 | }]; 479 | } 480 | 481 | /** 482 | * Inform if the app has the permission to show 483 | * badges and local notifications. 484 | */ 485 | - (void) hasPermission:(CDVInvokedUrlCommand*)command 486 | { 487 | [self.commandDelegate runInBackground:^{ 488 | CDVPluginResult* result; 489 | BOOL hasPermission; 490 | 491 | hasPermission = [self.app hasPermissionToScheduleLocalNotifications]; 492 | 493 | result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK 494 | messageAsBool:hasPermission]; 495 | 496 | [self.commandDelegate sendPluginResult:result 497 | callbackId:command.callbackId]; 498 | }]; 499 | } 500 | 501 | /** 502 | * Ask for permission to show badges. 503 | */ 504 | - (void) registerPermission:(CDVInvokedUrlCommand*)command 505 | { 506 | if ([[UIApplication sharedApplication] 507 | respondsToSelector:@selector(registerUserNotificationSettings:)]) 508 | { 509 | _command = command; 510 | 511 | [self.commandDelegate runInBackground:^{ 512 | [self.app registerPermissionToScheduleLocalNotifications]; 513 | }]; 514 | } else { 515 | [self hasPermission:command]; 516 | } 517 | } 518 | 519 | #pragma mark - 520 | #pragma mark Core Logic 521 | 522 | /** 523 | * Schedule the local notification. 524 | */ 525 | - (void) scheduleLocalNotification:(UILocalNotification*)notification 526 | { 527 | [self cancelForerunnerLocalNotification:notification]; 528 | [self.app scheduleLocalNotification:notification]; 529 | } 530 | 531 | /** 532 | * Update the local notification. 533 | */ 534 | - (void) updateLocalNotification:(UILocalNotification*)notification 535 | withOptions:(NSDictionary*)newOptions 536 | { 537 | NSMutableDictionary* options = [notification.userInfo mutableCopy]; 538 | 539 | [options addEntriesFromDictionary:newOptions]; 540 | [options setObject:[NSDate date] forKey:@"updatedAt"]; 541 | 542 | notification = [[UILocalNotification alloc] 543 | initWithOptions:options]; 544 | 545 | [self scheduleLocalNotification:notification]; 546 | } 547 | 548 | /** 549 | * Cancel all local notifications. 550 | */ 551 | - (void) cancelAllLocalNotifications 552 | { 553 | [self.app cancelAllLocalNotifications]; 554 | [self.app setApplicationIconBadgeNumber:0]; 555 | } 556 | 557 | /** 558 | * Clear all local notifications. 559 | */ 560 | - (void) clearAllLocalNotifications 561 | { 562 | [self.app clearAllLocalNotifications]; 563 | [self.app setApplicationIconBadgeNumber:0]; 564 | } 565 | 566 | /** 567 | * Cancel a maybe given forerunner with the same ID. 568 | */ 569 | - (void) cancelForerunnerLocalNotification:(UILocalNotification*)notification 570 | { 571 | NSNumber* id = notification.options.id; 572 | UILocalNotification* forerunner; 573 | 574 | forerunner = [self.app localNotificationWithId:id]; 575 | 576 | if (!forerunner) 577 | return; 578 | 579 | [self.app cancelLocalNotification:forerunner]; 580 | } 581 | 582 | /** 583 | * Cancels all non-repeating local notification older then 584 | * a specific amount of seconds 585 | */ 586 | - (void) cancelAllNotificationsWhichAreOlderThen:(float)seconds 587 | { 588 | NSArray* notifications; 589 | 590 | notifications = [self.app localNotifications]; 591 | 592 | for (UILocalNotification* notification in notifications) 593 | { 594 | if (![notification isRepeating] 595 | && notification.timeIntervalSinceFireDate > seconds) 596 | { 597 | [self.app cancelLocalNotification:notification]; 598 | [self fireEvent:@"cancel" notification:notification]; 599 | } 600 | } 601 | } 602 | 603 | #pragma mark - 604 | #pragma mark Delegates 605 | 606 | /** 607 | * Calls the cancel or trigger event after a local notification was received. 608 | * Cancels the local notification if autoCancel was set to true. 609 | */ 610 | - (void) didReceiveLocalNotification:(NSNotification*)localNotification 611 | { 612 | UILocalNotification* notification = [localNotification object]; 613 | 614 | if ([notification userInfo] == NULL || [notification wasUpdated] ) 615 | return; 616 | 617 | NSTimeInterval timeInterval = [notification timeIntervalSinceLastTrigger]; 618 | 619 | NSString* event = (timeInterval <= 1 && deviceready) ? @"trigger" : @"click"; 620 | 621 | [self fireEvent:event notification:notification]; 622 | 623 | if (![event isEqualToString:@"click"]) 624 | return; 625 | 626 | if ([notification isRepeating]) { 627 | [self fireEvent:@"clear" notification:notification]; 628 | } else { 629 | [self.app cancelLocalNotification:notification]; 630 | [self fireEvent:@"cancel" notification:notification]; 631 | } 632 | } 633 | 634 | /** 635 | * Called when app has started 636 | * (by clicking on a local notification). 637 | */ 638 | - (void) didFinishLaunchingWithOptions:(NSNotification*)notification 639 | { 640 | NSDictionary* launchOptions = [notification userInfo]; 641 | 642 | UILocalNotification* localNotification; 643 | 644 | localNotification = [launchOptions objectForKey: 645 | UIApplicationLaunchOptionsLocalNotificationKey]; 646 | 647 | if (localNotification) { 648 | [self didReceiveLocalNotification: 649 | [NSNotification notificationWithName:CDVLocalNotification 650 | object:localNotification]]; 651 | } 652 | } 653 | 654 | /** 655 | * Called on otification settings registration is completed. 656 | */ 657 | - (void) didRegisterUserNotificationSettings:(UIUserNotificationSettings*)settings 658 | { 659 | if (_command) 660 | { 661 | [self hasPermission:_command]; 662 | _command = NULL; 663 | } 664 | } 665 | 666 | #pragma mark - 667 | #pragma mark Life Cycle 668 | 669 | /** 670 | * Registers obervers after plugin was initialized. 671 | */ 672 | - (void) pluginInitialize 673 | { 674 | NSNotificationCenter* center = [NSNotificationCenter 675 | defaultCenter]; 676 | 677 | eventQueue = [[NSMutableDictionary alloc] init]; 678 | 679 | [center addObserver:self 680 | selector:@selector(didReceiveLocalNotification:) 681 | name:CDVLocalNotification 682 | object:nil]; 683 | 684 | [center addObserver:self 685 | selector:@selector(didFinishLaunchingWithOptions:) 686 | name:UIApplicationDidFinishLaunchingNotification 687 | object:nil]; 688 | 689 | [center addObserver:self 690 | selector:@selector(didRegisterUserNotificationSettings:) 691 | name:UIApplicationRegisterUserNotificationSettings 692 | object:nil]; 693 | } 694 | 695 | /** 696 | * Clears all single repeating notifications which are older then 5 days 697 | * before the app terminates. 698 | */ 699 | - (void) onAppTerminate 700 | { 701 | [self cancelAllNotificationsWhichAreOlderThen:432000]; 702 | } 703 | 704 | #pragma mark - 705 | #pragma mark Helper 706 | 707 | /** 708 | * Retrieves the application state 709 | * 710 | * @return 711 | * Either "background" or "foreground" 712 | */ 713 | - (NSString*) applicationState 714 | { 715 | UIApplicationState state = [self.app applicationState]; 716 | 717 | bool isActive = state == UIApplicationStateActive; 718 | 719 | return isActive ? @"foreground" : @"background"; 720 | } 721 | 722 | /** 723 | * Simply invokes the callback without any parameter. 724 | */ 725 | - (void) execCallback:(CDVInvokedUrlCommand*)command 726 | { 727 | CDVPluginResult *result = [CDVPluginResult 728 | resultWithStatus:CDVCommandStatus_OK]; 729 | 730 | [self.commandDelegate sendPluginResult:result 731 | callbackId:command.callbackId]; 732 | } 733 | 734 | /** 735 | * Short hand for shared application instance. 736 | */ 737 | - (UIApplication*) app 738 | { 739 | return [UIApplication sharedApplication]; 740 | } 741 | 742 | /** 743 | * Fire general event. 744 | */ 745 | - (void) fireEvent:(NSString*)event 746 | { 747 | [self fireEvent:event notification:NULL]; 748 | } 749 | 750 | /** 751 | * Fire event for local notification. 752 | */ 753 | - (void) fireEvent:(NSString*)event notification:(UILocalNotification*)notification 754 | { 755 | NSString* params = [NSString stringWithFormat: 756 | @"\"%@\"", self.applicationState]; 757 | 758 | if (notification) { 759 | NSString* args = [notification encodeToJSON]; 760 | 761 | params = [NSString stringWithFormat: 762 | @"%@,'%@'", 763 | args, self.applicationState]; 764 | } 765 | 766 | if (deviceready) { 767 | [self sendJSEvent:event withParams:params]; 768 | } else { 769 | [self.eventQueue setObject:params forKey:event]; 770 | } 771 | } 772 | 773 | @end 774 | -------------------------------------------------------------------------------- /ios/RCTLocalNotifications/APPLocalNotificationOptions.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | #import 24 | #import 25 | @interface APPLocalNotificationOptions : NSObject 26 | 27 | - (id) initWithDict:(NSDictionary*)dict; 28 | 29 | @property (readonly, getter=id) NSNumber* id; 30 | @property (readonly, getter=badgeNumber) NSInteger badgeNumber; 31 | @property (readonly, getter=alertBody) NSString* alertBody; 32 | @property (readonly, getter=soundName) NSString* soundName; 33 | @property (readonly, getter=fireDate) NSDate* fireDate; 34 | @property (readonly, getter=repeatInterval) NSCalendarUnit repeatInterval; 35 | @property (readonly, getter=userInfo) NSDictionary* userInfo; 36 | 37 | // If it's a repeating notification 38 | - (BOOL) isRepeating; 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /ios/RCTLocalNotifications/APPLocalNotificationOptions.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | #import "APPLocalNotificationOptions.h" 25 | 26 | // Default sound ressource path 27 | NSString* const DEFAULT_SOUND = @"res://platform_default"; 28 | 29 | @interface APPLocalNotificationOptions () 30 | 31 | // The dictionary which contains all notification properties 32 | @property(nonatomic, retain) NSDictionary* dict; 33 | 34 | @end 35 | 36 | @implementation APPLocalNotificationOptions 37 | 38 | @synthesize dict; 39 | 40 | #pragma mark - 41 | #pragma mark Initialization 42 | 43 | /** 44 | * Initialize the object with the given options when calling on JS side: 45 | * notification.local.add(options) 46 | */ 47 | - (id) initWithDict:(NSDictionary*)dictionary 48 | { 49 | self = [self init]; 50 | 51 | self.dict = dictionary; 52 | 53 | return self; 54 | } 55 | 56 | #pragma mark - 57 | #pragma mark Attributes 58 | 59 | /** 60 | * The notification's ID. 61 | */ 62 | - (NSNumber*) id 63 | { 64 | NSInteger id = [[dict objectForKey:@"id"] integerValue]; 65 | 66 | return [NSNumber numberWithInteger:id]; 67 | } 68 | 69 | /** 70 | * The notification's title. 71 | */ 72 | - (NSString*) title 73 | { 74 | return [dict objectForKey:@"title"]; 75 | } 76 | 77 | /** 78 | * The notification's message. 79 | */ 80 | - (NSString*) text 81 | { 82 | return [dict objectForKey:@"text"]; 83 | } 84 | 85 | /** 86 | * The notification's badge number. 87 | */ 88 | - (NSInteger) badgeNumber 89 | { 90 | return [[dict objectForKey:@"badge"] intValue]; 91 | } 92 | 93 | #pragma mark - 94 | #pragma mark Complex Attributes 95 | 96 | /** 97 | * The notification's alert body. 98 | */ 99 | - (NSString*) alertBody 100 | { 101 | NSString* title = [self title]; 102 | NSString* msg = [self text]; 103 | 104 | NSString* alertBody = msg; 105 | 106 | if (![self stringIsNullOrEmpty:title]) 107 | { 108 | alertBody = [NSString stringWithFormat:@"%@\n%@", 109 | title, msg]; 110 | } 111 | 112 | return alertBody; 113 | } 114 | 115 | /** 116 | * The notification's sound path. 117 | */ 118 | - (NSString*) soundName 119 | { 120 | NSString* path = [dict objectForKey:@"sound"]; 121 | 122 | if ([self stringIsNullOrEmpty:path]) 123 | return NULL; 124 | 125 | if ([path isEqualToString:DEFAULT_SOUND]) 126 | return UILocalNotificationDefaultSoundName; 127 | 128 | if ([path hasPrefix:@"file:/"]) 129 | return [self soundNameForAsset:path]; 130 | 131 | if ([path hasPrefix:@"res:"]) 132 | return [self soundNameForResource:path]; 133 | 134 | return NULL; 135 | } 136 | 137 | /** 138 | * The notification's fire date. 139 | */ 140 | - (NSDate*) fireDate 141 | { 142 | double timestamp = [[dict objectForKey:@"at"] 143 | doubleValue]; 144 | 145 | return [NSDate dateWithTimeIntervalSince1970:timestamp]; 146 | } 147 | 148 | /** 149 | * The notification's repeat interval. 150 | */ 151 | - (NSCalendarUnit) repeatInterval 152 | { 153 | NSString* interval = [dict objectForKey:@"every"]; 154 | 155 | if ([self stringIsNullOrEmpty:interval]) { 156 | return NSCalendarUnitEra; 157 | } 158 | else if ([interval isEqualToString:@"second"]) { 159 | return NSCalendarUnitSecond; 160 | } 161 | else if ([interval isEqualToString:@"minute"]) { 162 | return NSCalendarUnitMinute; 163 | } 164 | else if ([interval isEqualToString:@"hour"]) { 165 | return NSCalendarUnitHour; 166 | } 167 | else if ([interval isEqualToString:@"day"]) { 168 | return NSCalendarUnitDay; 169 | } 170 | else if ([interval isEqualToString:@"week"]) { 171 | return NSCalendarUnitWeekOfYear; 172 | } 173 | else if ([interval isEqualToString:@"month"]) { 174 | return NSCalendarUnitMonth; 175 | } 176 | else if ([interval isEqualToString:@"quarter"]) { 177 | return NSCalendarUnitQuarter; 178 | } 179 | else if ([interval isEqualToString:@"year"]) { 180 | return NSCalendarUnitYear; 181 | } 182 | 183 | return NSCalendarUnitEra; 184 | } 185 | 186 | #pragma mark - 187 | #pragma mark Methods 188 | 189 | /** 190 | * The notification's user info dict. 191 | */ 192 | - (NSDictionary*) userInfo 193 | { 194 | if ([dict objectForKey:@"updatedAt"]) { 195 | NSMutableDictionary* data = [dict mutableCopy]; 196 | 197 | [data removeObjectForKey:@"updatedAt"]; 198 | 199 | return data; 200 | } 201 | 202 | return dict; 203 | } 204 | 205 | /** 206 | * If it's a repeating notification. 207 | */ 208 | - (BOOL) isRepeating 209 | { 210 | NSCalendarUnit interval = self.repeatInterval; 211 | 212 | return !(interval == NSCalendarUnitEra || interval == 0); 213 | } 214 | 215 | #pragma mark - 216 | #pragma mark Helpers 217 | 218 | /** 219 | * Convert relative path to valid sound name attribute. 220 | */ 221 | - (NSString*) soundNameForAsset:(NSString*)path 222 | { 223 | return [path stringByReplacingOccurrencesOfString:@"file:/" 224 | withString:@"www"]; 225 | } 226 | 227 | /** 228 | * Convert resource path to valid sound name attribute. 229 | */ 230 | - (NSString*) soundNameForResource:(NSString*)path 231 | { 232 | return [path pathComponents].lastObject; 233 | } 234 | 235 | /** 236 | * If the string is empty. 237 | */ 238 | - (BOOL) stringIsNullOrEmpty:(NSString*)str 239 | { 240 | if (str == (NSString*)[NSNull null]) 241 | return YES; 242 | 243 | if ([str isEqualToString:@""]) 244 | return YES; 245 | 246 | return NO; 247 | } 248 | 249 | @end 250 | -------------------------------------------------------------------------------- /ios/RCTLocalNotifications/AppDelegate+APPRegisterUserNotificationSettings.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | #import "AppDelegate.h" 25 | #import 26 | #import 27 | 28 | extern NSString* const UIApplicationRegisterUserNotificationSettings; 29 | 30 | @interface AppDelegate (APPRegisterUserNotificationSettings) 31 | 32 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 33 | // Tells the delegate what types of notifications may be used 34 | - (void) application:(UIApplication*)application 35 | didRegisterUserNotificationSettings:(UIUserNotificationSettings*)settings; 36 | #endif 37 | 38 | @end -------------------------------------------------------------------------------- /ios/RCTLocalNotifications/AppDelegate+APPRegisterUserNotificationSettings.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | #import "AppDelegate+APPRegisterUserNotificationSettings.h" 24 | #import 25 | 26 | NSString* const UIApplicationRegisterUserNotificationSettings = @"UIApplicationRegisterUserNotificationSettings"; 27 | 28 | @implementation AppDelegate (APPRegisterUserNotificationSettings) 29 | 30 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 31 | /** 32 | * Tells the delegate what types of notifications may be used 33 | * to get the user’s attention. 34 | */ 35 | - (void) application:(UIApplication*)application 36 | didRegisterUserNotificationSettings:(UIUserNotificationSettings*)settings 37 | { 38 | NSNotificationCenter* center = [NSNotificationCenter 39 | defaultCenter]; 40 | 41 | // re-post (broadcast) 42 | [center postNotificationName:UIApplicationRegisterUserNotificationSettings 43 | object:settings]; 44 | } 45 | #endif 46 | 47 | @end -------------------------------------------------------------------------------- /ios/RCTLocalNotifications/UIApplication+APPLocalNotification.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | #import "UILocalNotification+APPLocalNotification.h" 25 | 26 | @interface UIApplication (APPLocalNotification) 27 | 28 | @property (readonly, getter=localNotifications) NSArray* localNotifications; 29 | @property (readonly, getter=localNotificationIds) NSArray* localNotificationIds; 30 | 31 | // If the app has the permission to schedule local notifications 32 | - (BOOL) hasPermissionToScheduleLocalNotifications; 33 | // Ask for permission to schedule local notifications 34 | - (void) registerPermissionToScheduleLocalNotifications; 35 | 36 | // List of all local notification IDs from given type 37 | - (NSArray*) localNotificationIdsByType:(APPLocalNotificationType)type; 38 | 39 | // If local notification with ID exists 40 | - (BOOL) localNotificationExist:(NSNumber*)id; 41 | // If local notification with ID and type exists 42 | - (BOOL) localNotificationExist:(NSNumber*)id type:(APPLocalNotificationType)type; 43 | 44 | // Local notification by ID 45 | - (UILocalNotification*) localNotificationWithId:(NSNumber*)id; 46 | // Local notification by ID and type 47 | - (UILocalNotification*) localNotificationWithId:(NSNumber*)id andType:(APPLocalNotificationType)type; 48 | 49 | // Property list from all local notifications 50 | - (NSArray*) localNotificationOptions; 51 | // Property list from given local notifications 52 | - (NSArray*) localNotificationOptionsById:(NSArray*)ids; 53 | // Property list from all local notifications with type constraint 54 | - (NSArray*) localNotificationOptionsByType:(APPLocalNotificationType)type; 55 | // Property list from given local notifications with type constraint 56 | - (NSArray*) localNotificationOptionsByType:(APPLocalNotificationType)type andId:(NSArray*)ids; 57 | 58 | // Clear single local notfications 59 | - (void) clearLocalNotification:(UILocalNotification*)notification; 60 | // Clear all local notfications 61 | - (void) clearAllLocalNotifications; 62 | 63 | @end 64 | -------------------------------------------------------------------------------- /ios/RCTLocalNotifications/UIApplication+APPLocalNotification.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | #import "UIApplication+APPLocalNotification.h" 25 | #import "UILocalNotification+APPLocalNotification.h" 26 | 27 | @implementation UIApplication (APPLocalNotification) 28 | 29 | #pragma mark - 30 | #pragma mark Permissions 31 | 32 | /** 33 | * If the app has the permission to schedule local notifications. 34 | */ 35 | - (BOOL) hasPermissionToScheduleLocalNotifications 36 | { 37 | if ([[UIApplication sharedApplication] 38 | respondsToSelector:@selector(registerUserNotificationSettings:)]) 39 | { 40 | UIUserNotificationType types; 41 | UIUserNotificationSettings *settings; 42 | 43 | settings = [[UIApplication sharedApplication] 44 | currentUserNotificationSettings]; 45 | 46 | types = UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound; 47 | 48 | return (settings.types & types); 49 | } else { 50 | return YES; 51 | } 52 | } 53 | 54 | /** 55 | * Ask for permission to schedule local notifications. 56 | */ 57 | - (void) registerPermissionToScheduleLocalNotifications 58 | { 59 | if ([[UIApplication sharedApplication] 60 | respondsToSelector:@selector(registerUserNotificationSettings:)]) 61 | { 62 | UIUserNotificationType types; 63 | UIUserNotificationSettings *settings; 64 | 65 | settings = [[UIApplication sharedApplication] 66 | currentUserNotificationSettings]; 67 | 68 | types = settings.types|UIUserNotificationTypeAlert|UIUserNotificationTypeBadge|UIUserNotificationTypeSound; 69 | 70 | settings = [UIUserNotificationSettings settingsForTypes:types 71 | categories:nil]; 72 | 73 | [[UIApplication sharedApplication] 74 | registerUserNotificationSettings:settings]; 75 | } 76 | } 77 | 78 | #pragma mark - 79 | #pragma mark LocalNotifications 80 | 81 | /** 82 | * List of all local notifications which have been added 83 | * but not yet removed from the notification center. 84 | */ 85 | - (NSArray*) localNotifications 86 | { 87 | NSArray* scheduledNotifications = self.scheduledLocalNotifications; 88 | NSMutableArray* notifications = [[NSMutableArray alloc] init]; 89 | 90 | for (UILocalNotification* notification in scheduledNotifications) 91 | { 92 | if (notification) { 93 | [notifications addObject:notification]; 94 | } 95 | } 96 | 97 | return notifications; 98 | } 99 | 100 | /** 101 | * List of all triggered local notifications which have been scheduled 102 | * and not yet removed the notification center. 103 | */ 104 | - (NSArray*) triggeredLocalNotifications 105 | { 106 | NSArray* notifications = self.localNotifications; 107 | NSMutableArray* triggeredNotifications = [[NSMutableArray alloc] init]; 108 | 109 | for (UILocalNotification* notification in notifications) 110 | { 111 | if ([notification isTriggered]) { 112 | [triggeredNotifications addObject:notification]; 113 | } 114 | } 115 | 116 | return triggeredNotifications; 117 | } 118 | 119 | /** 120 | * List of all local notifications IDs. 121 | */ 122 | - (NSArray*) localNotificationIds 123 | { 124 | NSArray* notifications = self.localNotifications; 125 | NSMutableArray* ids = [[NSMutableArray alloc] init]; 126 | 127 | for (UILocalNotification* notification in notifications) 128 | { 129 | [ids addObject:notification.options.id]; 130 | } 131 | 132 | return ids; 133 | } 134 | 135 | /** 136 | * List of all local notifications IDs from given type. 137 | * 138 | * @param type 139 | * Notification life cycle type 140 | */ 141 | - (NSArray*) localNotificationIdsByType:(APPLocalNotificationType)type 142 | { 143 | NSArray* notifications = self.localNotifications; 144 | NSMutableArray* ids = [[NSMutableArray alloc] init]; 145 | 146 | for (UILocalNotification* notification in notifications) 147 | { 148 | if (notification.type == type) { 149 | [ids addObject:notification.options.id]; 150 | } 151 | } 152 | 153 | return ids; 154 | } 155 | 156 | /* 157 | * If local notification with ID exists. 158 | * 159 | * @param id 160 | * Notification ID 161 | */ 162 | - (BOOL) localNotificationExist:(NSNumber*)id 163 | { 164 | return [self localNotificationWithId:id] != NULL; 165 | } 166 | 167 | /* If local notification with ID and type exists 168 | * 169 | * @param id 170 | * Notification ID 171 | * @param type 172 | * Notification life cycle type 173 | */ 174 | - (BOOL) localNotificationExist:(NSNumber*)id type:(APPLocalNotificationType)type 175 | { 176 | return [self localNotificationWithId:id andType:type] != NULL; 177 | } 178 | 179 | /** 180 | * Get local notification with ID. 181 | * 182 | * @param id 183 | * Notification ID 184 | */ 185 | - (UILocalNotification*) localNotificationWithId:(NSNumber*)id 186 | { 187 | NSArray* notifications = self.localNotifications; 188 | 189 | for (UILocalNotification* notification in notifications) 190 | { 191 | if ([notification.options.id isEqualToNumber:id]) { 192 | return notification; 193 | } 194 | } 195 | 196 | return NULL; 197 | } 198 | 199 | /* 200 | * Get local notification with ID and type. 201 | * 202 | * @param id 203 | * Notification ID 204 | * @param type 205 | * Notification life cycle type 206 | */ 207 | - (UILocalNotification*) localNotificationWithId:(NSNumber*)id andType:(APPLocalNotificationType)type 208 | { 209 | UILocalNotification* notification = [self localNotificationWithId:id]; 210 | 211 | if (notification && notification.type == type) 212 | return notification; 213 | 214 | return NULL; 215 | } 216 | 217 | /** 218 | * List of properties from all notifications. 219 | */ 220 | - (NSArray*) localNotificationOptions 221 | { 222 | NSArray* notifications = self.localNotifications; 223 | NSMutableArray* options = [[NSMutableArray alloc] init]; 224 | 225 | for (UILocalNotification* notification in notifications) 226 | { 227 | [options addObject:notification.options.userInfo]; 228 | } 229 | 230 | return options; 231 | } 232 | 233 | /** 234 | * List of properties from all local notifications from given type. 235 | * 236 | * @param type 237 | * Notification life cycle type 238 | */ 239 | - (NSArray*) localNotificationOptionsByType:(APPLocalNotificationType)type 240 | { 241 | NSArray* notifications = self.localNotifications; 242 | NSMutableArray* options = [[NSMutableArray alloc] init]; 243 | 244 | for (UILocalNotification* notification in notifications) 245 | { 246 | if (notification.type == type) { 247 | [options addObject:notification.options.userInfo]; 248 | } 249 | } 250 | 251 | return options; 252 | } 253 | 254 | /** 255 | * List of properties from given local notifications. 256 | * 257 | * @param ids 258 | * Notification IDs 259 | */ 260 | - (NSArray*) localNotificationOptionsById:(NSArray*)ids 261 | { 262 | UILocalNotification* notification; 263 | NSMutableArray* options = [[NSMutableArray alloc] init]; 264 | 265 | for (NSNumber* id in ids) 266 | { 267 | notification = [self localNotificationWithId:id]; 268 | 269 | if (notification) { 270 | [options addObject:notification.options.userInfo]; 271 | } 272 | } 273 | 274 | return options; 275 | } 276 | 277 | /** 278 | * List of properties from given local notifications. 279 | * 280 | * @param type 281 | * Notification life cycle type 282 | * @param ids 283 | * Notification IDs 284 | */ 285 | - (NSArray*) localNotificationOptionsByType:(APPLocalNotificationType)type andId:(NSArray*)ids 286 | { 287 | UILocalNotification* notification; 288 | NSMutableArray* options = [[NSMutableArray alloc] init]; 289 | 290 | for (NSNumber* id in ids) 291 | { 292 | notification = [self localNotificationWithId:id]; 293 | 294 | if (notification && notification.type == type) { 295 | [options addObject:notification.options.userInfo]; 296 | } 297 | } 298 | 299 | return options; 300 | } 301 | 302 | /* 303 | * Clear all local notfications. 304 | */ 305 | - (void) clearAllLocalNotifications 306 | { 307 | NSArray* notifications = self.triggeredLocalNotifications; 308 | 309 | for (UILocalNotification* notification in notifications) { 310 | [self clearLocalNotification:notification]; 311 | } 312 | } 313 | 314 | /* 315 | * Clear single local notfication. 316 | * 317 | * @param notification 318 | * The local notification object 319 | */ 320 | - (void) clearLocalNotification:(UILocalNotification*)notification 321 | { 322 | [self cancelLocalNotification:notification]; 323 | 324 | if ([notification isRepeating]) { 325 | notification.fireDate = notification.options.fireDate; 326 | 327 | [self scheduleLocalNotification:notification]; 328 | }; 329 | } 330 | 331 | @end 332 | -------------------------------------------------------------------------------- /ios/RCTLocalNotifications/UILocalNotification+APPLocalNotification.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | #import "APPLocalNotificationOptions.h" 25 | 26 | typedef NS_ENUM(NSUInteger, APPLocalNotificationType) { 27 | NotifcationTypeAll = 0, 28 | NotifcationTypeScheduled = 1, 29 | NotifcationTypeTriggered = 2 30 | }; 31 | 32 | @interface UILocalNotification (APPLocalNotification) 33 | 34 | // Initialize a new local notification 35 | - (id) initWithOptions:(NSDictionary*)dict; 36 | // The options provided by the plug-in 37 | - (APPLocalNotificationOptions*) options; 38 | // Timeinterval since last trigger date 39 | - (double) timeIntervalSinceLastTrigger; 40 | // Timeinterval since fire date 41 | - (double) timeIntervalSinceFireDate; 42 | // If the fire date was in the past 43 | - (BOOL) wasInThePast; 44 | // If the notification was already scheduled 45 | - (BOOL) isScheduled; 46 | // If the notification was already triggered 47 | - (BOOL) isTriggered; 48 | // If the notification was updated 49 | - (BOOL) wasUpdated; 50 | // If it's a repeating notification 51 | - (BOOL) isRepeating; 52 | // Notifciation type 53 | - (APPLocalNotificationType) type; 54 | // Encode the user info dict to JSON 55 | - (NSString*) encodeToJSON; 56 | 57 | @end 58 | -------------------------------------------------------------------------------- /ios/RCTLocalNotifications/UILocalNotification+APPLocalNotification.m: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | #import "UILocalNotification+APPLocalNotification.h" 25 | #import "APPLocalNotificationOptions.h" 26 | #import 27 | 28 | static char optionsKey; 29 | 30 | NSInteger const APPLocalNotificationTypeScheduled = 1; 31 | NSInteger const APPLocalNotificationTypeTriggered = 2; 32 | 33 | @implementation UILocalNotification (APPLocalNotification) 34 | 35 | #pragma mark - 36 | #pragma mark Init 37 | 38 | /** 39 | * Initialize a local notification with the given options when calling on JS side: 40 | * notification.local.add(options) 41 | */ 42 | - (id) initWithOptions:(NSDictionary*)dict 43 | { 44 | self = [self init]; 45 | 46 | [self setUserInfo:dict]; 47 | [self __init]; 48 | 49 | return self; 50 | } 51 | 52 | /** 53 | * Applies the given options when calling on JS side: 54 | * notification.local.add(options) 55 | 56 | */ 57 | - (void) __init 58 | { 59 | APPLocalNotificationOptions* options = self.options; 60 | 61 | self.fireDate = options.fireDate; 62 | self.timeZone = [NSTimeZone defaultTimeZone]; 63 | self.applicationIconBadgeNumber = options.badgeNumber; 64 | self.repeatInterval = options.repeatInterval; 65 | self.alertBody = options.alertBody; 66 | self.soundName = options.soundName; 67 | 68 | if ([self wasInThePast]) { 69 | self.fireDate = [NSDate date]; 70 | } 71 | } 72 | 73 | #pragma mark - 74 | #pragma mark Methods 75 | 76 | /** 77 | * The options provided by the plug-in. 78 | */ 79 | - (APPLocalNotificationOptions*) options 80 | { 81 | APPLocalNotificationOptions* options = [self getOptions]; 82 | 83 | if (!options) { 84 | options = [[APPLocalNotificationOptions alloc] 85 | initWithDict:[self userInfo]]; 86 | 87 | [self setOptions:options]; 88 | } 89 | 90 | return options; 91 | } 92 | 93 | /** 94 | * Get associated option object 95 | */ 96 | - (APPLocalNotificationOptions*) getOptions 97 | { 98 | return objc_getAssociatedObject(self, &optionsKey); 99 | } 100 | 101 | /** 102 | * Set associated option object 103 | */ 104 | - (void) setOptions:(APPLocalNotificationOptions*)options 105 | { 106 | objc_setAssociatedObject(self, &optionsKey, 107 | options, OBJC_ASSOCIATION_RETAIN_NONATOMIC); 108 | } 109 | 110 | /** 111 | * The repeating interval in seconds. 112 | */ 113 | - (int) repeatIntervalInSeconds 114 | { 115 | switch (self.repeatInterval) { 116 | case NSCalendarUnitMinute: 117 | return 60; 118 | 119 | case NSCalendarUnitHour: 120 | return 60000; 121 | 122 | case NSCalendarUnitDay: 123 | case NSCalendarUnitWeekOfYear: 124 | case NSCalendarUnitMonth: 125 | case NSCalendarUnitYear: 126 | return 86400; 127 | 128 | default: 129 | return 1; 130 | } 131 | } 132 | 133 | /** 134 | * Timeinterval since fire date. 135 | */ 136 | - (double) timeIntervalSinceFireDate 137 | { 138 | NSDate* now = [NSDate date]; 139 | NSDate* fireDate = self.fireDate; 140 | 141 | int timespan = [now timeIntervalSinceDate:fireDate]; 142 | 143 | return timespan; 144 | } 145 | 146 | /** 147 | * Timeinterval since last trigger date. 148 | */ 149 | - (double) timeIntervalSinceLastTrigger 150 | { 151 | int timespan = [self timeIntervalSinceFireDate]; 152 | 153 | if ([self isRepeating]) { 154 | timespan = timespan % [self repeatIntervalInSeconds]; 155 | } 156 | 157 | return timespan; 158 | } 159 | 160 | /** 161 | * Encode the user info dict to JSON. 162 | */ 163 | - (NSString*) encodeToJSON 164 | { 165 | NSString* json; 166 | NSData* data; 167 | NSMutableDictionary* obj = [self.userInfo mutableCopy]; 168 | 169 | [obj removeObjectForKey:@"updatedAt"]; 170 | 171 | if (obj == NULL || obj.count == 0) 172 | return json; 173 | 174 | data = [NSJSONSerialization dataWithJSONObject:obj 175 | options:NSJSONWritingPrettyPrinted 176 | error:NULL]; 177 | 178 | json = [[NSString alloc] initWithData:data 179 | encoding:NSUTF8StringEncoding]; 180 | 181 | return [json stringByReplacingOccurrencesOfString:@"\n" 182 | withString:@""]; 183 | } 184 | 185 | #pragma mark - 186 | #pragma mark State 187 | 188 | /** 189 | * If the fire date was in the past. 190 | */ 191 | - (BOOL) wasInThePast 192 | { 193 | return [self timeIntervalSinceLastTrigger] > 0; 194 | } 195 | 196 | // If the notification was already scheduled 197 | - (BOOL) isScheduled 198 | { 199 | return [self isRepeating] || ![self wasInThePast]; 200 | } 201 | 202 | /** 203 | * If the notification was already triggered. 204 | */ 205 | - (BOOL) isTriggered 206 | { 207 | NSDate* now = [NSDate date]; 208 | NSDate* fireDate = self.fireDate; 209 | 210 | bool isLaterThanFireDate = !([now compare:fireDate] == NSOrderedAscending); 211 | 212 | return isLaterThanFireDate; 213 | } 214 | 215 | /** 216 | * If the notification was updated. 217 | */ 218 | - (BOOL) wasUpdated 219 | { 220 | NSDate* now = [NSDate date]; 221 | NSDate* updatedAt = [self.userInfo objectForKey:@"updatedAt"]; 222 | 223 | if (updatedAt == NULL) 224 | return NO; 225 | 226 | int timespan = [now timeIntervalSinceDate:updatedAt]; 227 | 228 | return timespan < 1; 229 | } 230 | 231 | /** 232 | * If it's a repeating notification. 233 | */ 234 | - (BOOL) isRepeating 235 | { 236 | return [self.options isRepeating]; 237 | } 238 | 239 | /** 240 | * Process state type of the local notification. 241 | */ 242 | - (APPLocalNotificationType) type 243 | { 244 | return [self isTriggered] ? NotifcationTypeTriggered : NotifcationTypeScheduled; 245 | } 246 | 247 | @end 248 | -------------------------------------------------------------------------------- /libs/local-notification-core.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | const exec = require('@remobile/react-native-cordova').exec; 25 | const util = require('./local-notification-util.js'); 26 | const { NativeAppEventEmitter, DeviceEventEmitter, Platform } = require('react-native'); 27 | const EventEmitter = Platform.OS === 'android' ? DeviceEventEmitter : NativeAppEventEmitter; 28 | exports = Object.assign(exports, util); 29 | 30 | /******** 31 | * CORE * 32 | ********/ 33 | 34 | /** 35 | * Returns the default settings. 36 | * 37 | * @return {Object} 38 | */ 39 | exports.getDefaults = function () { 40 | return this._defaults; 41 | }; 42 | 43 | /** 44 | * Overwrite default settings. 45 | * 46 | * @param {Object} defaults 47 | */ 48 | exports.setDefaults = function (newDefaults) { 49 | const defaults = this.getDefaults(); 50 | 51 | for (const key in defaults) { 52 | if (newDefaults.hasOwnProperty(key)) { 53 | defaults[key] = newDefaults[key]; 54 | } 55 | } 56 | }; 57 | 58 | /** 59 | * Schedule a new local notification. 60 | * 61 | * @param {Object} opts 62 | * The notification properties 63 | * @param {Function} callback 64 | * A function to be called after the notification has been canceled 65 | * @param {Object?} scope 66 | * The scope for the callback function 67 | */ 68 | exports.schedule = function (opts, callback, scope) { 69 | this.registerPermission(function (granted) { 70 | if (!granted) { return; } 71 | 72 | const notifications = Array.isArray(opts) ? opts : [opts]; 73 | 74 | for (let i = 0; i < notifications.length; i++) { 75 | const properties = notifications[i]; 76 | 77 | this.mergeWithDefaults(properties); 78 | this.convertProperties(properties); 79 | } 80 | 81 | this.exec('schedule', notifications, callback, scope); 82 | }, this); 83 | }; 84 | 85 | /** 86 | * Update existing notifications specified by IDs in options. 87 | * 88 | * @param {Object} options 89 | * The notification properties to update 90 | * @param {Function} callback 91 | * A function to be called after the notification has been updated 92 | * @param {Object?} scope 93 | * The scope for the callback function 94 | */ 95 | exports.update = function (opts, callback, scope) { 96 | const notifications = Array.isArray(opts) ? opts : [opts]; 97 | 98 | for (let i = 0; i < notifications.length; i++) { 99 | const properties = notifications[i]; 100 | 101 | this.convertProperties(properties); 102 | } 103 | 104 | this.exec('update', notifications, callback, scope); 105 | }; 106 | 107 | /** 108 | * Clear the specified notification. 109 | * 110 | * @param {String} id 111 | * The ID of the notification 112 | * @param {Function} callback 113 | * A function to be called after the notification has been cleared 114 | * @param {Object?} scope 115 | * The scope for the callback function 116 | */ 117 | exports.clear = function (ids, callback, scope) { 118 | ids = Array.isArray(ids) ? ids : [ids]; 119 | ids = this.convertIds(ids); 120 | 121 | this.exec('clear', ids, callback, scope); 122 | }; 123 | 124 | /** 125 | * Clear all previously sheduled notifications. 126 | * 127 | * @param {Function} callback 128 | * A function to be called after all notifications have been cleared 129 | * @param {Object?} scope 130 | * The scope for the callback function 131 | */ 132 | exports.clearAll = function (callback, scope) { 133 | this.exec('clearAll', null, callback, scope); 134 | }; 135 | 136 | /** 137 | * Cancel the specified notifications. 138 | * 139 | * @param {String[]} ids 140 | * The IDs of the notifications 141 | * @param {Function} callback 142 | * A function to be called after the notifications has been canceled 143 | * @param {Object?} scope 144 | * The scope for the callback function 145 | */ 146 | exports.cancel = function (ids, callback, scope) { 147 | ids = Array.isArray(ids) ? ids : [ids]; 148 | ids = this.convertIds(ids); 149 | 150 | this.exec('cancel', ids, callback, scope); 151 | }; 152 | 153 | /** 154 | * Remove all previously registered notifications. 155 | * 156 | * @param {Function} callback 157 | * A function to be called after all notifications have been canceled 158 | * @param {Object?} scope 159 | * The scope for the callback function 160 | */ 161 | exports.cancelAll = function (callback, scope) { 162 | this.exec('cancelAll', null, callback, scope); 163 | }; 164 | 165 | /** 166 | * Check if a notification with an ID is present. 167 | * 168 | * @param {String} id 169 | * The ID of the notification 170 | * @param {Function} callback 171 | * A callback function to be called with the list 172 | * @param {Object?} scope 173 | * The scope for the callback function 174 | */ 175 | exports.isPresent = function (id, callback, scope) { 176 | this.exec('isPresent', id || 0, callback, scope); 177 | }; 178 | 179 | /** 180 | * Check if a notification with an ID is scheduled. 181 | * 182 | * @param {String} id 183 | * The ID of the notification 184 | * @param {Function} callback 185 | * A callback function to be called with the list 186 | * @param {Object?} scope 187 | * The scope for the callback function 188 | */ 189 | exports.isScheduled = function (id, callback, scope) { 190 | this.exec('isScheduled', id || 0, callback, scope); 191 | }; 192 | 193 | /** 194 | * Check if a notification with an ID was triggered. 195 | * 196 | * @param {String} id 197 | * The ID of the notification 198 | * @param {Function} callback 199 | * A callback function to be called with the list 200 | * @param {Object?} scope 201 | * The scope for the callback function 202 | */ 203 | exports.isTriggered = function (id, callback, scope) { 204 | this.exec('isTriggered', id || 0, callback, scope); 205 | }; 206 | 207 | /** 208 | * List all local notification IDs. 209 | * 210 | * @param {Function} callback 211 | * A callback function to be called with the list 212 | * @param {Object?} scope 213 | * The scope for the callback function 214 | */ 215 | exports.getAllIds = function (callback, scope) { 216 | this.exec('getAllIds', null, callback, scope); 217 | }; 218 | 219 | /** 220 | * Alias for `getAllIds`. 221 | */ 222 | exports.getIds = function () { 223 | this.getAllIds.apply(this, arguments); 224 | }; 225 | 226 | /** 227 | * List all scheduled notification IDs. 228 | * 229 | * @param {Function} callback 230 | * A callback function to be called with the list 231 | * @param {Object?} scope 232 | * The scope for the callback function 233 | */ 234 | exports.getScheduledIds = function (callback, scope) { 235 | this.exec('getScheduledIds', null, callback, scope); 236 | }; 237 | 238 | /** 239 | * List all triggered notification IDs. 240 | * 241 | * @param {Function} callback 242 | * A callback function to be called with the list 243 | * @param {Object?} scope 244 | * The scope for the callback function 245 | */ 246 | exports.getTriggeredIds = function (callback, scope) { 247 | this.exec('getTriggeredIds', null, callback, scope); 248 | }; 249 | 250 | /** 251 | * Property list for given local notifications. 252 | * If called without IDs, all notification will be returned. 253 | * 254 | * @param {Number[]?} ids 255 | * Set of notification IDs 256 | * @param {Function} callback 257 | * A callback function to be called with the list 258 | * @param {Object?} scope 259 | * The scope for the callback function 260 | */ 261 | exports.get = function () { 262 | const args = Array.apply(null, arguments); 263 | 264 | if (typeof args[0] == 'function') { 265 | args.unshift([]); 266 | } 267 | 268 | let ids = args[0], 269 | callback = args[1], 270 | scope = args[2]; 271 | 272 | if (!Array.isArray(ids)) { 273 | this.exec('getSingle', Number(ids), callback, scope); 274 | return; 275 | } 276 | 277 | ids = this.convertIds(ids); 278 | 279 | this.exec('getAll', ids, callback, scope); 280 | }; 281 | 282 | /** 283 | * Property list for all local notifications. 284 | * 285 | * @param {Function} callback 286 | * A callback function to be called with the list 287 | * @param {Object?} scope 288 | * The scope for the callback function 289 | */ 290 | exports.getAll = function (callback, scope) { 291 | this.exec('getAll', null, callback, scope); 292 | }; 293 | 294 | /** 295 | * Property list for given scheduled notifications. 296 | * If called without IDs, all notification will be returned. 297 | * 298 | * @param {Number[]?} ids 299 | * Set of notification IDs 300 | * @param {Function} callback 301 | * A callback function to be called with the list 302 | * @param {Object?} scope 303 | * The scope for the callback function 304 | */ 305 | exports.getScheduled = function () { 306 | const args = Array.apply(null, arguments); 307 | 308 | if (typeof args[0] == 'function') { 309 | args.unshift([]); 310 | } 311 | 312 | let ids = args[0], 313 | callback = args[1], 314 | scope = args[2]; 315 | 316 | if (!Array.isArray(ids)) { 317 | ids = [ids]; 318 | } 319 | 320 | if (!Array.isArray(ids)) { 321 | this.exec('getSingleScheduled', Number(ids), callback, scope); 322 | return; 323 | } 324 | 325 | ids = this.convertIds(ids); 326 | 327 | this.exec('getScheduled', ids, callback, scope); 328 | }; 329 | 330 | /** 331 | * Property list for all scheduled notifications. 332 | * 333 | * @param {Function} callback 334 | * A callback function to be called with the list 335 | * @param {Object?} scope 336 | * The scope for the callback function 337 | */ 338 | exports.getAllScheduled = function (callback, scope) { 339 | this.exec('getScheduled', null, callback, scope); 340 | }; 341 | 342 | /** 343 | * Property list for given triggered notifications. 344 | * If called without IDs, all notification will be returned. 345 | * 346 | * @param {Number[]?} ids 347 | * Set of notification IDs 348 | * @param {Function} callback 349 | * A callback function to be called with the list 350 | * @param {Object?} scope 351 | * The scope for the callback function 352 | */ 353 | exports.getTriggered = function () { 354 | const args = Array.apply(null, arguments); 355 | 356 | if (typeof args[0] == 'function') { 357 | args.unshift([]); 358 | } 359 | 360 | let ids = args[0], 361 | callback = args[1], 362 | scope = args[2]; 363 | 364 | if (!Array.isArray(ids)) { 365 | ids = [ids]; 366 | } 367 | 368 | if (!Array.isArray(ids)) { 369 | this.exec('getSingleTriggered', Number(ids), callback, scope); 370 | return; 371 | } 372 | 373 | ids = this.convertIds(ids); 374 | 375 | this.exec('getTriggered', ids, callback, scope); 376 | }; 377 | 378 | /** 379 | * Property list for all triggered notifications. 380 | * 381 | * @param {Function} callback 382 | * A callback function to be called with the list 383 | * @param {Object?} scope 384 | * The scope for the callback function 385 | */ 386 | exports.getAllTriggered = function (callback, scope) { 387 | this.exec('getTriggered', null, callback, scope); 388 | }; 389 | 390 | /** 391 | * Informs if the app has the permission to show notifications. 392 | * 393 | * @param {Function} callback 394 | * The function to be exec as the callback 395 | * @param {Object?} scope 396 | * The callback function's scope 397 | */ 398 | exports.hasPermission = function (callback, scope) { 399 | const fn = this.createCallbackFn(callback, scope); 400 | 401 | if (Platform.OS != 'ios') { 402 | fn(true); 403 | return; 404 | } 405 | 406 | exec(fn, null, 'LocalNotification', 'hasPermission', []); 407 | }; 408 | 409 | /** 410 | * Register permission to show notifications if not already granted. 411 | * 412 | * @param {Function} callback 413 | * The function to be exec as the callback 414 | * @param {Object?} scope 415 | * The callback function's scope 416 | */ 417 | exports.registerPermission = function (callback, scope) { 418 | const fn = this.createCallbackFn(callback, scope); 419 | 420 | if (Platform.OS != 'ios') { 421 | fn(true); 422 | return; 423 | } 424 | 425 | exec(fn, null, 'LocalNotification', 'registerPermission', []); 426 | }; 427 | 428 | /********** 429 | * EVENTS * 430 | **********/ 431 | 432 | /** 433 | * Register callback for given event. 434 | * 435 | * @param {String} event 436 | * The event's name 437 | * @param {Function} callback 438 | * The function to be exec as callback 439 | * @param {Object?} scope 440 | * The callback function's scope 441 | */ 442 | exports.on = function (event, callback, scope) { 443 | if (!this._subscription[event]) { 444 | this._subscription[event] = EventEmitter.addListener('RCT-LOCAL-NOTIFICATION-' + event, function (params) { 445 | console.log('[========]', event, params); 446 | exports.fireEvent(event, params); 447 | }); 448 | } 449 | 450 | if (!this._listener[event]) { 451 | this._listener[event] = []; 452 | } 453 | 454 | const item = [callback, scope]; 455 | 456 | this._listener[event].push(item); 457 | }; 458 | 459 | /** 460 | * Unregister callback for given event. 461 | * 462 | * @param {String} event 463 | * The event's name 464 | * @param {Function} callback 465 | * The function to be exec as callback 466 | */ 467 | exports.un = function (event, callback) { 468 | const listener = this._listener[event]; 469 | const subscription = this._subscription[event]; 470 | if (subscription) { 471 | subscription.remove(); 472 | delete this._subscription[event]; 473 | } 474 | 475 | if (!listener) { return; } 476 | 477 | for (let i = 0; i < listener.length; i++) { 478 | const fn = listener[i][0]; 479 | 480 | if (fn == callback) { 481 | listener.splice(i, 1); 482 | break; 483 | } 484 | } 485 | }; 486 | -------------------------------------------------------------------------------- /libs/local-notification-util.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | const exec = require('@remobile/react-native-cordova').exec; 25 | const { Platform } = require('react-native'); 26 | 27 | /*********** 28 | * MEMBERS * 29 | ***********/ 30 | 31 | // Default values 32 | exports._defaults = Platform.OS === 'android' ? { 33 | icon: 'res://ic_popup_reminder', 34 | smallIcon: undefined, 35 | ongoing: false, 36 | autoClear: true, 37 | led: 'FFFFFF', 38 | text: '', 39 | title: '', 40 | sound: 'res://platform_default', 41 | badge: 0, 42 | id: 0, 43 | data: undefined, 44 | every: undefined, 45 | at: undefined, 46 | } : { 47 | text: '', 48 | title: '', 49 | sound: 'res://platform_default', 50 | badge: 0, 51 | id: 0, 52 | data: undefined, 53 | every: undefined, 54 | at: undefined, 55 | }; 56 | 57 | // listener 58 | exports._listener = {}; 59 | exports._subscription = {}; 60 | 61 | /******** 62 | * UTIL * 63 | ********/ 64 | 65 | /** 66 | * Merge custom properties with the default values. 67 | * 68 | * @param {Object} options 69 | * Set of custom values 70 | * 71 | * @retrun {Object} 72 | * The merged property list 73 | */ 74 | exports.mergeWithDefaults = function (options) { 75 | const defaults = this.getDefaults(); 76 | 77 | options.at = this.getValueFor(options, 'at', 'firstAt', 'date'); 78 | options.text = this.getValueFor(options, 'text', 'message'); 79 | options.data = this.getValueFor(options, 'data', 'json'); 80 | 81 | if (defaults.hasOwnProperty('autoClear')) { 82 | options.autoClear = this.getValueFor(options, 'autoClear', 'autoCancel'); 83 | } 84 | 85 | if (options.autoClear !== true && options.ongoing) { 86 | options.autoClear = false; 87 | } 88 | 89 | if (options.at === undefined || options.at === null) { 90 | options.at = new Date(); 91 | } 92 | 93 | let key; 94 | for (key in defaults) { 95 | if (options[key] === null || options[key] === undefined) { 96 | if (options.hasOwnProperty(key) && ['data', 'sound'].indexOf(key) > -1) { 97 | options[key] = undefined; 98 | } else { 99 | options[key] = defaults[key]; 100 | } 101 | } 102 | } 103 | 104 | for (key in options) { 105 | if (!defaults.hasOwnProperty(key)) { 106 | delete options[key]; 107 | console.warn('Unknown property: ' + key); 108 | } 109 | } 110 | 111 | return options; 112 | }; 113 | 114 | /** 115 | * Convert the passed values to their required type. 116 | * 117 | * @param {Object} options 118 | * Set of custom values 119 | * 120 | * @retrun {Object} 121 | * The converted property list 122 | */ 123 | exports.convertProperties = function (options) { 124 | if (options.id) { 125 | if (isNaN(options.id)) { 126 | options.id = this.getDefaults().id; 127 | console.warn('Id is not a number: ' + options.id); 128 | } else { 129 | options.id = Number(options.id); 130 | } 131 | } 132 | 133 | if (options.title) { 134 | options.title = options.title.toString(); 135 | } 136 | 137 | if (options.text) { 138 | options.text = options.text.toString(); 139 | } 140 | 141 | if (options.badge) { 142 | if (isNaN(options.badge)) { 143 | options.badge = this.getDefaults().badge; 144 | console.warn('Badge number is not a number: ' + options.id); 145 | } else { 146 | options.badge = Number(options.badge); 147 | } 148 | } 149 | 150 | if (options.at) { 151 | if (typeof options.at == 'object') { 152 | options.at = options.at.getTime(); 153 | } 154 | 155 | options.at = Math.round(options.at / 1000); 156 | } 157 | 158 | if (typeof options.data == 'object') { 159 | options.data = JSON.stringify(options.data); 160 | } 161 | 162 | return options; 163 | }; 164 | 165 | /** 166 | * Create callback, which will be executed within a specific scope. 167 | * 168 | * @param {Function} callbackFn 169 | * The callback function 170 | * @param {Object} scope 171 | * The scope for the function 172 | * 173 | * @return {Function} 174 | * The new callback function 175 | */ 176 | exports.createCallbackFn = function (callbackFn, scope) { 177 | if (typeof callbackFn != 'function') { return; } 178 | 179 | return function () { 180 | callbackFn.apply(scope || this, arguments); 181 | }; 182 | }; 183 | 184 | /** 185 | * Convert the IDs to numbers. 186 | * 187 | * @param {String/Number[]} ids 188 | * 189 | * @return Array of Numbers 190 | */ 191 | exports.convertIds = function (ids) { 192 | const convertedIds = []; 193 | 194 | for (let i = 0; i < ids.length; i++) { 195 | convertedIds.push(Number(ids[i])); 196 | } 197 | 198 | return convertedIds; 199 | }; 200 | 201 | /** 202 | * First found value for the given keys. 203 | * 204 | * @param {Object} options 205 | * Object with key-value properties 206 | * @param {String[]} keys* 207 | * Key list 208 | */ 209 | exports.getValueFor = function (options) { 210 | const keys = Array.apply(null, arguments).slice(1); 211 | 212 | for (let i = 0; i < keys.length; i++) { 213 | const key = keys[i]; 214 | 215 | if (options.hasOwnProperty(key)) { 216 | return options[key]; 217 | } 218 | } 219 | }; 220 | 221 | /** 222 | * Fire event with given arguments. 223 | * 224 | * @param {String} event 225 | * The event's name 226 | * @param {args*} 227 | * The callback's arguments 228 | */ 229 | exports.fireEvent = function (event) { 230 | const args = Array.apply(null, arguments).slice(1), 231 | listener = this._listener[event]; 232 | 233 | if (!listener) { return; } 234 | 235 | for (let i = 0; i < listener.length; i++) { 236 | const fn = listener[i][0], 237 | scope = listener[i][1]; 238 | 239 | fn.apply(scope, args); 240 | } 241 | }; 242 | 243 | /** 244 | * Execute the native counterpart. 245 | * 246 | * @param {String} action 247 | * The name of the action 248 | * @param args[] 249 | * Array of arguments 250 | * @param {Function} callback 251 | * The callback function 252 | * @param {Object} scope 253 | * The scope for the function 254 | */ 255 | exports.exec = function (action, args, callback, scope) { 256 | const fn = this.createCallbackFn(callback, scope); 257 | let params = []; 258 | 259 | if (Array.isArray(args)) { 260 | params = args; 261 | } else if (args) { 262 | params.push(args); 263 | } 264 | 265 | exec(fn, null, 'LocalNotification', action, params); 266 | }; 267 | 268 | /********* 269 | * HOOKS * 270 | *********/ 271 | exec(null, null, 'LocalNotification', 'deviceready', []); 272 | -------------------------------------------------------------------------------- /libs/local-notification.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2013-2015 by appPlant UG. All rights reserved. 3 | * 4 | * @APPPLANT_LICENSE_HEADER_START@ 5 | * 6 | * This file contains Original Code and/or Modifications of Original Code 7 | * as defined in and that are subject to the Apache License 8 | * Version 2.0 (the 'License'). You may not use this file except in 9 | * compliance with the License. Please obtain a copy of the License at 10 | * http://opensource.org/licenses/Apache-2.0/ and read it before using this 11 | * file. 12 | * 13 | * The Original Code and all software distributed under the License are 14 | * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 | * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 | * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 | * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 | * Please see the License for the specific language governing rights and 19 | * limitations under the License. 20 | * 21 | * @APPPLANT_LICENSE_HEADER_END@ 22 | */ 23 | 24 | /************* 25 | * INTERFACE * 26 | *************/ 27 | 28 | const core = require('./local-notification-core.js'); 29 | /** 30 | * Returns the default settings. 31 | * 32 | * @return {Object} 33 | */ 34 | exports.getDefaults = function () { 35 | return core.getDefaults(); 36 | }; 37 | 38 | /** 39 | * Overwrite default settings. 40 | * 41 | * @param {Object} defaults 42 | */ 43 | exports.setDefaults = function (defaults) { 44 | core.setDefaults(defaults); 45 | }; 46 | 47 | /** 48 | * Schedule a new local notification. 49 | * 50 | * @param {Object} opts 51 | * The notification properties 52 | * @param {Function} callback 53 | * A function to be called after the notification has been canceled 54 | * @param {Object?} scope 55 | * The scope for the callback function 56 | */ 57 | exports.schedule = function (opts, callback, scope) { 58 | core.schedule(opts, callback, scope); 59 | }; 60 | 61 | /** 62 | * Update existing notifications specified by IDs in options. 63 | * 64 | * @param {Object} options 65 | * The notification properties to update 66 | * @param {Function} callback 67 | * A function to be called after the notification has been updated 68 | * @param {Object?} scope 69 | * The scope for the callback function 70 | */ 71 | exports.update = function (opts, callback, scope) { 72 | core.update(opts, callback, scope); 73 | }; 74 | 75 | /** 76 | * Clear the specified notification. 77 | * 78 | * @param {String} id 79 | * The ID of the notification 80 | * @param {Function} callback 81 | * A function to be called after the notification has been cleared 82 | * @param {Object?} scope 83 | * The scope for the callback function 84 | */ 85 | exports.clear = function (ids, callback, scope) { 86 | core.clear(ids, callback, scope); 87 | }; 88 | 89 | /** 90 | * Clear all previously sheduled notifications. 91 | * 92 | * @param {Function} callback 93 | * A function to be called after all notifications have been cleared 94 | * @param {Object?} scope 95 | * The scope for the callback function 96 | */ 97 | exports.clearAll = function (callback, scope) { 98 | core.clearAll(callback, scope); 99 | }; 100 | 101 | /** 102 | * Cancel the specified notifications. 103 | * 104 | * @param {String[]} ids 105 | * The IDs of the notifications 106 | * @param {Function} callback 107 | * A function to be called after the notifications has been canceled 108 | * @param {Object?} scope 109 | * The scope for the callback function 110 | */ 111 | exports.cancel = function (ids, callback, scope) { 112 | core.cancel(ids, callback, scope); 113 | }; 114 | 115 | /** 116 | * Remove all previously registered notifications. 117 | * 118 | * @param {Function} callback 119 | * A function to be called after all notifications have been canceled 120 | * @param {Object?} scope 121 | * The scope for the callback function 122 | */ 123 | exports.cancelAll = function (callback, scope) { 124 | core.cancelAll(callback, scope); 125 | }; 126 | 127 | /** 128 | * Check if a notification with an ID is present. 129 | * 130 | * @param {String} id 131 | * The ID of the notification 132 | * @param {Function} callback 133 | * A callback function to be called with the list 134 | * @param {Object?} scope 135 | * The scope for the callback function 136 | */ 137 | exports.isPresent = function (id, callback, scope) { 138 | core.isPresent(id, callback, scope); 139 | }; 140 | 141 | /** 142 | * Check if a notification with an ID is scheduled. 143 | * 144 | * @param {String} id 145 | * The ID of the notification 146 | * @param {Function} callback 147 | * A callback function to be called with the list 148 | * @param {Object?} scope 149 | * The scope for the callback function 150 | */ 151 | exports.isScheduled = function (id, callback, scope) { 152 | core.isScheduled(id, callback, scope); 153 | }; 154 | 155 | /** 156 | * Check if a notification with an ID was triggered. 157 | * 158 | * @param {String} id 159 | * The ID of the notification 160 | * @param {Function} callback 161 | * A callback function to be called with the list 162 | * @param {Object?} scope 163 | * The scope for the callback function 164 | */ 165 | exports.isTriggered = function (id, callback, scope) { 166 | core.isTriggered(id, callback, scope); 167 | }; 168 | 169 | /** 170 | * List all local notification IDs. 171 | * 172 | * @param {Function} callback 173 | * A callback function to be called with the list 174 | * @param {Object?} scope 175 | * The scope for the callback function 176 | */ 177 | exports.getAllIds = function (callback, scope) { 178 | core.getAllIds(callback, scope); 179 | }; 180 | 181 | /** 182 | * Alias for `getAllIds`. 183 | */ 184 | exports.getIds = function () { 185 | this.getAllIds.apply(this, arguments); 186 | }; 187 | 188 | /** 189 | * List all scheduled notification IDs. 190 | * 191 | * @param {Function} callback 192 | * A callback function to be called with the list 193 | * @param {Object?} scope 194 | * The scope for the callback function 195 | */ 196 | exports.getScheduledIds = function (callback, scope) { 197 | core.getScheduledIds(callback, scope); 198 | }; 199 | 200 | /** 201 | * List all triggered notification IDs. 202 | * 203 | * @param {Function} callback 204 | * A callback function to be called with the list 205 | * @param {Object?} scope 206 | * The scope for the callback function 207 | */ 208 | exports.getTriggeredIds = function (callback, scope) { 209 | core.getTriggeredIds(callback, scope); 210 | }; 211 | 212 | /** 213 | * Property list for given local notifications. 214 | * If called without IDs, all notification will be returned. 215 | * 216 | * @param {Number[]?} ids 217 | * Set of notification IDs 218 | * @param {Function} callback 219 | * A callback function to be called with the list 220 | * @param {Object?} scope 221 | * The scope for the callback function 222 | */ 223 | exports.get = function () { 224 | core.get.apply(core, arguments); 225 | }; 226 | 227 | /** 228 | * Property list for all local notifications. 229 | * 230 | * @param {Function} callback 231 | * A callback function to be called with the list 232 | * @param {Object?} scope 233 | * The scope for the callback function 234 | */ 235 | exports.getAll = function (callback, scope) { 236 | core.getAll(callback, scope); 237 | }; 238 | 239 | /** 240 | * Property list for given scheduled notifications. 241 | * If called without IDs, all notification will be returned. 242 | * 243 | * @param {Number[]?} ids 244 | * Set of notification IDs 245 | * @param {Function} callback 246 | * A callback function to be called with the list 247 | * @param {Object?} scope 248 | * The scope for the callback function 249 | */ 250 | exports.getScheduled = function () { 251 | core.getScheduled.apply(core, arguments); 252 | }; 253 | 254 | /** 255 | * Property list for all scheduled notifications. 256 | * 257 | * @param {Function} callback 258 | * A callback function to be called with the list 259 | * @param {Object?} scope 260 | * The scope for the callback function 261 | */ 262 | exports.getAllScheduled = function (callback, scope) { 263 | core.getAllScheduled(callback, scope); 264 | }; 265 | 266 | /** 267 | * Property list for given triggered notifications. 268 | * If called without IDs, all notification will be returned. 269 | * 270 | * @param {Number[]?} ids 271 | * Set of notification IDs 272 | * @param {Function} callback 273 | * A callback function to be called with the list 274 | * @param {Object?} scope 275 | * The scope for the callback function 276 | */ 277 | exports.getTriggered = function () { 278 | core.getTriggered.apply(core, arguments); 279 | }; 280 | 281 | /** 282 | * Property list for all triggered notifications. 283 | * 284 | * @param {Function} callback 285 | * A callback function to be called with the list 286 | * @param {Object?} scope 287 | * The scope for the callback function 288 | */ 289 | exports.getAllTriggered = function (callback, scope) { 290 | core.getAllTriggered(callback, scope); 291 | }; 292 | 293 | /** 294 | * Informs if the app has the permission to show notifications. 295 | * 296 | * @param {Function} callback 297 | * The function to be exec as the callback 298 | * @param {Object?} scope 299 | * The callback function's scope 300 | */ 301 | exports.hasPermission = function (callback, scope) { 302 | core.hasPermission(callback, scope); 303 | }; 304 | 305 | /** 306 | * Register permission to show notifications if not already granted. 307 | * 308 | * @param {Function} callback 309 | * The function to be exec as the callback 310 | * @param {Object?} scope 311 | * The callback function's scope 312 | */ 313 | exports.registerPermission = function (callback, scope) { 314 | core.registerPermission(callback, scope); 315 | }; 316 | 317 | /**************** 318 | * DEPRECATIONS * 319 | ****************/ 320 | 321 | /** 322 | * Schedule a new local notification. 323 | */ 324 | exports.add = function () { 325 | console.warn('Depreated: Please use `notification.local.schedule` instead.'); 326 | 327 | this.schedule.apply(this, arguments); 328 | }; 329 | 330 | /** 331 | * Register permission to show notifications 332 | * if not already granted. 333 | */ 334 | exports.promptForPermission = function () { 335 | console.warn('Depreated: Please use `notification.local.registerPermission` instead.'); 336 | 337 | this.registerPermission.apply(this, arguments); 338 | }; 339 | 340 | /********** 341 | * EVENTS * 342 | **********/ 343 | 344 | /** 345 | * Register callback for given event. 346 | * 347 | * @param {String} event 348 | * The event's name 349 | * @param {Function} callback 350 | * The function to be exec as callback 351 | * @param {Object?} scope 352 | * The callback function's scope 353 | */ 354 | exports.on = function (event, callback, scope) { 355 | core.on(event, callback, scope); 356 | }; 357 | 358 | /** 359 | * Unregister callback for given event. 360 | * 361 | * @param {String} event 362 | * The event's name 363 | * @param {Function} callback 364 | * The function to be exec as callback 365 | */ 366 | exports.un = function (event, callback) { 367 | core.un(event, callback); 368 | }; 369 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@remobile/react-native-local-notifications", 3 | "version": "1.0.4", 4 | "description": "A cordova local notifications for react-native", 5 | "main": "index.js", 6 | "author": { 7 | "name": "YunJiang.Fang", 8 | "email": "42550564@qq.com" 9 | }, 10 | "license": "MIT", 11 | "keywords": [ 12 | "react-native", 13 | "react-component", 14 | "ios", 15 | "android", 16 | "local-notifications", 17 | "push", 18 | "notifications", 19 | "contact", 20 | "save", 21 | "find", 22 | "remobile", 23 | "mobile" 24 | ], 25 | "homepage": "https://github.com/remobile/react-native-local-notifications", 26 | "bugs": { 27 | "url": "https://github.com/remobile/react-native-local-notifications/issues" 28 | }, 29 | "repository": { 30 | "type": "git", 31 | "url": "git://github.com/remobile/react-native-local-notifications.git" 32 | }, 33 | "peerDependencies": { 34 | "@remobile/react-native-cordova": "^1.1.1" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /screencasts/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remobile/react-native-local-notifications/25fffa04a8e13bf833a6f6103274698d7153166d/screencasts/1.png -------------------------------------------------------------------------------- /screencasts/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/remobile/react-native-local-notifications/25fffa04a8e13bf833a6f6103274698d7153166d/screencasts/2.png --------------------------------------------------------------------------------