cordova-plugin-local-notifications-continued 1.2.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/.github/FUNDING.yml +1 -0
  2. package/.github/ISSUE_TEMPLATE/report_issue.yml +126 -0
  3. package/CHANGELOG.md +358 -0
  4. package/LICENSE +202 -0
  5. package/README.md +1531 -0
  6. package/images/android-actions-with-input-clicked.png +0 -0
  7. package/images/android-actions-with-input-not-clicked.png +0 -0
  8. package/images/android-actions.png +0 -0
  9. package/images/android-alarms-and-reminders-in-app-settings.png +0 -0
  10. package/images/android-alarms-and-reminders-setting.png +0 -0
  11. package/images/android-app-hibernation-notification.png +0 -0
  12. package/images/android-app-hibernation-settings-android-12.png +0 -0
  13. package/images/android-app-hibernation-settings-android-13-14.png +0 -0
  14. package/images/android-app-hibernation-settings-android-15.png +0 -0
  15. package/images/android-attachments-image-folded.png +0 -0
  16. package/images/android-attachments-image-unfolded.png +0 -0
  17. package/images/android-chat.png +0 -0
  18. package/images/android-icon.svg +19 -0
  19. package/images/android-inbox.png +0 -0
  20. package/images/android-notification-example.png +0 -0
  21. package/images/android-progress.png +0 -0
  22. package/images/android-request-permission.png +0 -0
  23. package/images/android-stack.png +0 -0
  24. package/images/apple-icon.svg +1 -0
  25. package/images/cordova-app-notified-logo.png +0 -0
  26. package/images/ios-actions-with-input.png +0 -0
  27. package/images/ios-actions.png +0 -0
  28. package/images/ios-attachments-image-folded.png +0 -0
  29. package/images/ios-attachments-image-unfolded.png +0 -0
  30. package/images/ios-notification.png +0 -0
  31. package/images/ios-request-permission.png +0 -0
  32. package/images/logo.png +0 -0
  33. package/package.json +53 -0
  34. package/plugin.xml +266 -0
  35. package/src/android/ClickActivity.java +72 -0
  36. package/src/android/LocalNotification.java +779 -0
  37. package/src/android/Manager.java +327 -0
  38. package/src/android/Notification.java +844 -0
  39. package/src/android/Options.java +956 -0
  40. package/src/android/OptionsTrigger.java +104 -0
  41. package/src/android/action/Action.java +214 -0
  42. package/src/android/action/ActionGroup.java +135 -0
  43. package/src/android/build/localnotification.gradle +29 -0
  44. package/src/android/receiver/ClearReceiver.java +61 -0
  45. package/src/android/receiver/RestoreReceiver.java +70 -0
  46. package/src/android/receiver/TriggerReceiver.java +70 -0
  47. package/src/android/trigger/TriggerHandler.java +195 -0
  48. package/src/android/trigger/TriggerHandlerAt.java +63 -0
  49. package/src/android/trigger/TriggerHandlerEvery.java +257 -0
  50. package/src/android/trigger/TriggerHandlerIn.java +69 -0
  51. package/src/android/util/AssetUtil.java +385 -0
  52. package/src/android/util/CallbackContextUtil.java +88 -0
  53. package/src/android/util/PluginFileProvider.java +34 -0
  54. package/src/android/xml/shared_files_provider_paths.xml +38 -0
  55. package/src/ios/APPLocalNotification.h +56 -0
  56. package/src/ios/APPLocalNotification.m +650 -0
  57. package/src/ios/APPNotificationCategory.h +28 -0
  58. package/src/ios/APPNotificationCategory.m +107 -0
  59. package/src/ios/APPNotificationContent.h +32 -0
  60. package/src/ios/APPNotificationContent.m +122 -0
  61. package/src/ios/APPNotificationOptions.h +42 -0
  62. package/src/ios/APPNotificationOptions.m +689 -0
  63. package/src/ios/UNNotificationRequest+APPLocalNotification.h +32 -0
  64. package/src/ios/UNNotificationRequest+APPLocalNotification.m +100 -0
  65. package/src/ios/UNUserNotificationCenter+APPLocalNotification.h +60 -0
  66. package/src/ios/UNUserNotificationCenter+APPLocalNotification.m +344 -0
  67. package/www/local-notification.js +1104 -0
@@ -0,0 +1,1104 @@
1
+ /*
2
+ * Apache 2.0 License
3
+ *
4
+ * Copyright (c) Sebastian Katzer 2017
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
+
22
+ var exec = require('cordova/exec'),
23
+ channel = require('cordova/channel');
24
+
25
+ // Options for every platform
26
+ exports._commonOptions = {
27
+ actions: [],
28
+ attachments: [],
29
+ // Custom data for the notification. Can be used, when the notification
30
+ // is send back to the app, e.g. by clicking on it.
31
+ data: null,
32
+ id: 1,
33
+ launch: true,
34
+ silent: false,
35
+ text: "",
36
+ // In Android 7, this sets the sound uri of a notification.
37
+ // Since Android 8, it sets the sound uri of a notification channel.
38
+ // The string 'default' represents the default notification sound and is not a path.
39
+ sound: 'default',
40
+ // If empty, the app name will be used
41
+ title: "",
42
+ // Default will be set on _prepareTrigger if nothing is set
43
+ trigger: null,
44
+ meta: {
45
+ plugin: 'cordova-plugin-local-notifications-continued',
46
+ version: '1.2.4' // Current plugin version
47
+ }
48
+ }
49
+
50
+ exports._androidAlarmTypes = {
51
+ RTC_WAKEUP: 0,
52
+ RTC: 1,
53
+ ELAPSED_REALTIME_WAKEUP: 2, // Not supported
54
+ ELAPSED_REALTIME: 3, // Not supported
55
+ }
56
+
57
+ exports._androidChannelImportanceTypes = {
58
+ IMPORTANCE_NONE: 0,
59
+ IMPORTANCE_MIN: 1,
60
+ IMPORTANCE_LOW: 2,
61
+ IMPORTANCE_DEFAULT: 3,
62
+ IMPORTANCE_HIGH: 4,
63
+ IMPORTANCE_MAX: 5
64
+ }
65
+
66
+ exports.androidUnusedAppRestrictionsStatusCodes = {
67
+ // The status of Unused App Restrictions could not be retrieved from this app e.g.
68
+ // if the app's target SDK version <30 or the user is in locked device boot mode
69
+ // Check the logs for the reason
70
+ ERROR: 0,
71
+
72
+ // There are no available Unused App Restrictions for this app (would only happen on Devices older then Android 7)
73
+ FEATURE_NOT_AVAILABLE: 1,
74
+
75
+ // Any available Unused App Restrictions on the device are disabled for this app.
76
+ // In other words, this app is exempt from having its permissions automatically removed or being hibernated.
77
+ DISABLED: 2,
78
+
79
+ // Unused App Restrictions introduced by Android API 30, and since made available on earlier (API 23-29) devices
80
+ // are enabled for this app: permission auto-reset. Note: This value is only used on API 29 or earlier devices.
81
+ API_30_BACKPORT: 3,
82
+
83
+ // Unused App Restrictions introduced by Android API 30 are enabled for this app: permission auto-reset.
84
+ // Note: This value is only used on API 30 or later devices.
85
+ API_30: 4,
86
+
87
+ // Unused App Restrictions introduced by Android API 31 are enabled for this app:
88
+ // permission auto-reset and app hibernation.
89
+ // Note: This value is only used on API 31 or later devices.
90
+ API_31: 5
91
+ }
92
+
93
+ // Options only available on Android
94
+ exports._androidSpecificOptions = {
95
+ androidAlarmType: exports._androidAlarmTypes.RTC_WAKEUP,
96
+ // Alarm will be allowed to execute even when the system is in low-power idle (a.k.a. doze) modes.
97
+ androidAllowWhileIdle: false,
98
+ // Make this notification automatically dismissed when the user touches it
99
+ androidAutoCancel : true,
100
+ androidChannelEnableLights: false,
101
+ androidChannelDescription: null,
102
+ androidChannelEnableVibration: false,
103
+ androidChannelId: "default_channel",
104
+ androidChannelImportance: exports._androidChannelImportanceTypes.IMPORTANCE_DEFAULT,
105
+ androidChannelName: "Default channel",
106
+ // soundUsage of a channel. Default is USAGE_NOTIFICATION
107
+ androidChannelSoundUsage: 5,
108
+ // The notification background color for the small icon
109
+ androidColor: null,
110
+ // Android 7 only: Sets the default notification options
111
+ androidDefaults: 0,
112
+ androidGroup: null,
113
+ androidGroupSummary: false,
114
+ androidMessages: null,
115
+ androidLargeIcon : null,
116
+ // Can be square or circle
117
+ androidLargeIconType: "square",
118
+ androidLockscreen: true,
119
+ androidOngoing: false,
120
+ androidOnlyAlertOnce: false,
121
+ androidProgressBar: null,
122
+ // If the Notification should show the when date
123
+ androidShowWhen: true,
124
+ androidSmallIcon: 'res://ic_popup_reminder',
125
+ androidSummary: null,
126
+ // Specifies a duration in milliseconds after which this notification should be canceled,
127
+ // if it is not already canceled.
128
+ androidTimeoutAfter: 0,
129
+ androidTitleCount: "%n%",
130
+ // Show the Notification#when field as a stopwatch. Instead of presenting when as a timestamp,
131
+ // the notification will show an automatically updating display of the minutes and seconds since when
132
+ androidUsesChronometer: false,
133
+ androidWakeUpScreen: true,
134
+ // Overwrites default
135
+ // Increments the badge by the specified number for that notification
136
+ badgeNumber: 1,
137
+ // Only for Android 7
138
+ led: false
139
+ }
140
+
141
+ // Options only available on iOS
142
+ exports._iOSSpecificOptions = {
143
+ // Overwrites default
144
+ // Set the badge directly.
145
+ // -1: The badge will not be changed
146
+ // 0: The badge will be cleared
147
+ badgeNumber: -1,
148
+ // Displays notification in foreground, when app is active.
149
+ iOSForeground : true
150
+ }
151
+
152
+ exports._deprecatedProperties = {
153
+ // Changes since version 1.1.0
154
+ autoClear: {newPropertyKey: 'androidAutoCancel', since: "1.1.0"},
155
+ badge: {newPropertyKey: 'badgeNumber', since: "1.1.0"},
156
+ channelDescription: {newPropertyKey: 'androidChannelDescription', since: "1.1.0"},
157
+ channelId: {newPropertyKey: 'androidChannelId', since: "1.1.0"},
158
+ channelImportance: {newPropertyKey: 'androidChannelImportance', since: "1.1.0"},
159
+ channelName: {newPropertyKey: 'androidChannelName', since: "1.1.0"},
160
+ clock: {message: "Use for 'clock: true' = 'androidShowWhen' and clock: 'chronometer' = 'androidUsesChronometer'", since: "1.1.0"},
161
+ color: {newPropertyKey: 'androidColor', since: "1.1.0"},
162
+ description: {newPropertyKey: 'androidChannelDescription', since: "1.1.0"},
163
+ defaults: {newPropertyKey: 'androidDefaults', since: "1.1.0"},
164
+ foreground: {newPropertyKey: 'iOSForeground', since: "1.1.0"},
165
+ group: {newPropertyKey: 'androidGroup', since: "1.1.0"},
166
+ groupSummary: {newPropertyKey: 'androidGroupSummary', since: "1.1.0"},
167
+ icon: {newPropertyKey: 'androidLargeIcon', since: "1.1.0"},
168
+ iconType: {renanewPropertyKeymedTo: 'androidLargeIconType', since: "1.1.0"},
169
+ importance: {newPropertyKey: 'androidChannelImportance', since: "1.1.0"},
170
+ lockscreen: {newPropertyKey: 'androidLockscreen', since: "1.1.0"},
171
+ mediaSession: {removed: true, since: "1.1.0", additionalMessage: "Not supported anymore."},
172
+ onlyAlertOnce: {newPropertyKey: 'androidOnlyAlertOnce', since: "1.1.0"},
173
+ prio: {additionalMessage: 'Use androidChannelImportance, androidAlarmType and androidAllowWhileIdle instead.', since: "1.1.0"},
174
+ priority: {additionalMessage: 'Use androidChannelImportance, androidAlarmType and androidAllowWhileIdle instead.', since: "1.1.0"},
175
+ progressBar: {newPropertyKey: 'androidProgressBar', since: "1.1.0"},
176
+ smallIcon: {newPropertyKey: 'androidSmallIcon', since: "1.1.0"},
177
+ soundUsage: {newPropertyKey: 'androidChannelSoundUsage', since: "1.1.0"},
178
+ sticky: {newPropertyKey: 'androidOngoing', since: "1.1.0"},
179
+ ongoing: {newPropertyKey: 'androidOngoing', since: "1.1.0"},
180
+ summary: {newPropertyKey: 'androidSummary', since: "1.1.0"},
181
+ timeoutAfter: {newPropertyKey: 'androidTimeoutAfter', since: "1.1.0"},
182
+ titleCount: {newPropertyKey: 'androidTitleCount', since: "1.1.0"},
183
+ vibrate: {newPropertyKey: 'androidChannelEnableVibration', since: "1.1.1"},
184
+ wakeup: {newPropertyKey: 'androidWakeUpScreen', since: "1.1.0"},
185
+ }
186
+
187
+ /**
188
+ * Setting some things before 'deviceready' event has fired.
189
+ */
190
+ channel.onCordovaReady.subscribe(function () {
191
+ channel.onCordovaInfoReady.subscribe(function () {
192
+ console.log("LocalNotification: onCordovaInfoReady");
193
+
194
+ // Set defaults
195
+ // To be compatible with Android 7 and a not updated WebView,
196
+ // Object.assign is used instead of a spread ... in object literals
197
+ exports._defaults = Object.assign(
198
+ {},
199
+ exports._commonOptions,
200
+ // Platform specific defaults
201
+ (device.platform == 'Android' ? exports._androidSpecificOptions : exports._iOSSpecificOptions)
202
+ );
203
+
204
+ exports._setLaunchDetails();
205
+ });
206
+ });
207
+
208
+ // Called after 'deviceready' event
209
+ channel.deviceready.subscribe(function () {
210
+ console.log("LocalNotification: deviceready");
211
+ if (!window.skipLocalNotificationReady) {
212
+ exports.fireQueuedEvents();
213
+ }
214
+ });
215
+
216
+ /**
217
+ * Set the launch details if the app was launched by clicking on a toast.
218
+ */
219
+ exports._setLaunchDetails = function () {
220
+ exports._exec('launch', null, function (details) {
221
+ if (details) {
222
+ exports.launchDetails = details;
223
+ }
224
+ });
225
+ };
226
+
227
+ /**
228
+ * ====================
229
+ * Plugin methods
230
+ * ====================
231
+ **/
232
+
233
+ /**
234
+ * Fire queued events once the device is ready and all listeners are registered.
235
+ */
236
+ exports.fireQueuedEvents = function() {
237
+ exports._exec('ready');
238
+ };
239
+
240
+ // Event listeners
241
+ // For an event, multiple listeners can be added.
242
+ exports._listeners = {};
243
+
244
+ /**
245
+ * Overwrite default settings.
246
+ * @param {Object} newDefaults
247
+ */
248
+ exports.setDefaults = function (newDefaults) {
249
+ Object.assign(this._defaults, newDefaults);
250
+ };
251
+
252
+ /**
253
+ * Gets the default settings.
254
+ */
255
+ exports.getDefaults = function () {
256
+ return this._defaults;
257
+ };
258
+
259
+ /**
260
+ * Android only: Create notification channel
261
+ * @param {Object} options channel options
262
+ * @param {Function} callback The function to be exec as the callback
263
+ * @param {Object} scope The callback function's scope
264
+ */
265
+ exports.createChannel = function (options, callback, scope) {
266
+ options = exports._optionsWithDefaults(options)
267
+ exports._prepareOptions(options);
268
+ exports._exec('createChannel', options, callback, scope);
269
+ };
270
+
271
+ /**
272
+ * Android only: Deletes a notification channel.
273
+ * If you create a new channel with this same id, the deleted channel will be un-deleted
274
+ * with all of the same settings it had before it was deleted
275
+ * See: https://developer.android.com/reference/androidx/core/app/NotificationManagerCompat#deleteNotificationChannel(java.lang.String)
276
+ *
277
+ * @param {string} channelId Channel ID to delete. Has to be a string like "my_channel_id"
278
+ * @param {Function} callback The function to be exec as the callback.
279
+ * @param {Object} scope The callback function's scope.
280
+ */
281
+ exports.deleteChannel = function (channelId, callback, scope) {
282
+ exports._exec('deleteChannel', channelId, callback, scope);
283
+ };
284
+
285
+ /**
286
+ * Check permission to show notifications.
287
+ * @param {Function} callback The function to be exec as the callback.
288
+ * @param {Object} scope The callback function's scope.
289
+ */
290
+ exports.hasPermission = function (callback, scope) {
291
+ exports._exec('hasPermission', null, callback, scope);
292
+ };
293
+
294
+ /**
295
+ * Request permission to show notifications.
296
+ * @param {Function} callback The function to be exec as the callback.
297
+ * @param {Object} scope The callback function's scope.
298
+ */
299
+ exports.requestPermission = function (callback, scope) {
300
+ console.log("Requesting permission");
301
+ exports._exec('requestPermission', null, callback, scope);
302
+ };
303
+
304
+ /**
305
+ * Android only: Check permission to schedule exact alarms.
306
+ * @param {Function} callback The function to be exec as the callback.
307
+ * @param {Object} scope The callback function's scope.
308
+ */
309
+ exports.canScheduleExactAlarms = function (callback, scope) {
310
+ exports._exec('canScheduleExactAlarms', null, callback, scope);
311
+ };
312
+
313
+ /**
314
+ * Schedule notifications
315
+ * @param {Object|Array} optionsArg The notifications to schedule
316
+ * @param {Function} callback
317
+ * @param {Object} scope The callback function's scope.
318
+ * @param {Object} args Optional, can be {skipPermission: true} to skip the permission check
319
+ */
320
+ exports.schedule = function (optionsArg, callback, scope, args) {
321
+ let optionsList = exports._toArray(optionsArg);
322
+
323
+ for (let i = 0; i < optionsList.length; i++) {
324
+ let options = exports._optionsWithDefaults(optionsList[i])
325
+ exports._prepareOptions(options);
326
+ // Store back the prepared options
327
+ optionsList[i] = options;
328
+ }
329
+
330
+ // Filter out notifications where the trigger time is in the past
331
+ // On iOS notifications are ignored if the trigger time is in the past, so filter
332
+ // them already here out
333
+ optionsList = optionsList.filter((options) => {
334
+
335
+ // Don't filter out, if trigger.at is not set
336
+ if (!options.trigger || !options.trigger.at) {
337
+ return true;
338
+ }
339
+
340
+ // Calculate difference to now
341
+ const triggerAtDiff = options.trigger.at - new Date().getTime();
342
+
343
+ // Trigger time is in the future don't filter out
344
+ if (triggerAtDiff > 0) return true;
345
+
346
+ // Trigger time is in the past, filter out
347
+ console.warn("Notification trigger time is in the past, ignoring it, options=", JSON.stringify(options));
348
+
349
+ return false;
350
+ });
351
+
352
+ // Skip permission check if requested and schedule directly
353
+ if (args && args.skipPermission) {
354
+ console.log("Skip permission check");
355
+ exports._exec('schedule', optionsList, callback, scope);
356
+
357
+ // Ask for permission
358
+ } else {
359
+ exports.requestPermission((granted) => {
360
+ console.log("Permission granted=" + granted);
361
+
362
+ if (!granted) {
363
+ if (callback) callback.call(scope || this, false);
364
+ return;
365
+ }
366
+
367
+ exports._exec('schedule', optionsList, callback, scope);
368
+ }, this);
369
+ }
370
+ };
371
+
372
+ /**
373
+ * Update notifications
374
+ * @param {Object|Array} options The notifications to update
375
+ * @param {Function} callback
376
+ * @param {Object} scope The callback function's scope.
377
+ * @param {Object} args Optional, can be {skipPermission: true} to skip the permission check
378
+ */
379
+ exports.update = function (options, callback, scope, args) {
380
+ const optionsList = exports._toArray(options);
381
+
382
+ for (const options of optionsList) {
383
+ // Correct renamed properties and don't merge defaults
384
+ // The defaults are not merged, because otherwise, some values
385
+ // could be set back to a default value
386
+ exports._prepareOptions(options);
387
+ }
388
+
389
+ // Skip permission check if requested and update directly
390
+ if (args && args.skipPermission) {
391
+ exports._exec('update', optionsList, callback, scope);
392
+
393
+ // Ask for permission
394
+ } else {
395
+ exports.requestPermission((granted) => {
396
+ if (!granted) {
397
+ if (callback) callback.call(scope || this, false);
398
+ return;
399
+ }
400
+
401
+ exports._exec('update', optionsList, callback, scope);
402
+ }, this);
403
+ }
404
+ };
405
+
406
+ /**
407
+ * Clear one or multiple notifications by id/ids
408
+ * @param {Array<number>|number} ids One Id or an array of Ids
409
+ * @param {Function} callback The function to be exec as the callback.
410
+ * @param {Object} scope The callback function's scope.
411
+ */
412
+ exports.clear = function (ids, callback, scope) {
413
+ exports._exec('clear', exports._convertIdsToNumbers(ids), callback, scope);
414
+ };
415
+
416
+ /**
417
+ * Clear all triggered notifications.
418
+ * @param {Function} callback The function to be exec as the callback.
419
+ * @param {Object} scope The callback function's scope.
420
+ */
421
+ exports.clearAll = function (callback, scope) {
422
+ exports._exec('clearAll', null, callback, scope);
423
+ };
424
+
425
+ /**
426
+ * Clear one or multiple notifications by id/ids
427
+ * @param {Array<number>|number} ids One Id or an array of Ids
428
+ * @param {Function} callback The function to be exec as the callback.
429
+ * @param {Object} scope The callback function's scope.
430
+ */
431
+ exports.cancel = function (ids, callback, scope) {
432
+ exports._exec('cancel', exports._convertIdsToNumbers(ids), callback, scope);
433
+ };
434
+
435
+ /**
436
+ * Cancel all scheduled notifications.
437
+ * @param {Function} callback The function to be exec as the callback.
438
+ * @param {Object} scope The callback function's scope.
439
+ */
440
+ exports.cancelAll = function (callback, scope) {
441
+ exports._exec('cancelAll', null, callback, scope);
442
+ };
443
+
444
+ /**
445
+ * Check if a notification is present.
446
+ * @param {number} id The ID of the notification.
447
+ * @param {Function} callback The function to be exec as the callback.
448
+ * @param {Object} scope The callback function's scope.
449
+ */
450
+ exports.isPresent = function (id, callback, scope) {
451
+ exports.getType(id, function (type) {
452
+ exports._callbackWithScope(callback, scope)(type != 'unknown');
453
+ });
454
+ };
455
+
456
+ /**
457
+ * Check if a notification is scheduled.
458
+ * @param {Int} id The ID of the notification.
459
+ * @param {Function} callback The function to be exec as the callback.
460
+ * @param {Object} scope The callback function's scope.
461
+ */
462
+ exports.isScheduled = function (id, callback, scope) {
463
+ exports.hasType(id, 'scheduled', callback, scope);
464
+ };
465
+
466
+ /**
467
+ * Check if a notification was triggered.
468
+ * @param {Int} id The ID of the notification.
469
+ * @param {Function} callback The function to be exec as the callback.
470
+ * @param {Object} scope The callback function's scope.
471
+ */
472
+ exports.isTriggered = function (id, callback, scope) {
473
+ exports.hasType(id, 'triggered', callback, scope);
474
+ };
475
+
476
+ /**
477
+ * Check if a notification has a given type.
478
+ * @param {number} id The ID of the notification.
479
+ * @param {string} type The type of the notification.
480
+ * @param {Function} callback The function to be exec as the callback.
481
+ * @param {Object} scope The callback function's scope.
482
+ */
483
+ exports.hasType = function (id, type, callback, scope) {
484
+ exports.getType(id, function (type2) {
485
+ exports._callbackWithScope(callback, scope)(type == type2);
486
+ });
487
+ };
488
+
489
+ /**
490
+ * Get the type (triggered, scheduled) for the notification.
491
+ * @param {number} id The ID of the notification.
492
+ * @param {Function} callback The function to be exec as the callback.
493
+ * @param {Object} scope The callback function's scope.
494
+ */
495
+ exports.getType = function (id, callback, scope) {
496
+ exports._exec('type', id, callback, scope);
497
+ };
498
+
499
+ /**
500
+ * List of all notification ids.
501
+ * @param {Function} callback The function to be exec as the callback.
502
+ * @param {Object} scope The callback function's scope.
503
+ */
504
+ exports.getIds = function (callback, scope) {
505
+ exports._exec('ids', 0, callback, scope);
506
+ };
507
+
508
+ /**
509
+ * List of all scheduled notification IDs.
510
+ * @param {Function} callback The function to be exec as the callback.
511
+ * @param {Object} scope The callback function's scope.
512
+ */
513
+ exports.getScheduledIds = function (callback, scope) {
514
+ exports._exec('ids', 1, callback, scope);
515
+ };
516
+
517
+ /**
518
+ * List of all triggered notification IDs.
519
+ * @param {Function} callback The function to be exec as the callback.
520
+ * @param {Object} scope The callback function's scope.
521
+ */
522
+ exports.getTriggeredIds = function (callback, scope) {
523
+ exports._exec('ids', 2, callback, scope);
524
+ };
525
+
526
+ /**
527
+ * List of local notifications specified by id.
528
+ * If called without IDs, all notification will be returned.
529
+ *
530
+ * @param [ Array<Int> ] ids The IDs of the notifications.
531
+ * @param [ Function ] callback The function to be exec as the callback.
532
+ * @param [ Object ] scope The callback function's scope.
533
+ *
534
+ * @return [ Void ]
535
+ */
536
+ exports.get = function () {
537
+ var args = Array.apply(null, arguments);
538
+
539
+ if (typeof args[0] == 'function') {
540
+ args.unshift([]);
541
+ }
542
+
543
+ var ids = args[0], callback = args[1], scope = args[2];
544
+
545
+ if (!Array.isArray(ids)) {
546
+ this._exec('notification', Number(ids), callback, scope);
547
+ return;
548
+ }
549
+
550
+ this._exec('notifications', [3, exports._convertIdsToNumbers(ids)], callback, scope);
551
+ };
552
+
553
+ /**
554
+ * List for all notifications.
555
+ *
556
+ * @param [ Function ] callback The function to be exec as the callback.
557
+ * @param [ Object ] scope The callback function's scope.
558
+ *
559
+ * @return [ Void ]
560
+ */
561
+ exports.getAll = function (callback, scope) {
562
+ this._exec('notifications', 0, callback, scope);
563
+ };
564
+
565
+ /**
566
+ * List of all scheduled notifications.
567
+ *
568
+ * @param [ Function ] callback The function to be exec as the callback.
569
+ * @param [ Object ] scope The callback function's scope.
570
+ */
571
+ exports.getScheduled = function (callback, scope) {
572
+ this._exec('notifications', 1, callback, scope);
573
+ };
574
+
575
+ /**
576
+ * List of all triggered notifications.
577
+ *
578
+ * @param [ Function ] callback The function to be exec as the callback.
579
+ * @param [ Object ] scope The callback function's scope.
580
+ */
581
+ exports.getTriggered = function (callback, scope) {
582
+ this._exec('notifications', 2, callback, scope);
583
+ };
584
+
585
+ /**
586
+ * Adds an action group with actions.
587
+ * @param {string} actionsGroupId
588
+ * @param {Array} actions The actions to add for the groupId
589
+ * @param {Function} callback The function to be exec as the callback.
590
+ * @param {Object} scope The callback function's scope.
591
+ */
592
+ exports.addActions = function (actionsGroupId, actions, callback, scope) {
593
+ this._exec('actions', [0, actionsGroupId, actions], callback, scope);
594
+ };
595
+
596
+ /**
597
+ * Remove an actions group.
598
+ * @param {string} actionsGroupId
599
+ * @param {Function} callback The function to be exec as the callback.
600
+ * @param {Object} scope The callback function's scope.
601
+ */
602
+ exports.removeActions = function (actionsGroupId, callback, scope) {
603
+ this._exec('actions', [1, actionsGroupId], callback, scope);
604
+ };
605
+
606
+ /**
607
+ * Check if a group of actions is defined.
608
+ * @param {string} actionsGroupId
609
+ * @param {Function} callback The function to be exec as the callback.
610
+ * @param {Object} scope The callback function's scope.
611
+ */
612
+ exports.hasActions = function (actionsGroupId, callback, scope) {
613
+ this._exec('actions', [2, actionsGroupId], callback, scope);
614
+ };
615
+
616
+ /**
617
+ * Open native settings to enable notifications.
618
+ * @param {Function} callback The function to be exec as the callback.
619
+ * @param {Object} scope The callback function's scope.
620
+ */
621
+ exports.openNotificationSettings = function (callback, scope) {
622
+ this._exec('openNotificationSettings', null, callback, scope);
623
+ };
624
+
625
+ /**
626
+ * Android only: Open native settings to enable alarms & reminders.
627
+ * @param {Function} callback The function to be exec as the callback.
628
+ * @param {Object} scope The callback function's scope.
629
+ */
630
+ exports.openAlarmSettings = function (callback, scope) {
631
+ this._exec('openAlarmSettings', null, callback, scope);
632
+ };
633
+
634
+ /**
635
+ * iOS only: Clear the badge of the app icon.
636
+ * @param {Function} callback
637
+ * @param {Object} scope
638
+ */
639
+ exports.iOSClearBadge = function (callback, scope) {
640
+ this._exec('clearBadge', null, callback, scope);
641
+ }
642
+
643
+ /**
644
+ * Android only: Returns the status of Unused App Restrictions.
645
+ * @param {Function} successCallback The function to be exec as the callback.
646
+ * @param {Object} scope The callback function's scope.
647
+ */
648
+ exports.getUnusedAppRestrictionsStatus = function (successCallback, scope) {
649
+ exports._exec('getUnusedAppRestrictionsStatus', null, successCallback, scope);
650
+ }
651
+
652
+ /**
653
+ * Android only: Redirects the user to manage their unused app restriction settings.
654
+ * @param {Function} successCallback The function to be exec as the callback.
655
+ * @param {Object} scope The callback function's scope.
656
+ */
657
+ exports.openManageUnusedAppRestrictions = function (successCallback, scope) {
658
+ exports._exec('openManageUnusedAppRestrictions', null, successCallback, scope);
659
+ }
660
+
661
+ /**
662
+ * Register callback for a given event.
663
+ * @param {string} event The name of the event.
664
+ * @param {Function|string} callback The function to be exec as callback or the
665
+ * method name on the scope, which should be called.
666
+ * @param {Object} scope The callback function's scope.
667
+ */
668
+ exports.on = function (event, callback, scope) {
669
+ // If callback is a string, a method on the scope schould be called
670
+ if (typeof callback !== 'function' && typeof callback !== 'string') return;
671
+
672
+ // Create empty array, if there are no listeners already
673
+ if (!this._listeners[event]) this._listeners[event] = [];
674
+
675
+ this._listeners[event].push([callback, scope || window]);
676
+ };
677
+
678
+ /**
679
+ * Unregister callback for given event
680
+ * @param {string} event The name of the event
681
+ * @param {Function|string} callback Callback or method name for the scope, which should be unregistered
682
+ */
683
+ exports.un = function (event, callback) {
684
+ // No listeners added to this event
685
+ if (!this._listeners[event]) return;
686
+ // Remove all listeners by callback or method name
687
+ this._listeners[event] = this._listeners[event].filter((listener) => listener[0] != callback);
688
+ };
689
+
690
+ /**
691
+ * Fire the event with given arguments.
692
+ * @param {string} event The event's name.
693
+ * @param {...Object} args The callback's arguments. The first element, can be the options of a notification.
694
+ */
695
+ exports.fireEvent = function (event, ...args) {
696
+ // No listeners added for this event
697
+ if (!this._listeners[event]) return;
698
+
699
+ // Convert custom notification data to object
700
+ if (args[0] && typeof args[0].data === 'string') {
701
+ args[0].data = JSON.parse(args[0].data);
702
+ }
703
+
704
+ for (const listener of this._listeners[event]) {
705
+ const callback = listener[0];
706
+ const scope = listener[1];
707
+
708
+ // If callback is a string, a method on the scope schould be called
709
+ if (typeof callback === 'string') callback = scope[callback];
710
+
711
+ callback.apply(scope, args);
712
+ }
713
+ };
714
+
715
+ /**
716
+ * Helper method to return the key for a value in an object.
717
+ * @param {Object} object
718
+ * @param {*} value
719
+ * @returns {string} The key of the value in the object or {@link undefined} if not found.
720
+ */
721
+ exports.getKey = function (object, value) {
722
+ return Object.keys(object).find(key => object[key] === value);
723
+ }
724
+
725
+ /**
726
+ * ====================
727
+ * Internal JS Methods for handling options etc.
728
+ * ====================
729
+ **/
730
+
731
+ /**
732
+ * Adds defaults to options if not present.
733
+ * This will not merge options objects like trigger.
734
+ * They will just be overwritten by the user options and not merged.
735
+ * @param {*} options
736
+ * @returns {Object} User options with defaults
737
+ */
738
+ exports._optionsWithDefaults = function (options) {
739
+ // Create a deep copy of defaults, so objects like trigger
740
+ // are copied and not referenced and changes on them would
741
+ // not impact the defaults
742
+ // To be compatible with Android 7 and a not updated WebView,
743
+ // Object.assign is used instead of a spread ... in object literals
744
+ return Object.assign({}, exports._deepCopy(exports._defaults), options)
745
+ }
746
+
747
+ /**
748
+ * - Correct renamed properties
749
+ * - Correct options to their required type
750
+ * - Warn about wrong smallIcon uri
751
+ * - Log unknown and deprecated properties
752
+ * - Remove null values, because of a Android bug
753
+ * @param {Object} options The options to convert
754
+ */
755
+ exports._prepareOptions = function (options) {
756
+ exports._handleDeprecatedProperties(options)
757
+
758
+ // Convert custom data to string
759
+ options.data = JSON.stringify(options.data)
760
+
761
+ // No auto cancelling, if the notification is ongoing
762
+ if (options.androidOngoing) options.androidAutoCancel = false
763
+
764
+ exports._prepareTrigger(options);
765
+ exports._prepareActions(options);
766
+ exports._prepareAndroidProgressBar(options);
767
+
768
+ // Convert Enums
769
+ // Android: alarmType string to integer
770
+ if (typeof options.androidAlarmType === 'string') {
771
+ options.androidAlarmType = exports._androidAlarmTypes[options.androidAlarmType]
772
+ }
773
+
774
+ // Android: channelImportance string to integer
775
+ if (typeof options.androidChannelImportance === 'string') {
776
+ options.androidChannelImportance = exports._androidChannelImportanceTypes[options.androidChannelImportance]
777
+ }
778
+
779
+ // Due to a Android bug, null values have to be removed
780
+ exports._removeNullValues(options);
781
+ exports._logUnknownProperties(options);
782
+ };
783
+
784
+ /**
785
+ * Corrects renamed properties since Plugin version 1.1.0
786
+ * @param {Oject} options
787
+ */
788
+ exports._handleDeprecatedProperties = function (options) {
789
+ if (device.platform == "Android") {
790
+
791
+ // text as Array to androidMessages, since 1.1.0
792
+ if (Array.isArray(options.text)) {
793
+ options.androidMessages = options.text
794
+ console.log("Property 'text' as array is deprecated since version 1.1.0. Use 'androidMessages' instead.")
795
+ }
796
+
797
+ // clock: boolean to androidShowWhen: boolean, since 1.1.0
798
+ if (typeof options.clock === 'boolean') {
799
+ options.androidShowWhen = options.clock
800
+ console.log("Property 'clock: boolean' is deprecated since version 1.1.0. Use 'androidShowWhen: boolean' instead.")
801
+ }
802
+
803
+ // "clock: 'chronometer'" to androidUsesChronometer: true, since 1.1.0
804
+ if (options.clock == "chronometer") {
805
+ options.androidUsesChronometer = true
806
+ console.log("Property 'clock: 'chronometer'' is deprecated since version 1.1.0. Use 'androidUsesChronometer: true' instead.")
807
+ }
808
+
809
+ // led replaced by androidChannelEnableLights, since Android 8
810
+ if (options.led) options.androidChannelEnableLights = true
811
+
812
+ // priority changed to androidChannelImportance, androidAlarmType and androidAllowWhileIdle
813
+ this._androidHandleOldPropertyPriority(options)
814
+ }
815
+
816
+ // sound: true changed to sound: "default", since 1.1.0
817
+ if (options.sound === true) {
818
+ options.sound = "default"
819
+ console.log(`Property "sound: true" is deprecated since version 1.1.0. Use "sound: 'default'" instead.`)
820
+ }
821
+
822
+ // sound: false changed to sound: null, since 1.1.0
823
+ if (options.sound === false) {
824
+ options.sound = null
825
+ console.log(`Property "sound: false" is deprecated since version 1.1.0. Use "sound: null" instead`)
826
+ }
827
+
828
+ // Handle renamed and removed properties
829
+ // Log deprecated properties
830
+ for (const key in options) {
831
+
832
+ // Check if the property is deprecated
833
+ const deprecatedProperty = this._deprecatedProperties[key];
834
+
835
+ // Property not deprecated
836
+ if (!deprecatedProperty) continue
837
+
838
+ let message;
839
+
840
+ // Check if propert is renamed
841
+ if (deprecatedProperty.newPropertyKey) {
842
+ message = `Use "${deprecatedProperty.newPropertyKey}" instead.`
843
+ // Set deprecated property value to new property
844
+ options[deprecatedProperty.newPropertyKey] = options[key]
845
+
846
+ // Property was removed
847
+ } else if (deprecatedProperty.removed) {
848
+ message = `Property removed`
849
+ }
850
+
851
+ if (deprecatedProperty.additionalMessage) {
852
+ message += ` ${deprecatedProperty.additionalMessage}`
853
+ }
854
+
855
+ console.warn(`Property "${key}" is deprecated since version ${deprecatedProperty.since}. ${message}`)
856
+ }
857
+ }
858
+
859
+ /**
860
+ * Android: Backward compatibility for property priority.
861
+ * Replaced by androidChannelImportance, androidAlarmType and androidAllowWhileIdle
862
+ * Removed in plugin version 1.1.0
863
+ * If priority is present, it will set androidAlarmType and androidAllowWhileIdle, but not androidChannelImportance.
864
+ * @param {Object} options
865
+ */
866
+ exports._androidHandleOldPropertyPriority = function (options) {
867
+ let priority = options.priority || options.prio;
868
+
869
+ // Old property not found
870
+ if (priority === undefined) return
871
+
872
+ if (typeof priority === 'string') {
873
+ priority = { min: -2, low: -1, high: 1, max: 2 }[priority] || 0;
874
+ }
875
+
876
+ if (options.foreground === true) {
877
+ priority = Math.max(priority, 1);
878
+ }
879
+
880
+ if (options.foreground === false) {
881
+ priority = Math.min(priority, 0);
882
+ }
883
+
884
+ // PRIORITY_MIN and PRIORITY_LOW
885
+ if (priority < 0) {
886
+ options.androidAlarmType = "RTC"
887
+ options.androidAllowWhileIdle = false
888
+
889
+ // PRIORITY_DEFAULT and PRIORITY_HIGH
890
+ } else if (priority < 2) {
891
+ options.androidAlarmType = "RTC_WAKEUP"
892
+ options.androidAllowWhileIdle = false
893
+
894
+ // PRIORITY_MAX
895
+ } else {
896
+ options.androidAlarmType = "RTC_WAKEUP"
897
+ options.androidAllowWhileIdle = true
898
+ }
899
+
900
+ options.priority = priority;
901
+ };
902
+
903
+ /**
904
+ * Convert the passed values to their required type, modifying them
905
+ * directly for Android and passing the converted list back for iOS.
906
+ *
907
+ * @param [ Map ] options Set of custom values.
908
+ *
909
+ * @return [ Map ] Interaction object with category & actions.
910
+ */
911
+ exports._prepareActions = function (options) {
912
+ // options.actions is a string or not set
913
+ if (!options.actions || typeof options.actions === 'string') return options;
914
+
915
+ let actions = [];
916
+
917
+ for (const action of options.actions) {
918
+ if (!action.id) {
919
+ console.warn('Action with title ' + action.title + ' ' +
920
+ 'has no id and will not be added.');
921
+ continue;
922
+ }
923
+
924
+ action.id = action.id.toString();
925
+ actions.push(action);
926
+ }
927
+
928
+ options.actions = actions;
929
+
930
+ return options;
931
+ };
932
+
933
+ /**
934
+ * Convert the passed values for the trigger to their required type.
935
+ * @param {Object} options
936
+ * @return {Object} Converted options
937
+ */
938
+ exports._prepareTrigger = function (options) {
939
+ let trigger = options.trigger || {};
940
+
941
+ // Set trigger type
942
+ if (!trigger.type) trigger.type = trigger.center ? "location" : "calendar";
943
+
944
+ if (trigger.type == "calendar") {
945
+ // Set default trigger time at now if nothing is set
946
+ if (!trigger.at && !trigger.in && !trigger.every) trigger.at = new Date().getTime();
947
+
948
+ // Convert dates to numbers
949
+ if (trigger.at) trigger.at = exports._dateToNumber(trigger.at);
950
+ if (trigger.firstAt) trigger.firstAt = exports._dateToNumber(trigger.firstAt);
951
+ if (trigger.before) trigger.before = exports._dateToNumber(trigger.before);
952
+ if (trigger.after) trigger.after = exports._dateToNumber(trigger.after);
953
+
954
+ // On iOS notifications will be ignored if the trigger time is in the past
955
+ // Correct trigger.at if trigger time is maximum 5 seconds in the past
956
+ if (trigger.at) {
957
+ // Calculate the difference to now
958
+ const triggerAtDiff = trigger.at - new Date().getTime();
959
+
960
+ // Only correct if maximum 5 seconds in the past
961
+ if (triggerAtDiff > -5000 && triggerAtDiff <= 0) {
962
+ // Set it a little bit in the future so it will be definitely triggered
963
+ trigger.at = new Date().getTime() + 5000;
964
+ console.log("Correct trigger.at 5 seconds in the future because it was a bit in the past, new trigger.at=" + trigger.at);
965
+ }
966
+ }
967
+
968
+ // Warning that trigger.count is not supported on iOS
969
+ if (device.platform == 'iOS' && trigger.count) {
970
+ console.warn('trigger.count is not supported on iOS.');
971
+ }
972
+
973
+ // Location trigger, set defaults
974
+ } else {
975
+ trigger.notifyOnEntry = !!trigger.notifyOnEntry;
976
+ trigger.notifyOnExit = trigger.notifyOnExit === true;
977
+ trigger.radius = trigger.radius || 5;
978
+ trigger.single = !!trigger.single;
979
+ }
980
+
981
+ options.trigger = trigger;
982
+
983
+ return options;
984
+ };
985
+
986
+ /**
987
+ * Convert the passed values for the progressBar to their required type.
988
+ * @param {Object} options
989
+ */
990
+ exports._prepareAndroidProgressBar = function (options) {
991
+ if (!options.androidProgressBar) return
992
+
993
+ let progressBar = options.androidProgressBar
994
+ progressBar.value = progressBar.value || 0;
995
+ progressBar.maxValue = progressBar.maxValue || 100;
996
+ progressBar.indeterminate = progressBar.indeterminate || false;
997
+
998
+ // Show the Notification#when field as a stopwatch
999
+ if (options.androidShowWhen) options.androidUsesChronometer = true;
1000
+ };
1001
+
1002
+ /**
1003
+ * On Android exists the bug, that when using JSONObject.optString("key", null),
1004
+ * it will return "NULL" as string and not plain null. This function removes
1005
+ * all null values, to workaround this. If this is also a problem on iOS, is not known.
1006
+ *
1007
+ * See: https://stackoverflow.com/questions/18226288/json-jsonobject-optstring-returns-string-null
1008
+ * @param {Object} options
1009
+ */
1010
+ exports._removeNullValues = function (options) {
1011
+ for (const key in options) {
1012
+ if (options[key] === null) delete options[key];
1013
+ }
1014
+ }
1015
+
1016
+ /**
1017
+ * Warns about unknown properties in options
1018
+ * @param {Object} options
1019
+ */
1020
+ exports._logUnknownProperties = function (options) {
1021
+ for (const key in options) {
1022
+ // Check if property is missing in defaults and is not a deprecated property
1023
+ if (this._defaults[key] === undefined && this._deprecatedProperties[key] === undefined) {
1024
+ console.warn('Unknown property: ' + key);
1025
+ }
1026
+ }
1027
+ }
1028
+
1029
+ /**
1030
+ * Create a callback function to get executed within a specific scope.
1031
+ *
1032
+ * @param {Function} callback The function to be exec as the callback.
1033
+ * @param {Object} scope The callback function's scope.
1034
+ * @return {Function} The callback function.
1035
+ */
1036
+ exports._callbackWithScope = function (callback, scope) {
1037
+ if (typeof callback != 'function') return;
1038
+
1039
+ return function () {
1040
+ callback.apply(scope || this, arguments);
1041
+ };
1042
+ };
1043
+
1044
+ /**
1045
+ * Convert the IDs to numbers.
1046
+ * @param {Array|Object} ids Will be turned into an array, if it's not already.
1047
+ * @return {Array<Number>}
1048
+ */
1049
+ exports._convertIdsToNumbers = function (ids) {
1050
+ return exports._toArray(ids).map((id) => Number(id))
1051
+ };
1052
+
1053
+ /**
1054
+ * Execute the native counterpart.
1055
+ *
1056
+ * @param [ String ] action The name of the action.
1057
+ * @param [ Array ] args Array of arguments.
1058
+ * @param [ Function] callback The callback function.
1059
+ * @param [ Object ] scope The scope for the function.
1060
+ *
1061
+ * @return [ Void ]
1062
+ */
1063
+ exports._exec = function (action, options, callback, scope) {
1064
+ exec(
1065
+ exports._callbackWithScope(callback, scope),
1066
+ null,
1067
+ 'LocalNotification',
1068
+ action,
1069
+ // Convert options to array, if not already
1070
+ options === null ? [] : this._toArray(options)
1071
+ );
1072
+ };
1073
+
1074
+ /**
1075
+ * First found value for the given keys.
1076
+ * @param {Object} options
1077
+ * @param {...string} findkeys Keys to find
1078
+ * @returns {*|void} The first found value or undefined
1079
+ */
1080
+ exports._getValueFor = function (options, ...findKeys) {
1081
+ for (const findKey of findKeys) {
1082
+ if (options.hasOwnProperty(findKey)) return options[findKey];
1083
+ }
1084
+
1085
+ return undefined;
1086
+ };
1087
+
1088
+ /**
1089
+ * Convert a value to an array, if it is not already an array.
1090
+ *
1091
+ * @param {Object|Array} object Any kind of object, or an array.
1092
+ * @return {Array} An array with the object as first item or the object itself, if it's already an array.
1093
+ */
1094
+ exports._toArray = function (object) {
1095
+ return Array.isArray(object) ? object : [object];
1096
+ };
1097
+
1098
+ exports._deepCopy = function (object) {
1099
+ return JSON.parse(JSON.stringify(object));
1100
+ };
1101
+
1102
+ exports._dateToNumber = function (date) {
1103
+ return date instanceof Date ? date.getTime() : date;
1104
+ };