APPLocalNotification.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737
  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 "AppDelegate+APPRegisterUserNotificationSettings.h"
  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. {
  522. [self hasPermission:_command];
  523. _command = NULL;
  524. }
  525. }
  526. #pragma mark -
  527. #pragma mark Life Cycle
  528. /**
  529. * Registers obervers after plugin was initialized.
  530. */
  531. - (void) pluginInitialize
  532. {
  533. NSNotificationCenter* center = [NSNotificationCenter
  534. defaultCenter];
  535. eventQueue = [[NSMutableArray alloc] init];
  536. [center addObserver:self
  537. selector:@selector(didReceiveLocalNotification:)
  538. name:CDVLocalNotification
  539. object:nil];
  540. [center addObserver:self
  541. selector:@selector(didFinishLaunchingWithOptions:)
  542. name:UIApplicationDidFinishLaunchingNotification
  543. object:nil];
  544. [center addObserver:self
  545. selector:@selector(didRegisterUserNotificationSettings:)
  546. name:UIApplicationRegisterUserNotificationSettings
  547. object:nil];
  548. }
  549. /**
  550. * Clears all single repeating notifications which are older then 5 days
  551. * before the app terminates.
  552. */
  553. - (void) onAppTerminate
  554. {
  555. [self cancelAllNotificationsWhichAreOlderThen:432000];
  556. }
  557. #pragma mark -
  558. #pragma mark Helper
  559. /**
  560. * Retrieves the application state
  561. *
  562. * @return
  563. * Either "background" or "foreground"
  564. */
  565. - (NSString*) applicationState
  566. {
  567. UIApplicationState state = [self.app applicationState];
  568. bool isActive = state == UIApplicationStateActive;
  569. return isActive ? @"foreground" : @"background";
  570. }
  571. /**
  572. * Simply invokes the callback without any parameter.
  573. */
  574. - (void) execCallback:(CDVInvokedUrlCommand*)command
  575. {
  576. CDVPluginResult *result = [CDVPluginResult
  577. resultWithStatus:CDVCommandStatus_OK];
  578. [self.commandDelegate sendPluginResult:result
  579. callbackId:command.callbackId];
  580. }
  581. /**
  582. * Short hand for shared application instance.
  583. */
  584. - (UIApplication*) app
  585. {
  586. return [UIApplication sharedApplication];
  587. }
  588. /**
  589. * Fire general event.
  590. */
  591. - (void) fireEvent:(NSString*)event
  592. {
  593. [self fireEvent:event notification:NULL];
  594. }
  595. /**
  596. * Fire event for local notification.
  597. */
  598. - (void) fireEvent:(NSString*)event notification:(UILocalNotification*)notification
  599. {
  600. NSString* js;
  601. NSString* params = [NSString stringWithFormat:
  602. @"\"%@\"", self.applicationState];
  603. if (notification) {
  604. NSString* args = [notification encodeToJSON];
  605. params = [NSString stringWithFormat:
  606. @"%@,'%@'",
  607. args, self.applicationState];
  608. }
  609. js = [NSString stringWithFormat:
  610. @"cordova.plugins.notification.local.core.fireEvent('%@', %@)",
  611. event, params];
  612. if (deviceready) {
  613. [self.commandDelegate evalJs:js];
  614. } else {
  615. [self.eventQueue addObject:js];
  616. }
  617. }
  618. @end