APPLocalNotification.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684
  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 "AppDelegate+APPLocalNotification.h"
  26. #import "UIApplication+APPLocalNotification.h"
  27. #import "UILocalNotification+APPLocalNotification.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. NSString* 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 (NSString* 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 (NSString* 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. NSString* 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. * Property list for given local notifications.
  266. *
  267. * @param ids
  268. * The IDs of the notifications
  269. */
  270. - (void) getAll:(CDVInvokedUrlCommand*)command
  271. {
  272. [self getOptions:command byType:NotifcationTypeAll];
  273. }
  274. /**
  275. * Property list for given scheduled notifications.
  276. *
  277. * @param ids
  278. * The IDs of the notifications
  279. */
  280. - (void) getScheduled:(CDVInvokedUrlCommand*)command
  281. {
  282. [self getOptions:command byType:NotifcationTypeScheduled];
  283. }
  284. /**
  285. * Property list for given triggered notifications.
  286. *
  287. * @param ids
  288. * The IDs of the notifications
  289. */
  290. - (void) getTriggered:(CDVInvokedUrlCommand *)command
  291. {
  292. [self getOptions:command byType:NotifcationTypeTriggered];
  293. }
  294. /**
  295. * Property list for given triggered notifications.
  296. *
  297. * @param type
  298. * Notification life cycle type
  299. * @param ids
  300. * The IDs of the notifications
  301. */
  302. - (void) getOptions:(CDVInvokedUrlCommand*)command
  303. byType:(APPLocalNotificationType)type;
  304. {
  305. [self.commandDelegate runInBackground:^{
  306. NSArray* ids = command.arguments;
  307. NSArray* notifications;
  308. CDVPluginResult* result;
  309. if (type == NotifcationTypeAll && ids.count == 0) {
  310. notifications = [self.app localNotificationOptions];
  311. }
  312. else if (type == NotifcationTypeAll) {
  313. notifications = [self.app localNotificationOptionsById:ids];
  314. }
  315. else if (ids.count == 0) {
  316. notifications = [self.app localNotificationOptionsByType:type];
  317. }
  318. else {
  319. notifications = [self.app localNotificationOptionsByType:type
  320. andId:ids];
  321. }
  322. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  323. messageAsArray:notifications];
  324. [self.commandDelegate sendPluginResult:result
  325. callbackId:command.callbackId];
  326. }];
  327. }
  328. /**
  329. * Inform if the app has the permission to show
  330. * badges and local notifications.
  331. */
  332. - (void) hasPermission:(CDVInvokedUrlCommand*)command
  333. {
  334. [self.commandDelegate runInBackground:^{
  335. CDVPluginResult* result;
  336. BOOL hasPermission;
  337. hasPermission = [self.app hasPermissionToScheduleLocalNotifications];
  338. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  339. messageAsBool:hasPermission];
  340. [self.commandDelegate sendPluginResult:result
  341. callbackId:command.callbackId];
  342. }];
  343. }
  344. /**
  345. * Ask for permission to show badges.
  346. */
  347. - (void) registerPermission:(CDVInvokedUrlCommand*)command
  348. {
  349. if ([[UIApplication sharedApplication]
  350. respondsToSelector:@selector(registerUserNotificationSettings:)])
  351. {
  352. _command = command;
  353. [self.commandDelegate runInBackground:^{
  354. [self.app registerPermissionToScheduleLocalNotifications];
  355. }];
  356. } else {
  357. [self hasPermission:command];
  358. }
  359. }
  360. #pragma mark -
  361. #pragma mark Core Logic
  362. /**
  363. * Schedule the local notification.
  364. */
  365. - (void) scheduleLocalNotification:(UILocalNotification*)notification
  366. {
  367. [self cancelForerunnerLocalNotification:notification];
  368. [self.app scheduleLocalNotification:notification];
  369. }
  370. /**
  371. * Update the local notification.
  372. */
  373. - (void) updateLocalNotification:(UILocalNotification*)notification
  374. withOptions:(NSDictionary*)newOptions
  375. {
  376. NSMutableDictionary* options = [notification.userInfo mutableCopy];
  377. [options addEntriesFromDictionary:newOptions];
  378. [options setObject:[NSDate date] forKey:@"updatedAt"];
  379. notification = [[UILocalNotification alloc]
  380. initWithOptions:options];
  381. [self scheduleLocalNotification:notification];
  382. }
  383. /**
  384. * Cancel all local notifications.
  385. */
  386. - (void) cancelAllLocalNotifications
  387. {
  388. [self.app cancelAllLocalNotifications];
  389. [self.app setApplicationIconBadgeNumber:0];
  390. }
  391. /**
  392. * Clear all local notifications.
  393. */
  394. - (void) clearAllLocalNotifications
  395. {
  396. [self.app clearAllLocalNotifications];
  397. [self.app setApplicationIconBadgeNumber:0];
  398. }
  399. /**
  400. * Cancel a maybe given forerunner with the same ID.
  401. */
  402. - (void) cancelForerunnerLocalNotification:(UILocalNotification*)notification
  403. {
  404. NSString* id = notification.options.id;
  405. UILocalNotification* forerunner;
  406. forerunner = [self.app localNotificationWithId:id];
  407. if (!forerunner)
  408. return;
  409. [self.app cancelLocalNotification:forerunner];
  410. }
  411. /**
  412. * Cancels all non-repeating local notification older then
  413. * a specific amount of seconds
  414. */
  415. - (void) cancelAllNotificationsWhichAreOlderThen:(float)seconds
  416. {
  417. NSArray* notifications;
  418. notifications = [self.app localNotifications];
  419. for (UILocalNotification* notification in notifications)
  420. {
  421. if (![notification isRepeating]
  422. && notification.timeIntervalSinceFireDate > seconds)
  423. {
  424. [self.app cancelLocalNotification:notification];
  425. [self fireEvent:@"cancel" notification:notification];
  426. }
  427. }
  428. }
  429. #pragma mark -
  430. #pragma mark Delegates
  431. /**
  432. * Calls the cancel or trigger event after a local notification was received.
  433. * Cancels the local notification if autoCancel was set to true.
  434. */
  435. - (void) didReceiveLocalNotification:(NSNotification*)localNotification
  436. {
  437. UILocalNotification* notification = [localNotification object];
  438. if ([notification wasUpdated])
  439. return;
  440. NSTimeInterval timeInterval = [notification timeIntervalSinceLastTrigger];
  441. NSString* event = (timeInterval <= 1 && deviceready) ? @"trigger" : @"click";
  442. [self fireEvent:event notification:notification];
  443. if (![event isEqualToString:@"click"])
  444. return;
  445. if ([notification isRepeating]) {
  446. [self fireEvent:@"clear" notification:notification];
  447. } else {
  448. [self.app cancelLocalNotification:notification];
  449. [self fireEvent:@"cancel" notification:notification];
  450. }
  451. }
  452. /**
  453. * Called when app has started
  454. * (by clicking on a local notification).
  455. */
  456. - (void) didFinishLaunchingWithOptions:(NSNotification*)notification
  457. {
  458. NSDictionary* launchOptions = [notification userInfo];
  459. UILocalNotification* localNotification;
  460. localNotification = [launchOptions objectForKey:
  461. UIApplicationLaunchOptionsLocalNotificationKey];
  462. if (localNotification) {
  463. [self didReceiveLocalNotification:
  464. [NSNotification notificationWithName:CDVLocalNotification
  465. object:localNotification]];
  466. }
  467. }
  468. /**
  469. * Called on otification settings registration is completed.
  470. */
  471. - (void) didRegisterUserNotificationSettings:(UIUserNotificationSettings*)settings
  472. {
  473. if (_command)
  474. {
  475. [self hasPermission:_command];
  476. _command = NULL;
  477. }
  478. }
  479. #pragma mark -
  480. #pragma mark Life Cycle
  481. /**
  482. * Registers obervers after plugin was initialized.
  483. */
  484. - (void) pluginInitialize
  485. {
  486. NSNotificationCenter* center = [NSNotificationCenter
  487. defaultCenter];
  488. eventQueue = [[NSMutableArray alloc] init];
  489. [center addObserver:self
  490. selector:@selector(didReceiveLocalNotification:)
  491. name:CDVLocalNotification
  492. object:nil];
  493. [center addObserver:self
  494. selector:@selector(didFinishLaunchingWithOptions:)
  495. name:UIApplicationDidFinishLaunchingNotification
  496. object:nil];
  497. [center addObserver:self
  498. selector:@selector(didRegisterUserNotificationSettings:)
  499. name:UIApplicationRegisterUserNotificationSettings
  500. object:nil];
  501. }
  502. /**
  503. * Clears all single repeating notifications which are older then 5 days
  504. * before the app terminates.
  505. */
  506. - (void) onAppTerminate
  507. {
  508. [self cancelAllNotificationsWhichAreOlderThen:432000];
  509. }
  510. #pragma mark -
  511. #pragma mark Helper
  512. /**
  513. * Retrieves the application state
  514. *
  515. * @return
  516. * Either "background" or "foreground"
  517. */
  518. - (NSString*) applicationState
  519. {
  520. UIApplicationState state = [self.app applicationState];
  521. bool isActive = state == UIApplicationStateActive;
  522. return isActive ? @"foreground" : @"background";
  523. }
  524. /**
  525. * Simply invokes the callback without any parameter.
  526. */
  527. - (void) execCallback:(CDVInvokedUrlCommand*)command
  528. {
  529. CDVPluginResult *result = [CDVPluginResult
  530. resultWithStatus:CDVCommandStatus_OK];
  531. [self.commandDelegate sendPluginResult:result
  532. callbackId:command.callbackId];
  533. }
  534. /**
  535. * Short hand for shared application instance.
  536. */
  537. - (UIApplication*) app
  538. {
  539. return [UIApplication sharedApplication];
  540. }
  541. /**
  542. * Fire general event.
  543. */
  544. - (void) fireEvent:(NSString*)event
  545. {
  546. [self fireEvent:event notification:NULL];
  547. }
  548. /**
  549. * Fire event for local notification.
  550. */
  551. - (void) fireEvent:(NSString*)event notification:(UILocalNotification*)notification
  552. {
  553. NSString* js;
  554. NSString* params = [NSString stringWithFormat:
  555. @"\"%@\"", self.applicationState];
  556. if (notification) {
  557. NSString* args = [notification encodeToJSON];
  558. params = [NSString stringWithFormat:
  559. @"%@,'%@'",
  560. args, self.applicationState];
  561. }
  562. js = [NSString stringWithFormat:
  563. @"cordova.plugins.notification.local.fireEvent('%@', %@)",
  564. event, params];
  565. if (deviceready) {
  566. [self.commandDelegate evalJs:js];
  567. } else {
  568. [self.eventQueue addObject:js];
  569. }
  570. }
  571. @end