APPLocalNotification.m 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. /*
  2. Copyright 2013-2014 appPlant UG
  3. Licensed to the Apache Software Foundation (ASF) under one
  4. or more contributor license agreements. See the NOTICE file
  5. distributed with this work for additional information
  6. regarding copyright ownership. The ASF licenses this file
  7. to you under the Apache License, Version 2.0 (the
  8. "License"); you may not use this file except in compliance
  9. with the License. You may obtain a copy of the License at
  10. http://www.apache.org/licenses/LICENSE-2.0
  11. Unless required by applicable law or agreed to in writing,
  12. software distributed under the License is distributed on an
  13. "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  14. KIND, either express or implied. See the License for the
  15. specific language governing permissions and limitations
  16. under the License.
  17. */
  18. #import "APPLocalNotification.h"
  19. #import "APPLocalNotificationOptions.h"
  20. #import "AppDelegate+APPLocalNotification.h"
  21. #import "UIApplication+APPLocalNotification.h"
  22. #import "UILocalNotification+APPLocalNotification.h"
  23. #import <Availability.h>
  24. @interface APPLocalNotification ()
  25. // Retrieves the application state
  26. @property (readonly, getter=applicationState) NSString* applicationState;
  27. // All events will be queued until deviceready has been fired
  28. @property (readwrite, assign) BOOL deviceready;
  29. // Event queue
  30. @property (readonly, nonatomic, retain) NSMutableArray* eventQueue;
  31. // Needed when calling `registerPermission`
  32. @property (nonatomic, retain) CDVInvokedUrlCommand* command;
  33. @end
  34. @implementation APPLocalNotification
  35. @synthesize deviceready, eventQueue;
  36. #pragma mark -
  37. #pragma mark Interface
  38. /**
  39. * Execute all queued events.
  40. */
  41. - (void) deviceready:(CDVInvokedUrlCommand*)command
  42. {
  43. deviceready = YES;
  44. for (NSString* js in eventQueue) {
  45. [self.commandDelegate evalJs:js];
  46. }
  47. [eventQueue removeAllObjects];
  48. }
  49. /**
  50. * Schedule a set of notifications.
  51. *
  52. * @param properties
  53. * A dict of properties for each notification
  54. */
  55. - (void) add:(CDVInvokedUrlCommand*)command
  56. {
  57. NSArray* notifications = command.arguments;
  58. [self.commandDelegate runInBackground:^{
  59. for (NSDictionary* options in notifications) {
  60. UILocalNotification* notification;
  61. notification = [[UILocalNotification alloc]
  62. initWithOptions:options];
  63. [self scheduleLocalNotification:[notification copy]];
  64. [self fireEvent:@"add" localNotification:notification];
  65. if (notifications.count > 1) {
  66. [NSThread sleepForTimeInterval:0.01];
  67. }
  68. }
  69. [self execCallback:command];
  70. }];
  71. }
  72. /**
  73. * Update a set of notifications.
  74. *
  75. * @param properties
  76. * A dict of properties for each notification
  77. */
  78. - (void) update:(CDVInvokedUrlCommand*)command
  79. {
  80. NSArray* notifications = command.arguments;
  81. [self.commandDelegate runInBackground:^{
  82. for (NSDictionary* options in notifications) {
  83. NSString* id = [options objectForKey:@"id"];
  84. UILocalNotification* notification;
  85. notification = [[UIApplication sharedApplication]
  86. scheduledLocalNotificationWithId:id];
  87. if (!notification)
  88. continue;
  89. [self updateLocalNotification:[notification copy]
  90. withOptions:options];
  91. if (notifications.count > 1) {
  92. [NSThread sleepForTimeInterval:0.01];
  93. }
  94. }
  95. [self execCallback:command];
  96. }];
  97. }
  98. /**
  99. * Cancel a set of notifications.
  100. *
  101. * @param ids
  102. * The IDs of the notifications
  103. */
  104. - (void) cancel:(CDVInvokedUrlCommand*)command
  105. {
  106. [self.commandDelegate runInBackground:^{
  107. for (NSString* id in command.arguments) {
  108. UILocalNotification* notification;
  109. notification = [[UIApplication sharedApplication]
  110. scheduledLocalNotificationWithId:id];
  111. if (!notification)
  112. continue;
  113. [self cancelLocalNotification:notification];
  114. [self fireEvent:@"cancel" localNotification:notification];
  115. }
  116. [self execCallback:command];
  117. }];
  118. }
  119. /**
  120. * Cancel all currently scheduled notifications.
  121. */
  122. - (void) cancelAll:(CDVInvokedUrlCommand*)command
  123. {
  124. [self.commandDelegate runInBackground:^{
  125. [self cancelAllLocalNotifications];
  126. [self fireEvent:@"cancelall"];
  127. [self execCallback:command];
  128. }];
  129. }
  130. /**
  131. * If a notification by ID is scheduled.
  132. *
  133. * @param id
  134. * The ID of the notification
  135. */
  136. - (void) isScheduled:(CDVInvokedUrlCommand*)command
  137. {
  138. [self.commandDelegate runInBackground:^{
  139. NSString* id = [[command arguments]
  140. objectAtIndex:0];
  141. CDVPluginResult* result;
  142. UILocalNotification* notification;
  143. notification = [[UIApplication sharedApplication]
  144. scheduledLocalNotificationWithId:id];
  145. bool isScheduled = notification != NULL;
  146. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  147. messageAsBool:isScheduled];
  148. [self.commandDelegate sendPluginResult:result
  149. callbackId:command.callbackId];
  150. }];
  151. }
  152. /**
  153. * Check if a notification with an ID was triggered.
  154. *
  155. * @param id
  156. * The ID of the notification
  157. */
  158. - (void) isTriggered:(CDVInvokedUrlCommand*)command
  159. {
  160. [self.commandDelegate runInBackground:^{
  161. NSString* id = [[command arguments]
  162. objectAtIndex:0];
  163. CDVPluginResult* result;
  164. UILocalNotification* notification;
  165. notification = [[UIApplication sharedApplication]
  166. triggeredLocalNotificationWithId:id];
  167. bool isTriggered = notification != NULL;
  168. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  169. messageAsBool:isTriggered];
  170. [self.commandDelegate sendPluginResult:result
  171. callbackId:command.callbackId];
  172. }];
  173. }
  174. /**
  175. * List all ids from all pending notifications.
  176. */
  177. - (void) getScheduledIds:(CDVInvokedUrlCommand*)command
  178. {
  179. [self.commandDelegate runInBackground:^{
  180. CDVPluginResult* result;
  181. NSArray* scheduledIds;
  182. scheduledIds = [[UIApplication sharedApplication]
  183. scheduledLocalNotificationIds];
  184. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  185. messageAsArray:scheduledIds];
  186. [self.commandDelegate sendPluginResult:result
  187. callbackId:command.callbackId];
  188. }];
  189. }
  190. /**
  191. * List all ids from all triggered notifications.
  192. */
  193. - (void) getTriggeredIds:(CDVInvokedUrlCommand*)command
  194. {
  195. [self.commandDelegate runInBackground:^{
  196. CDVPluginResult* result;
  197. NSArray* triggeredIds;
  198. triggeredIds = [[UIApplication sharedApplication]
  199. triggeredLocalNotificationIds];
  200. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  201. messageAsArray:triggeredIds];
  202. [self.commandDelegate sendPluginResult:result
  203. callbackId:command.callbackId];
  204. }];
  205. }
  206. /**
  207. * List all properties for given scheduled notifications.
  208. *
  209. * @param ids
  210. * The IDs of the notifications
  211. */
  212. - (void) getScheduled:(CDVInvokedUrlCommand*)command
  213. {
  214. [self.commandDelegate runInBackground:^{
  215. NSArray* ids = command.arguments;
  216. NSArray* notifications;
  217. CDVPluginResult* result;
  218. if (ids.count == 0) {
  219. notifications = [[UIApplication sharedApplication]
  220. scheduledLocalNotificationOptions];
  221. } else {
  222. notifications = [[UIApplication sharedApplication]
  223. scheduledLocalNotificationOptions:ids];
  224. }
  225. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  226. messageAsArray:notifications];
  227. [self.commandDelegate sendPluginResult:result
  228. callbackId:command.callbackId];
  229. }];
  230. }
  231. /**
  232. * List all properties for given triggered notifications.
  233. *
  234. * @param ids
  235. * The IDs of the notifications
  236. */
  237. - (void) getTriggered:(CDVInvokedUrlCommand *)command
  238. {
  239. [self.commandDelegate runInBackground:^{
  240. NSArray* ids = command.arguments;
  241. NSArray* notifications;
  242. CDVPluginResult* result;
  243. if (ids.count == 0) {
  244. notifications = [[UIApplication sharedApplication]
  245. triggeredLocalNotificationOptions];
  246. } else {
  247. notifications = [[UIApplication sharedApplication]
  248. triggeredLocalNotificationOptions:ids];
  249. }
  250. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  251. messageAsArray:notifications];
  252. [self.commandDelegate sendPluginResult:result
  253. callbackId:command.callbackId];
  254. }];
  255. }
  256. /**
  257. * Inform if the app has the permission to show
  258. * badges and local notifications.
  259. */
  260. - (void) hasPermission:(CDVInvokedUrlCommand*)command
  261. {
  262. [self.commandDelegate runInBackground:^{
  263. CDVPluginResult* result;
  264. BOOL hasPermission;
  265. hasPermission = [[UIApplication sharedApplication]
  266. hasPermissionToScheduleLocalNotifications];
  267. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  268. messageAsBool:hasPermission];
  269. [self.commandDelegate sendPluginResult:result
  270. callbackId:command.callbackId];
  271. }];
  272. }
  273. /**
  274. * Ask for permission to show badges.
  275. */
  276. - (void) registerPermission:(CDVInvokedUrlCommand*)command
  277. {
  278. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
  279. _command = command;
  280. [self.commandDelegate runInBackground:^{
  281. [[UIApplication sharedApplication]
  282. registerPermissionToScheduleLocalNotifications];
  283. }];
  284. #else
  285. [self hasPermission:command];
  286. #endif
  287. }
  288. #pragma mark -
  289. #pragma mark Core Logic
  290. /**
  291. * Schedule the local notification.
  292. */
  293. - (void) scheduleLocalNotification:(UILocalNotification*)notification
  294. {
  295. [self cancelForerunnerLocalNotification:notification];
  296. [[UIApplication sharedApplication]
  297. scheduleLocalNotification:notification];
  298. }
  299. /**
  300. * Update the local notification.
  301. */
  302. - (void) updateLocalNotification:(UILocalNotification*)notification
  303. withOptions:(NSDictionary*)newOptions
  304. {
  305. NSMutableDictionary* options = [notification.userInfo mutableCopy];
  306. [options addEntriesFromDictionary:newOptions];
  307. [options setObject:[NSDate date] forKey:@"updatedAt"];
  308. notification = [[UILocalNotification alloc]
  309. initWithOptions:options];
  310. [self scheduleLocalNotification:notification];
  311. }
  312. /**
  313. * Cancel the local notification.
  314. */
  315. - (void) cancelLocalNotification:(UILocalNotification*)notification
  316. {
  317. [[UIApplication sharedApplication]
  318. cancelLocalNotification:notification];
  319. [UIApplication sharedApplication]
  320. .applicationIconBadgeNumber -= 1;
  321. }
  322. /**
  323. * Cancel all currently scheduled notifications.
  324. */
  325. - (void) cancelAllLocalNotifications
  326. {
  327. [[UIApplication sharedApplication]
  328. cancelAllLocalNotifications];
  329. [[UIApplication sharedApplication]
  330. setApplicationIconBadgeNumber:0];
  331. }
  332. /**
  333. * Cancel a maybe given forerunner with the same ID.
  334. */
  335. - (void) cancelForerunnerLocalNotification:(UILocalNotification*)notification
  336. {
  337. NSString* id = notification.options.id;
  338. UILocalNotification* forerunner;
  339. forerunner = [[UIApplication sharedApplication]
  340. scheduledLocalNotificationWithId:id];
  341. if (!forerunner)
  342. return;
  343. [self cancelLocalNotification:forerunner];
  344. }
  345. /**
  346. * Cancels all local notification with are older then
  347. * a specific amount of seconds
  348. */
  349. - (void) cancelAllNotificationsWhichAreOlderThen:(float)seconds
  350. {
  351. NSArray* notifications;
  352. notifications = [[UIApplication sharedApplication]
  353. scheduledLocalNotifications];
  354. for (UILocalNotification* notification in notifications)
  355. {
  356. if (notification && [notification isRepeating]
  357. && notification.timeIntervalSinceFireDate > seconds)
  358. {
  359. [self cancelLocalNotification:notification];
  360. [self fireEvent:@"cancel" localNotification:notification];
  361. }
  362. }
  363. }
  364. #pragma mark -
  365. #pragma mark Delegates
  366. /**
  367. * Calls the cancel or trigger event after a local notification was received.
  368. * Cancels the local notification if autoCancel was set to true.
  369. */
  370. - (void) didReceiveLocalNotification:(NSNotification*)localNotification
  371. {
  372. UILocalNotification* notification = [localNotification object];
  373. if ([notification wasUpdated])
  374. return;
  375. BOOL autoCancel = notification.options.autoCancel;
  376. NSTimeInterval timeInterval = [notification timeIntervalSinceFireDate];
  377. NSString* event = (timeInterval <= 1 && deviceready) ? @"trigger" : @"click";
  378. if ([event isEqualToString:@"click"]) {
  379. [UIApplication sharedApplication]
  380. .applicationIconBadgeNumber -= 1;
  381. }
  382. [self fireEvent:event localNotification:notification];
  383. if (autoCancel && [event isEqualToString:@"click"]) {
  384. [self cancelLocalNotification:notification];
  385. [self fireEvent:@"cancel" localNotification:notification];
  386. }
  387. }
  388. /**
  389. * Called when app has started
  390. * (by clicking on a local notification).
  391. */
  392. - (void) didFinishLaunchingWithOptions:(NSNotification*)notification
  393. {
  394. NSDictionary* launchOptions = [notification userInfo];
  395. UILocalNotification* localNotification;
  396. localNotification = [launchOptions objectForKey:
  397. UIApplicationLaunchOptionsLocalNotificationKey];
  398. if (localNotification) {
  399. [self didReceiveLocalNotification:
  400. [NSNotification notificationWithName:CDVLocalNotification
  401. object:localNotification]];
  402. }
  403. }
  404. /**
  405. * Called on otification settings registration is completed.
  406. */
  407. - (void) didRegisterUserNotificationSettings:(UIUserNotificationSettings*)settings
  408. {
  409. if (_command)
  410. {
  411. [self hasPermission:_command];
  412. _command = NULL;
  413. }
  414. }
  415. #pragma mark -
  416. #pragma mark Life Cycle
  417. /**
  418. * Registers obervers after plugin was initialized.
  419. */
  420. - (void) pluginInitialize
  421. {
  422. NSNotificationCenter* center = [NSNotificationCenter
  423. defaultCenter];
  424. eventQueue = [[NSMutableArray alloc] init];
  425. [center addObserver:self
  426. selector:@selector(didReceiveLocalNotification:)
  427. name:CDVLocalNotification
  428. object:nil];
  429. [center addObserver:self
  430. selector:@selector(didFinishLaunchingWithOptions:)
  431. name:UIApplicationDidFinishLaunchingNotification
  432. object:nil];
  433. [center addObserver:self
  434. selector:@selector(didRegisterUserNotificationSettings:)
  435. name:UIApplicationRegisterUserNotificationSettings
  436. object:nil];
  437. }
  438. /**
  439. * Clears all single repeating notifications which are older then 5 days
  440. * before the app terminates.
  441. */
  442. - (void) onAppTerminate
  443. {
  444. [self cancelAllNotificationsWhichAreOlderThen:432000];
  445. }
  446. #pragma mark -
  447. #pragma mark Helper
  448. /**
  449. * Retrieves the application state
  450. *
  451. * @return
  452. * Either "background" or "foreground"
  453. */
  454. - (NSString*) applicationState
  455. {
  456. UIApplicationState state = [[UIApplication sharedApplication]
  457. applicationState];
  458. bool isActive = state == UIApplicationStateActive;
  459. return isActive ? @"foreground" : @"background";
  460. }
  461. /**
  462. * Simply invokes the callback without any parameter.
  463. */
  464. - (void) execCallback:(CDVInvokedUrlCommand*)command
  465. {
  466. CDVPluginResult *result = [CDVPluginResult
  467. resultWithStatus:CDVCommandStatus_OK];
  468. [self.commandDelegate sendPluginResult:result
  469. callbackId:command.callbackId];
  470. }
  471. /**
  472. * Fire general event.
  473. */
  474. - (void) fireEvent:(NSString*)event
  475. {
  476. [self fireEvent:event localNotification:NULL];
  477. }
  478. /**
  479. * Fire event for local notification.
  480. */
  481. - (void) fireEvent:(NSString*)event localNotification:(UILocalNotification*)notification
  482. {
  483. NSString* js;
  484. NSString* params = [NSString stringWithFormat:
  485. @"\"%@\"", self.applicationState];
  486. if (notification) {
  487. NSString* id = notification.options.id;
  488. NSString* json = notification.options.json;
  489. NSString* args = [notification encodeToJSON];
  490. json = [json stringByReplacingOccurrencesOfString:@"'"
  491. withString:@"\\\\\\'"];
  492. params = [NSString stringWithFormat:
  493. @"\"%@\",\"%@\",\\'%@\\',JSON.parse(\\'%@\\')",
  494. id, self.applicationState, json, args];
  495. }
  496. js = [NSString stringWithFormat:
  497. @"setTimeout('cordova.plugins.notification.local.on%@(%@)',0)",
  498. event, params];
  499. if (deviceready) {
  500. [self.commandDelegate evalJs:js];
  501. } else {
  502. [self.eventQueue addObject:js];
  503. }
  504. }
  505. @end