APPLocalNotification.m 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  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 (NSMutableDictionary* 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. * Cancel a set of notifications.
  74. *
  75. * @param ids
  76. * The IDs of the notifications
  77. */
  78. - (void) cancel:(CDVInvokedUrlCommand*)command
  79. {
  80. [self.commandDelegate runInBackground:^{
  81. for (NSString* id in command.arguments) {
  82. UILocalNotification* notification;
  83. notification = [[UIApplication sharedApplication]
  84. scheduledLocalNotificationWithId:id];
  85. if (!notification)
  86. continue;
  87. [self cancelLocalNotification:notification];
  88. [self fireEvent:@"cancel" localNotification:notification];
  89. }
  90. [self execCallback:command];
  91. }];
  92. }
  93. /**
  94. * Cancel all currently scheduled notifications.
  95. */
  96. - (void) cancelAll:(CDVInvokedUrlCommand*)command
  97. {
  98. [self.commandDelegate runInBackground:^{
  99. [self cancelAllLocalNotifications];
  100. [self fireEvent:@"cancelall"];
  101. [self execCallback:command];
  102. }];
  103. }
  104. /**
  105. * If a notification by ID is scheduled.
  106. *
  107. * @param id
  108. * The ID of the notification
  109. */
  110. - (void) isScheduled:(CDVInvokedUrlCommand*)command
  111. {
  112. [self.commandDelegate runInBackground:^{
  113. NSString* id = [[command arguments]
  114. objectAtIndex:0];
  115. CDVPluginResult* result;
  116. UILocalNotification* notification;
  117. notification = [[UIApplication sharedApplication]
  118. scheduledLocalNotificationWithId:id];
  119. bool isScheduled = notification != NULL;
  120. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  121. messageAsBool:isScheduled];
  122. [self.commandDelegate sendPluginResult:result
  123. callbackId:command.callbackId];
  124. }];
  125. }
  126. /**
  127. * Check if a notification with an ID was triggered.
  128. *
  129. * @param id
  130. * The ID of the notification
  131. */
  132. - (void) isTriggered:(CDVInvokedUrlCommand*)command
  133. {
  134. [self.commandDelegate runInBackground:^{
  135. NSString* id = [[command arguments]
  136. objectAtIndex:0];
  137. CDVPluginResult* result;
  138. UILocalNotification* notification;
  139. notification = [[UIApplication sharedApplication]
  140. triggeredLocalNotificationWithId:id];
  141. bool isTriggered = notification != NULL;
  142. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  143. messageAsBool:isTriggered];
  144. [self.commandDelegate sendPluginResult:result
  145. callbackId:command.callbackId];
  146. }];
  147. }
  148. /**
  149. * List all ids from all pending notifications.
  150. */
  151. - (void) getScheduledIds:(CDVInvokedUrlCommand*)command
  152. {
  153. [self.commandDelegate runInBackground:^{
  154. CDVPluginResult* result;
  155. NSArray* scheduledIds;
  156. scheduledIds = [[UIApplication sharedApplication]
  157. scheduledLocalNotificationIds];
  158. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  159. messageAsArray:scheduledIds];
  160. [self.commandDelegate sendPluginResult:result
  161. callbackId:command.callbackId];
  162. }];
  163. }
  164. /**
  165. * List all ids from all triggered notifications.
  166. */
  167. - (void) getTriggeredIds:(CDVInvokedUrlCommand*)command
  168. {
  169. [self.commandDelegate runInBackground:^{
  170. CDVPluginResult* result;
  171. NSArray* triggeredIds;
  172. triggeredIds = [[UIApplication sharedApplication]
  173. triggeredLocalNotificationIds];
  174. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  175. messageAsArray:triggeredIds];
  176. [self.commandDelegate sendPluginResult:result
  177. callbackId:command.callbackId];
  178. }];
  179. }
  180. /**
  181. * List all properties for given scheduled notifications.
  182. *
  183. * @param ids
  184. * The IDs of the notifications
  185. */
  186. - (void) getScheduled:(CDVInvokedUrlCommand*)command
  187. {
  188. [self.commandDelegate runInBackground:^{
  189. NSArray* ids = command.arguments;
  190. NSArray* notifications;
  191. CDVPluginResult* result;
  192. if (ids.count == 0) {
  193. notifications = [[UIApplication sharedApplication]
  194. scheduledLocalNotificationOptions];
  195. } else {
  196. notifications = [[UIApplication sharedApplication]
  197. scheduledLocalNotificationOptions:ids];
  198. }
  199. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  200. messageAsArray:notifications];
  201. [self.commandDelegate sendPluginResult:result
  202. callbackId:command.callbackId];
  203. }];
  204. }
  205. /**
  206. * List all properties for given triggered notifications.
  207. *
  208. * @param ids
  209. * The IDs of the notifications
  210. */
  211. - (void) getTriggered:(CDVInvokedUrlCommand *)command
  212. {
  213. [self.commandDelegate runInBackground:^{
  214. NSArray* ids = command.arguments;
  215. NSArray* notifications;
  216. CDVPluginResult* result;
  217. if (ids.count == 0) {
  218. notifications = [[UIApplication sharedApplication]
  219. triggeredLocalNotificationOptions];
  220. } else {
  221. notifications = [[UIApplication sharedApplication]
  222. triggeredLocalNotificationOptions:ids];
  223. }
  224. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  225. messageAsArray:notifications];
  226. [self.commandDelegate sendPluginResult:result
  227. callbackId:command.callbackId];
  228. }];
  229. }
  230. /**
  231. * Inform if the app has the permission to show
  232. * badges and local notifications.
  233. */
  234. - (void) hasPermission:(CDVInvokedUrlCommand*)command
  235. {
  236. [self.commandDelegate runInBackground:^{
  237. CDVPluginResult* result;
  238. BOOL hasPermission;
  239. hasPermission = [[UIApplication sharedApplication]
  240. hasPermissionToScheduleLocalNotifications];
  241. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  242. messageAsBool:hasPermission];
  243. [self.commandDelegate sendPluginResult:result
  244. callbackId:command.callbackId];
  245. }];
  246. }
  247. /**
  248. * Ask for permission to show badges.
  249. */
  250. - (void) registerPermission:(CDVInvokedUrlCommand*)command
  251. {
  252. #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
  253. _command = command;
  254. [self.commandDelegate runInBackground:^{
  255. [[UIApplication sharedApplication]
  256. registerPermissionToScheduleLocalNotifications];
  257. }];
  258. #else
  259. [self hasPermission:command];
  260. #endif
  261. }
  262. #pragma mark -
  263. #pragma mark Core Logic
  264. /**
  265. * Schedule the local notification.
  266. */
  267. - (void) scheduleLocalNotification:(UILocalNotification*)notification
  268. {
  269. [self cancelForerunnerLocalNotification:notification];
  270. [[UIApplication sharedApplication]
  271. scheduleLocalNotification:notification];
  272. }
  273. /**
  274. * Cancel the local notification.
  275. */
  276. - (void) cancelLocalNotification:(UILocalNotification*)notification
  277. {
  278. if (!notification)
  279. return;
  280. [[UIApplication sharedApplication]
  281. cancelLocalNotification:notification];
  282. [UIApplication sharedApplication]
  283. .applicationIconBadgeNumber -= 1;
  284. }
  285. /**
  286. * Cancel all currently scheduled notifications.
  287. */
  288. - (void) cancelAllLocalNotifications
  289. {
  290. NSArray* notifications;
  291. notifications = [[UIApplication sharedApplication]
  292. scheduledLocalNotifications];
  293. for (UILocalNotification* notification in notifications) {
  294. [self cancelLocalNotification:notification];
  295. }
  296. [[UIApplication sharedApplication]
  297. cancelAllLocalNotifications];
  298. [[UIApplication sharedApplication]
  299. setApplicationIconBadgeNumber:0];
  300. }
  301. /**
  302. * Cancel a maybe given forerunner with the same ID.
  303. */
  304. - (void) cancelForerunnerLocalNotification:(UILocalNotification*)notification
  305. {
  306. NSString* id = notification.options.id;
  307. UILocalNotification* forerunner;
  308. forerunner = [[UIApplication sharedApplication]
  309. scheduledLocalNotificationWithId:id];
  310. if (!forerunner)
  311. return;
  312. [self cancelLocalNotification:forerunner];
  313. }
  314. /**
  315. * Cancels all local notification with are older then
  316. * a specific amount of seconds
  317. */
  318. - (void) cancelAllNotificationsWhichAreOlderThen:(float)seconds
  319. {
  320. NSArray* notifications;
  321. notifications = [[UIApplication sharedApplication]
  322. scheduledLocalNotifications];
  323. for (UILocalNotification* notification in notifications)
  324. {
  325. if (notification && notification.repeatInterval == NSCalendarUnitEra
  326. && notification.timeIntervalSinceFireDate > seconds)
  327. {
  328. [self cancelLocalNotification:notification];
  329. [self fireEvent:@"cancel" localNotification:notification];
  330. }
  331. }
  332. }
  333. #pragma mark -
  334. #pragma mark Delegates
  335. /**
  336. * Calls the cancel or trigger event after a local notification was received.
  337. * Cancels the local notification if autoCancel was set to true.
  338. */
  339. - (void) didReceiveLocalNotification:(NSNotification*)localNotification
  340. {
  341. UILocalNotification* notification = [localNotification object];
  342. BOOL autoCancel = notification.options.autoCancel;
  343. NSTimeInterval timeInterval = notification.timeIntervalSinceFireDate;
  344. NSString* event = (timeInterval <= 1 && deviceready) ? @"trigger" : @"click";
  345. if ([event isEqualToString:@"click"]) {
  346. [UIApplication sharedApplication]
  347. .applicationIconBadgeNumber -= 1;
  348. }
  349. [self fireEvent:event localNotification:notification];
  350. if (autoCancel && [event isEqualToString:@"click"]) {
  351. [self cancelLocalNotification:notification];
  352. [self fireEvent:@"cancel" localNotification:notification];
  353. }
  354. }
  355. /**
  356. * Called when app has started
  357. * (by clicking on a local notification).
  358. */
  359. - (void) didFinishLaunchingWithOptions:(NSNotification*)notification
  360. {
  361. NSDictionary* launchOptions = [notification userInfo];
  362. UILocalNotification* localNotification;
  363. localNotification = [launchOptions objectForKey:
  364. UIApplicationLaunchOptionsLocalNotificationKey];
  365. if (localNotification) {
  366. [self didReceiveLocalNotification:
  367. [NSNotification notificationWithName:CDVLocalNotification
  368. object:localNotification]];
  369. }
  370. }
  371. /**
  372. * Called on otification settings registration is completed.
  373. */
  374. - (void) didRegisterUserNotificationSettings:(UIUserNotificationSettings*)settings
  375. {
  376. if (_command)
  377. {
  378. [self hasPermission:_command];
  379. _command = NULL;
  380. }
  381. }
  382. #pragma mark -
  383. #pragma mark Life Cycle
  384. /**
  385. * Registers obervers after plugin was initialized.
  386. */
  387. - (void) pluginInitialize
  388. {
  389. NSNotificationCenter* center = [NSNotificationCenter
  390. defaultCenter];
  391. eventQueue = [[NSMutableArray alloc] init];
  392. [center addObserver:self
  393. selector:@selector(didReceiveLocalNotification:)
  394. name:CDVLocalNotification
  395. object:nil];
  396. [center addObserver:self
  397. selector:@selector(didFinishLaunchingWithOptions:)
  398. name:UIApplicationDidFinishLaunchingNotification
  399. object:nil];
  400. [center addObserver:self
  401. selector:@selector(didRegisterUserNotificationSettings:)
  402. name:UIApplicationRegisterUserNotificationSettings
  403. object:nil];
  404. }
  405. /**
  406. * Clears all single repeating notifications which are older then 5 days
  407. * before the app terminates.
  408. */
  409. - (void) onAppTerminate
  410. {
  411. [self cancelAllNotificationsWhichAreOlderThen:432000];
  412. }
  413. #pragma mark -
  414. #pragma mark Helper
  415. /**
  416. * Retrieves the application state
  417. *
  418. * @return
  419. * Either "background" or "foreground"
  420. */
  421. - (NSString*) applicationState
  422. {
  423. UIApplicationState state = [[UIApplication sharedApplication]
  424. applicationState];
  425. bool isActive = state == UIApplicationStateActive;
  426. return isActive ? @"foreground" : @"background";
  427. }
  428. /**
  429. * Simply invokes the callback without any parameter.
  430. */
  431. - (void) execCallback:(CDVInvokedUrlCommand*)command
  432. {
  433. CDVPluginResult *result = [CDVPluginResult
  434. resultWithStatus:CDVCommandStatus_OK];
  435. [self.commandDelegate sendPluginResult:result
  436. callbackId:command.callbackId];
  437. }
  438. /**
  439. * Fire general event.
  440. */
  441. - (void) fireEvent:(NSString*)event
  442. {
  443. [self fireEvent:event localNotification:NULL];
  444. }
  445. /**
  446. * Fire event for local notification.
  447. */
  448. - (void) fireEvent:(NSString*)event localNotification:(UILocalNotification*)notification
  449. {
  450. NSString* js;
  451. NSString* params = [NSString stringWithFormat:
  452. @"\"%@\"", self.applicationState];
  453. if (notification) {
  454. NSString* id = notification.options.id;
  455. NSString* json = notification.options.json;
  456. NSString* args = [notification.options encodeToJSON];
  457. params = [NSString stringWithFormat:
  458. @"\"%@\",\"%@\",\\'%@\\',JSON.parse(\\'%@\\')",
  459. id, self.applicationState, json, args];
  460. }
  461. js = [NSString stringWithFormat:
  462. @"setTimeout('plugin.notification.local.on%@(%@)',0)",
  463. event, params];
  464. if (deviceready) {
  465. [self.commandDelegate evalJs:js];
  466. } else {
  467. [self.eventQueue addObject:js];
  468. }
  469. }
  470. @end