APPLocalNotification.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691
  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. #import "APPLocalNotification.h"
  22. #import "APPNotificationOptions.h"
  23. #import "UNUserNotificationCenter+APPLocalNotification.h"
  24. #import "UNNotificationRequest+APPLocalNotification.h"
  25. #import "APPNotificationContent.h"
  26. @interface APPLocalNotification ()
  27. @property (strong, nonatomic) UNUserNotificationCenter* center;
  28. @property (readwrite, assign) BOOL deviceready;
  29. @property (readwrite, assign) BOOL isActive;
  30. @property (readonly, nonatomic, retain) NSArray* launchDetails;
  31. @property (readonly, nonatomic, retain) NSMutableArray* eventQueue;
  32. @end
  33. @implementation APPLocalNotification
  34. @synthesize deviceready, isActive, eventQueue;
  35. #pragma mark -
  36. #pragma mark Interface
  37. /**
  38. * Set launchDetails object.
  39. *
  40. * @return [ Void ]
  41. */
  42. - (void) launch:(CDVInvokedUrlCommand*)command
  43. {
  44. NSString* js;
  45. if (!_launchDetails)
  46. return;
  47. js = [NSString stringWithFormat:
  48. @"cordova.plugins.notification.local.launchDetails = {id:%@, action:'%@'}",
  49. _launchDetails[0], _launchDetails[1]];
  50. [self.commandDelegate evalJs:js];
  51. _launchDetails = NULL;
  52. }
  53. /**
  54. * Execute all queued events.
  55. *
  56. * @return [ Void ]
  57. */
  58. - (void) ready:(CDVInvokedUrlCommand*)command
  59. {
  60. deviceready = YES;
  61. for (NSString* js in eventQueue) {
  62. [self.commandDelegate evalJs:js];
  63. }
  64. [eventQueue removeAllObjects];
  65. }
  66. /**
  67. * Schedule notifications.
  68. *
  69. * @param [Array<Hash>] properties A list of key-value properties.
  70. *
  71. * @return [ Void ]
  72. */
  73. - (void) schedule:(CDVInvokedUrlCommand*)command
  74. {
  75. NSArray* notifications = command.arguments;
  76. [self.commandDelegate runInBackground:^{
  77. for (NSDictionary* options in notifications) {
  78. APPNotificationContent* notification;
  79. notification = [[APPNotificationContent alloc]
  80. initWithOptions:options];
  81. [self scheduleNotification:notification];
  82. }
  83. [self execCallback:command];
  84. }];
  85. }
  86. /**
  87. * Update notifications.
  88. *
  89. * @param [Array<Hash>] properties A list of key-value properties.
  90. *
  91. * @return [ Void ]
  92. */
  93. - (void) update:(CDVInvokedUrlCommand*)command
  94. {
  95. NSArray* notifications = command.arguments;
  96. [self.commandDelegate runInBackground:^{
  97. for (NSDictionary* options in notifications) {
  98. NSNumber* id = [options objectForKey:@"id"];
  99. UNNotificationRequest* notification;
  100. notification = [_center getNotificationWithId:id];
  101. if (!notification)
  102. continue;
  103. [self updateNotification:[notification copy]
  104. withOptions:options];
  105. [self fireEvent:@"update" notification:notification];
  106. }
  107. [self execCallback:command];
  108. }];
  109. }
  110. /**
  111. * Clear notifications by id.
  112. *
  113. * @param [ Array<Int> ] The IDs of the notifications to clear.
  114. *
  115. * @return [ Void ]
  116. */
  117. - (void) clear:(CDVInvokedUrlCommand*)command
  118. {
  119. [self.commandDelegate runInBackground:^{
  120. for (NSNumber* id in command.arguments) {
  121. UNNotificationRequest* notification;
  122. notification = [_center getNotificationWithId:id];
  123. if (!notification)
  124. continue;
  125. [_center clearNotification:notification];
  126. [self fireEvent:@"clear" notification:notification];
  127. }
  128. [self execCallback:command];
  129. }];
  130. }
  131. /**
  132. * Clear all local notifications.
  133. *
  134. * @return [ Void ]
  135. */
  136. - (void) clearAll:(CDVInvokedUrlCommand*)command
  137. {
  138. [self.commandDelegate runInBackground:^{
  139. [_center clearAllNotifications];
  140. [self clearApplicationIconBadgeNumber];
  141. [self fireEvent:@"clearall"];
  142. [self execCallback:command];
  143. }];
  144. }
  145. /**
  146. * Cancel notifications by id.
  147. *
  148. * @param [ Array<Int> ] The IDs of the notifications to clear.
  149. *
  150. * @return [ Void ]
  151. */
  152. - (void) cancel:(CDVInvokedUrlCommand*)command
  153. {
  154. [self.commandDelegate runInBackground:^{
  155. for (NSNumber* id in command.arguments) {
  156. UNNotificationRequest* notification;
  157. notification = [_center getNotificationWithId:id];
  158. if (!notification)
  159. continue;
  160. [_center cancelNotification:notification];
  161. [self fireEvent:@"cancel" notification:notification];
  162. }
  163. [self execCallback:command];
  164. }];
  165. }
  166. /**
  167. * Cancel all local notifications.
  168. *
  169. * @return [ Void ]
  170. */
  171. - (void) cancelAll:(CDVInvokedUrlCommand*)command
  172. {
  173. [self.commandDelegate runInBackground:^{
  174. [_center cancelAllNotifications];
  175. [self clearApplicationIconBadgeNumber];
  176. [self fireEvent:@"cancelall"];
  177. [self execCallback:command];
  178. }];
  179. }
  180. /**
  181. * Get type of notification.
  182. *
  183. * @param [ Int ] id The ID of the notification.
  184. *
  185. * @return [ Void ]
  186. */
  187. - (void) type:(CDVInvokedUrlCommand*)command
  188. {
  189. [self.commandDelegate runInBackground:^{
  190. NSNumber* id = [command argumentAtIndex:0];
  191. NSString* type;
  192. switch ([_center getTypeOfNotificationWithId:id]) {
  193. case NotifcationTypeScheduled:
  194. type = @"scheduled";
  195. break;
  196. case NotifcationTypeTriggered:
  197. type = @"triggered";
  198. break;
  199. default:
  200. type = @"unknown";
  201. }
  202. CDVPluginResult* result;
  203. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  204. messageAsString:type];
  205. [self.commandDelegate sendPluginResult:result
  206. callbackId:command.callbackId];
  207. }];
  208. }
  209. /**
  210. * List of all notification IDs.
  211. *
  212. * @return [ Void ]
  213. */
  214. - (void) ids:(CDVInvokedUrlCommand*)command
  215. {
  216. [self ids:command byType:NotifcationTypeAll];
  217. }
  218. /**
  219. * List of all scheduled notification IDs.
  220. *
  221. * @return [ Void ]
  222. */
  223. - (void) scheduledIds:(CDVInvokedUrlCommand*)command
  224. {
  225. [self ids:command byType:NotifcationTypeScheduled];
  226. }
  227. /**
  228. * List of all triggered notification IDs.
  229. *
  230. * @return [ Void ]
  231. */
  232. - (void) triggeredIds:(CDVInvokedUrlCommand*)command
  233. {
  234. [self ids:command byType:NotifcationTypeTriggered];
  235. }
  236. /**
  237. * List of ids for given local notifications.
  238. *
  239. * @param [ APPNotificationType ] type The type of notifications to look for.
  240. *
  241. * @return [ Void ]
  242. */
  243. - (void) ids:(CDVInvokedUrlCommand*)command
  244. byType:(APPNotificationType)type;
  245. {
  246. [self.commandDelegate runInBackground:^{
  247. NSArray* ids = [_center getNotificationIdsByType:type];
  248. CDVPluginResult* result;
  249. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  250. messageAsArray:ids];
  251. [self.commandDelegate sendPluginResult:result
  252. callbackId:command.callbackId];
  253. }];
  254. }
  255. /**
  256. * Notification by id.
  257. *
  258. * @param [ Number ] id The id of the notification to return.
  259. *
  260. * @return [ Void ]
  261. */
  262. - (void) notification:(CDVInvokedUrlCommand*)command
  263. {
  264. [self.commandDelegate runInBackground:^{
  265. NSArray* ids = command.arguments;
  266. NSArray* notifications;
  267. notifications = [_center getNotificationOptionsById:ids];
  268. CDVPluginResult* result;
  269. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  270. messageAsDictionary:[notifications firstObject]];
  271. [self.commandDelegate sendPluginResult:result
  272. callbackId:command.callbackId];
  273. }];
  274. }
  275. /**
  276. * List of notifications by id.
  277. *
  278. * @param [ Array<Number> ] ids The ids of the notifications to return.
  279. *
  280. * @return [ Void ]
  281. */
  282. - (void) notifications:(CDVInvokedUrlCommand*)command
  283. {
  284. [self notifications:command byType:NotifcationTypeAll];
  285. }
  286. /**
  287. * List of scheduled notifications by id.
  288. *
  289. * @param [ Array<Number> ] ids The ids of the notifications to return.
  290. *
  291. * @return [ Void ]
  292. */
  293. - (void) scheduledNotifications:(CDVInvokedUrlCommand*)command
  294. {
  295. [self notifications:command byType:NotifcationTypeScheduled];
  296. }
  297. /**
  298. * List of triggered notifications by id.
  299. *
  300. * @param [ Array<Number> ] ids The ids of the notifications to return.
  301. *
  302. * @return [ Void ]
  303. */
  304. - (void) triggeredNotifications:(CDVInvokedUrlCommand *)command
  305. {
  306. [self notifications:command byType:NotifcationTypeTriggered];
  307. }
  308. /**
  309. * List of notifications by type or id.
  310. *
  311. * @param [ APPNotificationType ] type The type of notifications to look for.
  312. *
  313. * @return [ Void ]
  314. */
  315. - (void) notifications:(CDVInvokedUrlCommand*)command
  316. byType:(APPNotificationType)type;
  317. {
  318. [self.commandDelegate runInBackground:^{
  319. NSArray* ids = command.arguments;
  320. NSArray* notifications;
  321. if (ids.count > 0) {
  322. notifications = [_center getNotificationOptionsById:ids];
  323. }
  324. else {
  325. notifications = [_center getNotificationOptionsByType:type];
  326. }
  327. CDVPluginResult* result;
  328. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  329. messageAsArray:notifications];
  330. [self.commandDelegate sendPluginResult:result
  331. callbackId:command.callbackId];
  332. }];
  333. }
  334. /**
  335. * Check for permission to show notifications.
  336. *
  337. * @return [ Void ]
  338. */
  339. - (void) check:(CDVInvokedUrlCommand*)command
  340. {
  341. [_center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings* settings) {
  342. BOOL authorized = settings.authorizationStatus == UNAuthorizationStatusAuthorized;
  343. BOOL enabled = settings.notificationCenterSetting == UNNotificationSettingEnabled;
  344. BOOL permitted = authorized && enabled;
  345. CDVPluginResult* result;
  346. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  347. messageAsBool:permitted];
  348. [self.commandDelegate sendPluginResult:result
  349. callbackId:command.callbackId];
  350. }];
  351. }
  352. /**
  353. * Request for permission to show notifcations.
  354. *
  355. * @return [ Void ]
  356. */
  357. - (void) request:(CDVInvokedUrlCommand*)command
  358. {
  359. UNAuthorizationOptions options =
  360. (UNAuthorizationOptionBadge | UNAuthorizationOptionSound | UNAuthorizationOptionAlert);
  361. [_center requestAuthorizationWithOptions:options completionHandler:^(BOOL granted, NSError* e) {
  362. [self check:command];
  363. }];
  364. }
  365. /**
  366. * Register/update an action group.
  367. *
  368. * @return [ Void ]
  369. */
  370. - (void) actions:(CDVInvokedUrlCommand *)command
  371. {
  372. [self.commandDelegate runInBackground:^{
  373. NSDictionary* options = command.arguments[0];
  374. APPNotificationContent* notification;
  375. notification = [[APPNotificationContent alloc]
  376. initWithOptions:options];
  377. [_center addNotificationCategory:notification.category];
  378. [self execCallback:command];
  379. }];
  380. }
  381. #pragma mark -
  382. #pragma mark Private
  383. /**
  384. * Schedule the local notification.
  385. *
  386. * @param [ APPNotificationContent* ] notification The notification to schedule.
  387. *
  388. * @return [ Void ]
  389. */
  390. - (void) scheduleNotification:(APPNotificationContent*)notification
  391. {
  392. __weak APPLocalNotification* weakSelf = self;
  393. UNNotificationRequest* request = notification.request;
  394. NSString* event = [notification.request wasUpdated] ? @"update" : @"add";
  395. [_center addNotificationCategory:notification.category];
  396. [_center addNotificationRequest:request withCompletionHandler:^(NSError* e) {
  397. __strong APPLocalNotification* strongSelf = weakSelf;
  398. [strongSelf fireEvent:event notification:request];
  399. }];
  400. }
  401. /**
  402. * Update the local notification.
  403. *
  404. * @param [ UNNotificationRequest* ] notification The notification to update.
  405. * @param [ NSDictionary* ] options The options to update.
  406. *
  407. * @return [ Void ]
  408. */
  409. - (void) updateNotification:(UNNotificationRequest*)notification
  410. withOptions:(NSDictionary*)newOptions
  411. {
  412. NSMutableDictionary* options = [notification.content.userInfo mutableCopy];
  413. [options addEntriesFromDictionary:newOptions];
  414. [options setObject:[NSDate date] forKey:@"updatedAt"];
  415. APPNotificationContent*
  416. newNotification = [[APPNotificationContent alloc] initWithOptions:options];
  417. [self scheduleNotification:newNotification];
  418. }
  419. #pragma mark -
  420. #pragma mark UNUserNotificationCenterDelegate
  421. /**
  422. * Called when a notification is delivered to the app while being in foreground.
  423. */
  424. - (void) userNotificationCenter:(UNUserNotificationCenter *)center
  425. willPresentNotification:(UNNotification *)notification
  426. withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
  427. {
  428. if (![notification.request wasUpdated]) {
  429. [self fireEvent:@"trigger" notification:notification.request];
  430. }
  431. if (notification.request.options.silent) {
  432. completionHandler(UNNotificationPresentationOptionNone);
  433. } else {
  434. completionHandler(UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionSound|UNNotificationPresentationOptionAlert);
  435. }
  436. }
  437. /**
  438. * Called to let your app know which action was selected by the user for a given
  439. * notification.
  440. */
  441. - (void) userNotificationCenter:(UNUserNotificationCenter *)center
  442. didReceiveNotificationResponse:(UNNotificationResponse *)response
  443. withCompletionHandler:(void (^)())completionHandler
  444. {
  445. UNNotificationRequest* notification = response.notification.request;
  446. NSMutableDictionary* data = [[NSMutableDictionary alloc] init];
  447. NSString* action = response.actionIdentifier;
  448. NSString* event = action;
  449. completionHandler();
  450. if ([action isEqualToString:UNNotificationDefaultActionIdentifier]) {
  451. event = @"click";
  452. } else
  453. if ([action isEqualToString:UNNotificationDismissActionIdentifier]) {
  454. event = @"clear";
  455. }
  456. if (!deviceready && [event isEqualToString:@"click"]) {
  457. _launchDetails = @[notification.options.id, event];
  458. }
  459. if (![event isEqualToString:@"clear"]) {
  460. [self fireEvent:@"clear" notification:notification];
  461. }
  462. if ([response isKindOfClass:UNTextInputNotificationResponse.class]) {
  463. [data setObject:((UNTextInputNotificationResponse*) response).userText
  464. forKey:@"text"];
  465. }
  466. [self fireEvent:event notification:notification data:data];
  467. }
  468. #pragma mark -
  469. #pragma mark Life Cycle
  470. /**
  471. * Registers obervers after plugin was initialized.
  472. */
  473. - (void) pluginInitialize
  474. {
  475. eventQueue = [[NSMutableArray alloc] init];
  476. _center = [UNUserNotificationCenter currentNotificationCenter];
  477. _center.delegate = self;
  478. [_center registerGeneralNotificationCategory];
  479. [self monitorAppStateChanges];
  480. }
  481. /**
  482. * Monitor changes of the app state and update the _isActive flag.
  483. */
  484. - (void) monitorAppStateChanges
  485. {
  486. NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
  487. [center addObserverForName:UIApplicationDidBecomeActiveNotification
  488. object:NULL queue:[NSOperationQueue mainQueue]
  489. usingBlock:^(NSNotification *e) { isActive = YES; }];
  490. [center addObserverForName:UIApplicationDidEnterBackgroundNotification
  491. object:NULL queue:[NSOperationQueue mainQueue]
  492. usingBlock:^(NSNotification *e) { isActive = NO; }];
  493. }
  494. #pragma mark -
  495. #pragma mark Helper
  496. /**
  497. * Removes the badge number from the app icon.
  498. */
  499. - (void) clearApplicationIconBadgeNumber
  500. {
  501. dispatch_async(dispatch_get_main_queue(), ^{
  502. [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
  503. });
  504. }
  505. /**
  506. * Simply invokes the callback without any parameter.
  507. */
  508. - (void) execCallback:(CDVInvokedUrlCommand*)command
  509. {
  510. CDVPluginResult *result = [CDVPluginResult
  511. resultWithStatus:CDVCommandStatus_OK];
  512. [self.commandDelegate sendPluginResult:result
  513. callbackId:command.callbackId];
  514. }
  515. /**
  516. * Fire general event.
  517. *
  518. * @param [ NSString* ] event The name of the event to fire.
  519. *
  520. * @return [ Void ]
  521. */
  522. - (void) fireEvent:(NSString*)event
  523. {
  524. NSMutableDictionary* data = [[NSMutableDictionary alloc] init];
  525. [self fireEvent:event notification:NULL data:data];
  526. }
  527. /**
  528. * Fire event for about a local notification.
  529. *
  530. * @param [ NSString* ] event The name of the event to fire.
  531. * @param [ APPNotificationRequest* ] notification The local notification.
  532. *
  533. * @return [ Void ]
  534. */
  535. - (void) fireEvent:(NSString*)event
  536. notification:(UNNotificationRequest*)notitification
  537. {
  538. NSMutableDictionary* data = [[NSMutableDictionary alloc] init];
  539. [self fireEvent:event notification:notitification data:data];
  540. }
  541. /**
  542. * Fire event for about a local notification.
  543. *
  544. * @param [ NSString* ] event The name of the event to fire.
  545. * @param [ APPNotificationRequest* ] notification The local notification.
  546. * @param [ NSMutableDictionary* ] data Event object with additional data.
  547. *
  548. * @return [ Void ]
  549. */
  550. - (void) fireEvent:(NSString*)event
  551. notification:(UNNotificationRequest*)request
  552. data:(NSMutableDictionary*)data
  553. {
  554. NSString *js, *params, *notiAsJSON, *dataAsJSON;
  555. NSData* dataAsData;
  556. [data setObject:event forKey:@"event"];
  557. [data setObject:@(isActive) forKey:@"foreground"];
  558. [data setObject:@(!deviceready) forKey:@"queued"];
  559. if (request) {
  560. notiAsJSON = [request encodeToJSON];
  561. [data setObject:request.options.id forKey:@"notification"];
  562. }
  563. dataAsData =
  564. [NSJSONSerialization dataWithJSONObject:data options:0 error:NULL];
  565. dataAsJSON =
  566. [[NSString alloc] initWithData:dataAsData encoding:NSUTF8StringEncoding];
  567. if (request) {
  568. params = [NSString stringWithFormat:@"%@,%@", notiAsJSON, dataAsJSON];
  569. } else {
  570. params = [NSString stringWithFormat:@"%@", dataAsJSON];
  571. }
  572. js = [NSString stringWithFormat:
  573. @"cordova.plugins.notification.local.core.fireEvent('%@', %@)",
  574. event, params];
  575. if (deviceready) {
  576. [self.commandDelegate evalJs:js];
  577. } else {
  578. [self.eventQueue addObject:js];
  579. }
  580. }
  581. @end