APPLocalNotification.m 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755
  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 "APPLocalNotification.h"
  24. #import "APPLocalNotificationOptions.h"
  25. #import "UIApplication+APPLocalNotification.h"
  26. #import "UILocalNotification+APPLocalNotification.h"
  27. @import UserNotifications;
  28. @interface APPLocalNotification ()
  29. // Retrieves the application state
  30. @property (readonly, getter=applicationState) NSString* applicationState;
  31. // All events will be queued until deviceready has been fired
  32. @property (readwrite, assign) BOOL deviceready;
  33. // Event queue
  34. @property (readonly, nonatomic, retain) NSMutableArray* eventQueue;
  35. // Needed when calling `registerPermission`
  36. @property (nonatomic, retain) CDVInvokedUrlCommand* command;
  37. @end
  38. @implementation APPLocalNotification
  39. @synthesize deviceready, eventQueue;
  40. #pragma mark -
  41. #pragma mark Interface
  42. /**
  43. * Execute all queued events.
  44. */
  45. - (void) deviceready:(CDVInvokedUrlCommand*)command
  46. {
  47. deviceready = YES;
  48. for (NSString* js in eventQueue) {
  49. [self.commandDelegate evalJs:js];
  50. }
  51. [eventQueue removeAllObjects];
  52. }
  53. /**
  54. * Schedule a set of notifications.
  55. *
  56. * @param properties
  57. * A dict of properties for each notification
  58. */
  59. - (void) schedule:(CDVInvokedUrlCommand*)command
  60. {
  61. NSArray* notifications = command.arguments;
  62. [self.commandDelegate runInBackground:^{
  63. for (NSDictionary* options in notifications) {
  64. UILocalNotification* notification;
  65. notification = [[UILocalNotification alloc]
  66. initWithOptions:options];
  67. [self scheduleLocalNotification:[notification copy]];
  68. [self fireEvent:@"schedule" notification:notification];
  69. if (notifications.count > 1) {
  70. [NSThread sleepForTimeInterval:0.01];
  71. }
  72. }
  73. [self execCallback:command];
  74. }];
  75. }
  76. /**
  77. * Update a set of notifications.
  78. *
  79. * @param properties
  80. * A dict of properties for each notification
  81. */
  82. - (void) update:(CDVInvokedUrlCommand*)command
  83. {
  84. NSArray* notifications = command.arguments;
  85. [self.commandDelegate runInBackground:^{
  86. for (NSDictionary* options in notifications) {
  87. NSNumber* id = [options objectForKey:@"id"];
  88. UILocalNotification* notification;
  89. notification = [self.app localNotificationWithId:id];
  90. if (!notification)
  91. continue;
  92. [self updateLocalNotification:[notification copy]
  93. withOptions:options];
  94. [self fireEvent:@"update" notification:notification];
  95. if (notifications.count > 1) {
  96. [NSThread sleepForTimeInterval:0.01];
  97. }
  98. }
  99. [self execCallback:command];
  100. }];
  101. }
  102. /**
  103. * Cancel a set of notifications.
  104. *
  105. * @param ids
  106. * The IDs of the notifications
  107. */
  108. - (void) cancel:(CDVInvokedUrlCommand*)command
  109. {
  110. [self.commandDelegate runInBackground:^{
  111. for (NSNumber* id in command.arguments) {
  112. UILocalNotification* notification;
  113. notification = [self.app localNotificationWithId:id];
  114. if (!notification)
  115. continue;
  116. [self.app cancelLocalNotification:notification];
  117. [self fireEvent:@"cancel" notification:notification];
  118. }
  119. [self execCallback:command];
  120. }];
  121. }
  122. /**
  123. * Cancel all local notifications.
  124. */
  125. - (void) cancelAll:(CDVInvokedUrlCommand*)command
  126. {
  127. [self.commandDelegate runInBackground:^{
  128. [self cancelAllLocalNotifications];
  129. [self fireEvent:@"cancelall"];
  130. [self execCallback:command];
  131. }];
  132. }
  133. /**
  134. * Clear a set of notifications.
  135. *
  136. * @param ids
  137. * The IDs of the notifications
  138. */
  139. - (void) clear:(CDVInvokedUrlCommand*)command
  140. {
  141. [self.commandDelegate runInBackground:^{
  142. for (NSNumber* id in command.arguments) {
  143. UILocalNotification* notification;
  144. notification = [self.app localNotificationWithId:id];
  145. if (!notification)
  146. continue;
  147. [self.app clearLocalNotification:notification];
  148. [self fireEvent:@"clear" notification:notification];
  149. }
  150. [self execCallback:command];
  151. }];
  152. }
  153. /**
  154. * Clear all local notifications.
  155. */
  156. - (void) clearAll:(CDVInvokedUrlCommand*)command
  157. {
  158. [self.commandDelegate runInBackground:^{
  159. [self clearAllLocalNotifications];
  160. [self fireEvent:@"clearall"];
  161. [self execCallback:command];
  162. }];
  163. }
  164. /**
  165. * If a notification by ID is present.
  166. *
  167. * @param id
  168. * The ID of the notification
  169. */
  170. - (void) isPresent:(CDVInvokedUrlCommand *)command
  171. {
  172. [self isPresent:command type:NotifcationTypeAll];
  173. }
  174. /**
  175. * If a notification by ID is scheduled.
  176. *
  177. * @param id
  178. * The ID of the notification
  179. */
  180. - (void) isScheduled:(CDVInvokedUrlCommand*)command
  181. {
  182. [self isPresent:command type:NotifcationTypeScheduled];
  183. }
  184. /**
  185. * Check if a notification with an ID is triggered.
  186. *
  187. * @param id
  188. * The ID of the notification
  189. */
  190. - (void) isTriggered:(CDVInvokedUrlCommand*)command
  191. {
  192. [self isPresent:command type:NotifcationTypeTriggered];
  193. }
  194. /**
  195. * Check if a notification with an ID exists.
  196. *
  197. * @param type
  198. * The notification life cycle type
  199. */
  200. - (void) isPresent:(CDVInvokedUrlCommand*)command
  201. type:(APPLocalNotificationType)type;
  202. {
  203. [self.commandDelegate runInBackground:^{
  204. NSNumber* id = [command argumentAtIndex:0];
  205. BOOL exist;
  206. CDVPluginResult* result;
  207. if (type == NotifcationTypeAll) {
  208. exist = [self.app localNotificationExist:id];
  209. } else {
  210. exist = [self.app localNotificationExist:id type:type];
  211. }
  212. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  213. messageAsBool:exist];
  214. [self.commandDelegate sendPluginResult:result
  215. callbackId:command.callbackId];
  216. }];
  217. }
  218. /**
  219. * List all ids from all local notifications.
  220. */
  221. - (void) getAllIds:(CDVInvokedUrlCommand*)command
  222. {
  223. [self getIds:command byType:NotifcationTypeAll];
  224. }
  225. /**
  226. * List all ids from all pending notifications.
  227. */
  228. - (void) getScheduledIds:(CDVInvokedUrlCommand*)command
  229. {
  230. [self getIds:command byType:NotifcationTypeScheduled];
  231. }
  232. /**
  233. * List all ids from all triggered notifications.
  234. */
  235. - (void) getTriggeredIds:(CDVInvokedUrlCommand*)command
  236. {
  237. [self getIds:command byType:NotifcationTypeTriggered];
  238. }
  239. /**
  240. * List of ids for given local notifications.
  241. *
  242. * @param type
  243. * Notification life cycle type
  244. * @param ids
  245. * The IDs of the notifications
  246. */
  247. - (void) getIds:(CDVInvokedUrlCommand*)command
  248. byType:(APPLocalNotificationType)type;
  249. {
  250. [self.commandDelegate runInBackground:^{
  251. CDVPluginResult* result;
  252. NSArray* ids;
  253. if (type == NotifcationTypeAll) {
  254. ids = [self.app localNotificationIds];
  255. } else {
  256. ids = [self.app localNotificationIdsByType:type];
  257. }
  258. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  259. messageAsArray:ids];
  260. [self.commandDelegate sendPluginResult:result
  261. callbackId:command.callbackId];
  262. }];
  263. }
  264. /**
  265. * Propertys for given local notification.
  266. */
  267. - (void) getSingle:(CDVInvokedUrlCommand*)command
  268. {
  269. [self getOption:command byType:NotifcationTypeAll];
  270. }
  271. /**
  272. * Propertya for given scheduled notification.
  273. */
  274. - (void) getSingleScheduled:(CDVInvokedUrlCommand*)command
  275. {
  276. [self getOption:command byType:NotifcationTypeScheduled];
  277. }
  278. // Propertys for given triggered notification
  279. - (void) getSingleTriggered:(CDVInvokedUrlCommand*)command
  280. {
  281. [self getOption:command byType:NotifcationTypeTriggered];
  282. }
  283. /**
  284. * Property list for given local notifications.
  285. *
  286. * @param ids
  287. * The IDs of the notifications
  288. */
  289. - (void) getAll:(CDVInvokedUrlCommand*)command
  290. {
  291. [self getOptions:command byType:NotifcationTypeAll];
  292. }
  293. /**
  294. * Property list for given scheduled notifications.
  295. *
  296. * @param ids
  297. * The IDs of the notifications
  298. */
  299. - (void) getScheduled:(CDVInvokedUrlCommand*)command
  300. {
  301. [self getOptions:command byType:NotifcationTypeScheduled];
  302. }
  303. /**
  304. * Property list for given triggered notifications.
  305. *
  306. * @param ids
  307. * The IDs of the notifications
  308. */
  309. - (void) getTriggered:(CDVInvokedUrlCommand *)command
  310. {
  311. [self getOptions:command byType:NotifcationTypeTriggered];
  312. }
  313. /**
  314. * Propertys for given triggered notification.
  315. *
  316. * @param type
  317. * Notification life cycle type
  318. * @param ids
  319. * The ID of the notification
  320. */
  321. - (void) getOption:(CDVInvokedUrlCommand*)command
  322. byType:(APPLocalNotificationType)type;
  323. {
  324. [self.commandDelegate runInBackground:^{
  325. NSArray* ids = command.arguments;
  326. NSArray* notifications;
  327. CDVPluginResult* result;
  328. if (type == NotifcationTypeAll) {
  329. notifications = [self.app localNotificationOptionsById:ids];
  330. }
  331. else {
  332. notifications = [self.app localNotificationOptionsByType:type
  333. andId:ids];
  334. }
  335. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  336. messageAsDictionary:[notifications firstObject]];
  337. [self.commandDelegate sendPluginResult:result
  338. callbackId:command.callbackId];
  339. }];
  340. }
  341. /**
  342. * Property list for given triggered notifications.
  343. *
  344. * @param type
  345. * Notification life cycle type
  346. * @param ids
  347. * The IDs of the notifications
  348. */
  349. - (void) getOptions:(CDVInvokedUrlCommand*)command
  350. byType:(APPLocalNotificationType)type;
  351. {
  352. [self.commandDelegate runInBackground:^{
  353. NSArray* ids = command.arguments;
  354. NSArray* notifications;
  355. CDVPluginResult* result;
  356. if (type == NotifcationTypeAll && ids.count == 0) {
  357. notifications = [self.app localNotificationOptions];
  358. }
  359. else if (type == NotifcationTypeAll) {
  360. notifications = [self.app localNotificationOptionsById:ids];
  361. }
  362. else if (ids.count == 0) {
  363. notifications = [self.app localNotificationOptionsByType:type];
  364. }
  365. else {
  366. notifications = [self.app localNotificationOptionsByType:type
  367. andId:ids];
  368. }
  369. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  370. messageAsArray:notifications];
  371. [self.commandDelegate sendPluginResult:result
  372. callbackId:command.callbackId];
  373. }];
  374. }
  375. /**
  376. * Inform if the app has the permission to show
  377. * badges and local notifications.
  378. */
  379. - (void) hasPermission:(CDVInvokedUrlCommand*)command
  380. {
  381. [self.commandDelegate runInBackground:^{
  382. CDVPluginResult* result;
  383. BOOL hasPermission;
  384. hasPermission = [self.app hasPermissionToScheduleLocalNotifications];
  385. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  386. messageAsBool:hasPermission];
  387. [self.commandDelegate sendPluginResult:result
  388. callbackId:command.callbackId];
  389. }];
  390. }
  391. /**
  392. * Ask for permission to show badges.
  393. */
  394. - (void) registerPermission:(CDVInvokedUrlCommand*)command
  395. {
  396. if ([[UIApplication sharedApplication]
  397. respondsToSelector:@selector(registerUserNotificationSettings:)])
  398. {
  399. _command = command;
  400. [self.commandDelegate runInBackground:^{
  401. [self.app registerPermissionToScheduleLocalNotifications];
  402. }];
  403. } else {
  404. [self hasPermission:command];
  405. }
  406. }
  407. #pragma mark -
  408. #pragma mark Core Logic
  409. /**
  410. * Schedule the local notification.
  411. */
  412. - (void) scheduleLocalNotification:(UILocalNotification*)notification
  413. {
  414. [self cancelForerunnerLocalNotification:notification];
  415. [self.app scheduleLocalNotification:notification];
  416. }
  417. /**
  418. * Update the local notification.
  419. */
  420. - (void) updateLocalNotification:(UILocalNotification*)notification
  421. withOptions:(NSDictionary*)newOptions
  422. {
  423. NSMutableDictionary* options = [notification.userInfo mutableCopy];
  424. [options addEntriesFromDictionary:newOptions];
  425. [options setObject:[NSDate date] forKey:@"updatedAt"];
  426. notification = [[UILocalNotification alloc]
  427. initWithOptions:options];
  428. [self scheduleLocalNotification:notification];
  429. }
  430. /**
  431. * Cancel all local notifications.
  432. */
  433. - (void) cancelAllLocalNotifications
  434. {
  435. [self.app cancelAllLocalNotifications];
  436. [self.app setApplicationIconBadgeNumber:0];
  437. }
  438. /**
  439. * Clear all local notifications.
  440. */
  441. - (void) clearAllLocalNotifications
  442. {
  443. [self.app clearAllLocalNotifications];
  444. [self.app setApplicationIconBadgeNumber:0];
  445. }
  446. /**
  447. * Cancel a maybe given forerunner with the same ID.
  448. */
  449. - (void) cancelForerunnerLocalNotification:(UILocalNotification*)notification
  450. {
  451. NSNumber* id = notification.options.id;
  452. UILocalNotification* forerunner;
  453. forerunner = [self.app localNotificationWithId:id];
  454. if (!forerunner)
  455. return;
  456. [self.app cancelLocalNotification:forerunner];
  457. }
  458. /**
  459. * Cancels all non-repeating local notification older then
  460. * a specific amount of seconds
  461. */
  462. - (void) cancelAllNotificationsWhichAreOlderThen:(float)seconds
  463. {
  464. NSArray* notifications;
  465. notifications = [self.app localNotifications];
  466. for (UILocalNotification* notification in notifications)
  467. {
  468. if (![notification isRepeating]
  469. && notification.timeIntervalSinceFireDate > seconds)
  470. {
  471. [self.app cancelLocalNotification:notification];
  472. [self fireEvent:@"cancel" notification:notification];
  473. }
  474. }
  475. }
  476. #pragma mark -
  477. #pragma mark Delegates
  478. /**
  479. * Calls the cancel or trigger event after a local notification was received.
  480. * Cancels the local notification if autoCancel was set to true.
  481. */
  482. - (void) didReceiveLocalNotification:(NSNotification*)localNotification
  483. {
  484. UILocalNotification* notification = [localNotification object];
  485. if ([notification userInfo] == NULL || [notification wasUpdated])
  486. return;
  487. NSTimeInterval timeInterval = [notification timeIntervalSinceLastTrigger];
  488. NSString* event = timeInterval < 0.2 && deviceready ? @"trigger" : @"click";
  489. [self fireEvent:event notification:notification];
  490. if (![event isEqualToString:@"click"])
  491. return;
  492. if ([notification isRepeating]) {
  493. [self fireEvent:@"clear" notification:notification];
  494. } else {
  495. [self.app cancelLocalNotification:notification];
  496. [self fireEvent:@"cancel" notification:notification];
  497. }
  498. }
  499. /**
  500. * Called when app has started
  501. * (by clicking on a local notification).
  502. */
  503. - (void) didFinishLaunchingWithOptions:(NSNotification*)notification
  504. {
  505. NSDictionary* launchOptions = [notification userInfo];
  506. UILocalNotification* localNotification;
  507. localNotification = [launchOptions objectForKey:
  508. UIApplicationLaunchOptionsLocalNotificationKey];
  509. if (localNotification) {
  510. [self didReceiveLocalNotification:
  511. [NSNotification notificationWithName:CDVLocalNotification
  512. object:localNotification]];
  513. }
  514. }
  515. /**
  516. * Called on otification settings registration is completed.
  517. */
  518. - (void) didRegisterUserNotificationSettings:(UIUserNotificationSettings*)settings
  519. {
  520. if (_command) {
  521. [self hasPermission:_command];
  522. _command = NULL;
  523. }
  524. }
  525. #pragma mark -
  526. #pragma mark Life Cycle
  527. /**
  528. * Registers obervers after plugin was initialized.
  529. */
  530. - (void) pluginInitialize
  531. {
  532. eventQueue = [[NSMutableArray alloc] init];
  533. }
  534. /**
  535. * Clears all single repeating notifications which are older then 5 days
  536. * before the app terminates.
  537. */
  538. - (void) onAppTerminate
  539. {
  540. [self cancelAllNotificationsWhichAreOlderThen:432000];
  541. }
  542. #pragma mark -
  543. #pragma mark Helper
  544. /**
  545. * Retrieves the application state
  546. *
  547. * @return
  548. * Either "background" or "foreground"
  549. */
  550. - (NSString*) applicationState
  551. {
  552. UIApplicationState state = [self.app applicationState];
  553. bool isActive = state == UIApplicationStateActive;
  554. return isActive ? @"foreground" : @"background";
  555. }
  556. /**
  557. * Simply invokes the callback without any parameter.
  558. */
  559. - (void) execCallback:(CDVInvokedUrlCommand*)command
  560. {
  561. CDVPluginResult *result = [CDVPluginResult
  562. resultWithStatus:CDVCommandStatus_OK];
  563. [self.commandDelegate sendPluginResult:result
  564. callbackId:command.callbackId];
  565. }
  566. /**
  567. * Short hand for shared application instance.
  568. */
  569. - (UIApplication*) app
  570. {
  571. return [UIApplication sharedApplication];
  572. }
  573. /**
  574. * Fire general event.
  575. */
  576. - (void) fireEvent:(NSString*)event
  577. {
  578. [self fireEvent:event notification:NULL];
  579. }
  580. /**
  581. * Fire event for local notification.
  582. */
  583. - (void) fireEvent:(NSString*)event notification:(UILocalNotification*)notification
  584. {
  585. if (IsAtLeastiOSVersion(@"10.0")) {
  586. UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
  587. content.title = [NSString localizedUserNotificationStringForKey:notification.alertTitle arguments:nil];
  588. content.body = [NSString localizedUserNotificationStringForKey:notification.alertBody
  589. arguments:nil];
  590. content.sound = [UNNotificationSound defaultSound];
  591. NSDate *fireDate = notification.fireDate;
  592. if(fireDate==nil) {
  593. fireDate = [NSDate date];
  594. }
  595. NSCalendar *gregorianCalendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];
  596. // Extract all date components into dateComponents
  597. NSDateComponents *dateComponents = [gregorianCalendar components:NSYearCalendarUnit | NSMonthCalendarUnit | NSDayCalendarUnit
  598. | NSHourCalendarUnit | NSMinuteCalendarUnit | NSSecondCalendarUnit
  599. fromDate:fireDate];
  600. /// 4. update application icon badge number
  601. //content.badge = @([[UIApplication sharedApplication] applicationIconBadgeNumber] + 1);
  602. // Deliver the notification at the fire date.
  603. UNCalendarNotificationTrigger *trigger = [UNCalendarNotificationTrigger triggerWithDateMatchingComponents:dateComponents repeats:NO];
  604. UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:@"DefaultNotificationIdentifier" content:content trigger:trigger];
  605. /// 3. schedule localNotification
  606. UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
  607. [center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
  608. if (!error) {
  609. NSLog(@"add NotificationRequest succeeded!");
  610. }
  611. }];
  612. } else {
  613. NSString* js;
  614. NSString* params = [NSString stringWithFormat:
  615. @"\"%@\"", self.applicationState];
  616. if (notification) {
  617. NSString* args = [notification encodeToJSON];
  618. params = [NSString stringWithFormat:
  619. @"%@,'%@'",
  620. args, self.applicationState];
  621. }
  622. js = [NSString stringWithFormat:
  623. @"cordova.plugins.notification.local.core.fireEvent('%@', %@)",
  624. event, params];
  625. if (deviceready) {
  626. [self.commandDelegate evalJs:js];
  627. } else {
  628. [self.eventQueue addObject:js];
  629. }
  630. }
  631. }
  632. @end