APPLocalNotification.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  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. @interface APPLocalNotification ()
  20. // Retrieves all scheduled notifications
  21. @property (readonly, getter=scheduledNotifications) NSArray* scheduledNotifications;
  22. // Retrieves the application state
  23. @property (readonly, getter=applicationState) NSString* applicationState;
  24. // All events will be queued until deviceready has been fired
  25. @property (readwrite, assign) BOOL deviceready;
  26. // Event queue
  27. @property (readonly, nonatomic, retain) NSMutableArray* eventQueue;
  28. @end
  29. @implementation APPLocalNotification
  30. @synthesize deviceready, eventQueue, applicationState, scheduledNotifications;
  31. #pragma mark -
  32. #pragma mark Plugin interface methods
  33. /**
  34. * Executes all queued events.
  35. */
  36. - (void) deviceready:(CDVInvokedUrlCommand*)command
  37. {
  38. deviceready = YES;
  39. for (NSString* js in eventQueue) {
  40. [self.commandDelegate evalJs:js];
  41. }
  42. [eventQueue removeAllObjects];
  43. }
  44. /**
  45. * Schedules a new local notification.
  46. *
  47. * @param {NSMutableDictionary} properties
  48. * The properties of the notification
  49. */
  50. - (void) add:(CDVInvokedUrlCommand*)command
  51. {
  52. [self.commandDelegate runInBackground:^{
  53. NSArray* arguments = [command arguments];
  54. NSMutableDictionary* properties = [arguments objectAtIndex:0];
  55. NSString* id = [properties objectForKey:@"id"];
  56. if ([self isNotificationScheduledWithId:id]) {
  57. UILocalNotification* notification = [self notificationWithId:id];
  58. dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.3 * NSEC_PER_SEC),
  59. dispatch_get_main_queue(), ^{
  60. [self cancelNotification:notification fireEvent:NO];
  61. });
  62. }
  63. [self scheduleNotificationWithProperties:properties];
  64. [self execCallback:command];
  65. }];
  66. }
  67. /**
  68. * Cancels a given local notification.
  69. *
  70. * @param {NSString} id
  71. * The ID of the local notification
  72. */
  73. - (void) cancel:(CDVInvokedUrlCommand*)command
  74. {
  75. [self.commandDelegate runInBackground:^{
  76. NSArray* arguments = [command arguments];
  77. NSString* id = [arguments objectAtIndex:0];
  78. UILocalNotification* notification = [self notificationWithId:id];
  79. if (notification) {
  80. [self cancelNotification:notification fireEvent:YES];
  81. }
  82. [self execCallback:command];
  83. }];
  84. }
  85. /**
  86. * Cancels all currently scheduled notifications.
  87. */
  88. - (void) cancelAll:(CDVInvokedUrlCommand*)command
  89. {
  90. [self.commandDelegate runInBackground:^{
  91. NSArray* notifications = self.scheduledNotifications;
  92. for (UILocalNotification* notification in notifications) {
  93. [self cancelNotification:notification fireEvent:YES];
  94. }
  95. [[UIApplication sharedApplication]
  96. cancelAllLocalNotifications];
  97. [[UIApplication sharedApplication]
  98. setApplicationIconBadgeNumber:0];
  99. [self execCallback:command];
  100. }];
  101. }
  102. /**
  103. * Checks wether a notification with an ID is scheduled.
  104. *
  105. * @param {NSString} id
  106. * The ID of the notification
  107. * @param callback
  108. * The callback function to be called with the result
  109. */
  110. - (void) isScheduled:(CDVInvokedUrlCommand*)command
  111. {
  112. [self.commandDelegate runInBackground:^{
  113. NSArray* arguments = [command arguments];
  114. NSString* id = [arguments objectAtIndex:0];
  115. bool isScheduled = [self isNotificationScheduledWithId:id];
  116. CDVPluginResult* result;
  117. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  118. messageAsBool:isScheduled];
  119. [self.commandDelegate sendPluginResult:result
  120. callbackId:command.callbackId];
  121. }];
  122. }
  123. /**
  124. * Retrieves a list of ids from all currently pending notifications.
  125. *
  126. * @param callback
  127. * The callback function to be called with the result
  128. */
  129. - (void) getScheduledIds:(CDVInvokedUrlCommand*)command
  130. {
  131. [self.commandDelegate runInBackground:^{
  132. NSArray* notifications = self.scheduledNotifications;
  133. NSMutableArray* scheduledIds = [[NSMutableArray alloc] init];
  134. CDVPluginResult* result;
  135. for (UILocalNotification* notification in notifications)
  136. {
  137. NSString* id = [notification.userInfo objectForKey:@"id"];
  138. [scheduledIds addObject:id];
  139. }
  140. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  141. messageAsArray:scheduledIds];
  142. [self.commandDelegate sendPluginResult:result
  143. callbackId:command.callbackId];
  144. }];
  145. }
  146. /**
  147. * Checks wether a notification with an ID was triggered.
  148. *
  149. * @param {NSString} id
  150. * The ID of the notification
  151. * @param callback
  152. * The callback function to be called with the result
  153. */
  154. - (void) isTriggered:(CDVInvokedUrlCommand*)command
  155. {
  156. [self.commandDelegate runInBackground:^{
  157. NSArray* arguments = [command arguments];
  158. NSString* id = [arguments objectAtIndex:0];
  159. bool isTriggered = [self isNotificationTriggeredWithId:id];
  160. CDVPluginResult* result;
  161. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  162. messageAsBool:isTriggered];
  163. [self.commandDelegate sendPluginResult:result
  164. callbackId:command.callbackId];
  165. }];
  166. }
  167. /**
  168. * Retrieves a list of ids from all currently triggered notifications.
  169. *
  170. * @param callback
  171. * The callback function to be called with the result
  172. */
  173. - (void) getTriggeredIds:(CDVInvokedUrlCommand*)command
  174. {
  175. [self.commandDelegate runInBackground:^{
  176. NSArray* notifications = self.scheduledNotifications;
  177. NSMutableArray* scheduledIds = [[NSMutableArray alloc] init];
  178. CDVPluginResult* result;
  179. for (UILocalNotification* notification in notifications)
  180. {
  181. if (![self isNotificationTriggered:notification]) {
  182. continue;
  183. }
  184. NSString* id = [notification.userInfo objectForKey:@"id"];
  185. [scheduledIds addObject:id];
  186. }
  187. result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK
  188. messageAsArray:scheduledIds];
  189. [self.commandDelegate sendPluginResult:result
  190. callbackId:command.callbackId];
  191. }];
  192. }
  193. #pragma mark -
  194. #pragma mark Plugin core methods
  195. /**
  196. * Schedules a new local notification and fies the coresponding event.
  197. *
  198. * @param {NSMutableDictionary} properties
  199. * The properties of the notification
  200. */
  201. - (void) scheduleNotificationWithProperties:(NSMutableDictionary*)properties
  202. {
  203. UILocalNotification* notification = [self notificationWithProperties:
  204. properties];
  205. NSDictionary* userInfo = notification.userInfo;
  206. NSString* id = [userInfo objectForKey:@"id"];
  207. NSString* json = [userInfo objectForKey:@"json"];
  208. [self fireEvent:@"add" id:id json:json];
  209. [[UIApplication sharedApplication]
  210. scheduleLocalNotification:notification];
  211. }
  212. /**
  213. * Cancels the given local notification
  214. * and fires the cancel event.
  215. *
  216. * @param {NSString} id
  217. * The ID of the local notification
  218. */
  219. - (void) cancelNotification:(UILocalNotification*)notification
  220. fireEvent:(BOOL)fireEvent
  221. {
  222. NSDictionary* userInfo = notification.userInfo;
  223. NSString* id = [userInfo objectForKey:@"id"];
  224. NSString* json = [userInfo objectForKey:@"json"];
  225. [[UIApplication sharedApplication]
  226. cancelLocalNotification:notification];
  227. if (fireEvent) {
  228. [self fireEvent:@"cancel" id:id json:json];
  229. }
  230. }
  231. /**
  232. * Cancels all local notification with are older then
  233. * a specific amount of seconds
  234. *
  235. * @param {float} seconds
  236. * The time interval in seconds
  237. */
  238. - (void) cancelAllNotificationsWhichAreOlderThen:(float)seconds
  239. {
  240. NSDate* now = [NSDate date];
  241. NSArray* notifications = self.scheduledNotifications;
  242. for (UILocalNotification* notification in notifications)
  243. {
  244. NSDate* fireDate = notification.fireDate;
  245. NSTimeInterval fireDateDistance = [now timeIntervalSinceDate:
  246. fireDate];
  247. if (notification.repeatInterval == NSCalendarUnitEra
  248. && fireDateDistance > seconds) {
  249. [self cancelNotification:notification fireEvent:YES];
  250. }
  251. }
  252. }
  253. /**
  254. * Creates an notification object based on the given properties.
  255. *
  256. * @param {NSMutableDictionary} properties
  257. * The properties for the local notification
  258. * @return {UILocalNotification}
  259. */
  260. - (UILocalNotification*) notificationWithProperties:(NSMutableDictionary*)options
  261. {
  262. UILocalNotification* notification = [[UILocalNotification alloc] init];
  263. double timestamp = [[options objectForKey:@"date"] doubleValue];
  264. NSString* msg = [options objectForKey:@"message"];
  265. NSString* title = [options objectForKey:@"title"];
  266. NSString* sound = [options objectForKey:@"sound"];
  267. NSString* repeat = [options objectForKey:@"repeat"];
  268. NSInteger badge = [[options objectForKey:@"badge"] intValue];
  269. notification.fireDate = [NSDate dateWithTimeIntervalSince1970:timestamp];
  270. notification.timeZone = [NSTimeZone defaultTimeZone];
  271. notification.userInfo = [self userDict:options];
  272. notification.applicationIconBadgeNumber = badge;
  273. notification.repeatInterval = [[[self repeatDict] objectForKey:repeat]
  274. intValue];
  275. if (![self stringIsNullOrEmpty:msg])
  276. {
  277. if (![self stringIsNullOrEmpty:title]) {
  278. notification.alertBody = [NSString stringWithFormat:
  279. @"%@\n%@", title, msg];
  280. } else {
  281. notification.alertBody = msg;
  282. }
  283. }
  284. if (sound != (NSString*)[NSNull null])
  285. {
  286. if ([sound isEqualToString:@""]) {
  287. notification.soundName = UILocalNotificationDefaultSoundName;
  288. } else {
  289. notification.soundName = sound;
  290. }
  291. }
  292. return notification;
  293. }
  294. #pragma mark -
  295. #pragma mark Plugin delegate and life cycle methods
  296. /**
  297. * Calls the cancel or trigger event after a local notification was received.
  298. * Cancels the local notification if autoCancel was set to true.
  299. */
  300. - (void) didReceiveLocalNotification:(NSNotification*)localNotification
  301. {
  302. UILocalNotification* notification = [localNotification object];
  303. NSDictionary* userInfo = notification.userInfo;
  304. NSString* id = [userInfo objectForKey:@"id"];
  305. NSString* json = [userInfo objectForKey:@"json"];
  306. BOOL autoCancel = [[userInfo objectForKey:@"autoCancel"] boolValue];
  307. NSDate* now = [NSDate date];
  308. NSDate* fireDate = notification.fireDate;
  309. NSTimeInterval fireDateDistance = [now timeIntervalSinceDate:fireDate];
  310. NSString* event = (fireDateDistance < 1) ? @"trigger" : @"click";
  311. if ([[self applicationState] isEqualToString:@"foreground"]) {
  312. event = @"trigger";
  313. }
  314. if (autoCancel && [event isEqualToString:@"click"]) {
  315. [self cancelNotification:notification fireEvent:YES];
  316. }
  317. [self fireEvent:event id:id json:json];
  318. }
  319. /**
  320. * Calls the cancel or trigger event after a local notification was received.
  321. */
  322. - (void) didFinishLaunchingWithOptions:(NSNotification*)notification
  323. {
  324. NSDictionary* launchOptions = [notification userInfo];
  325. UILocalNotification* localNotification = [launchOptions objectForKey:
  326. UIApplicationLaunchOptionsLocalNotificationKey];
  327. if (localNotification) {
  328. [self didReceiveLocalNotification:
  329. [NSNotification notificationWithName:CDVLocalNotification
  330. object:localNotification]];
  331. }
  332. }
  333. /**
  334. * Registers obervers for the following events after plugin was initialized.
  335. * didReceiveLocalNotification:
  336. * didFinishLaunchingWithOptions:
  337. */
  338. - (void) pluginInitialize
  339. {
  340. NSNotificationCenter* notificationCenter = [NSNotificationCenter
  341. defaultCenter];
  342. eventQueue = [[NSMutableArray alloc] init];
  343. [notificationCenter addObserver:self
  344. selector:@selector(didReceiveLocalNotification:)
  345. name:CDVLocalNotification
  346. object:nil];
  347. [notificationCenter addObserver:self
  348. selector:@selector(didFinishLaunchingWithOptions:)
  349. name:UIApplicationDidFinishLaunchingNotification
  350. object:nil];
  351. }
  352. /**
  353. * Clears all single repeating notifications which are older then 5 days
  354. * before the app terminates.
  355. */
  356. - (void) onAppTerminate
  357. {
  358. [self cancelAllNotificationsWhichAreOlderThen:432000];
  359. }
  360. #pragma mark -
  361. #pragma mark Plugin helper methods
  362. /**
  363. * Retrurns a key-value dictionary for repeat intervals.
  364. *
  365. * @return {NSMutableDictionary}
  366. */
  367. - (NSMutableDictionary*) repeatDict
  368. {
  369. NSMutableDictionary* repeatDict = [[NSMutableDictionary alloc] init];
  370. [repeatDict setObject:
  371. [NSNumber numberWithInt:NSCalendarUnitSecond] forKey:@"secondly"];
  372. [repeatDict setObject:
  373. [NSNumber numberWithInt:NSCalendarUnitMinute] forKey:@"minutely"];
  374. [repeatDict setObject:
  375. [NSNumber numberWithInt:NSCalendarUnitHour] forKey:@"hourly"];
  376. [repeatDict setObject:
  377. [NSNumber numberWithInt:NSCalendarUnitDay] forKey:@"daily"];
  378. [repeatDict setObject:
  379. [NSNumber numberWithInt:NSWeekCalendarUnit] forKey:@"weekly"];
  380. [repeatDict setObject:
  381. [NSNumber numberWithInt:NSCalendarUnitMonth] forKey:@"monthly"];
  382. [repeatDict setObject:
  383. [NSNumber numberWithInt:NSCalendarUnitYear] forKey:@"yearly"];
  384. [repeatDict setObject:
  385. [NSNumber numberWithInt:NSCalendarUnitEra] forKey:@""];
  386. return repeatDict;
  387. }
  388. /**
  389. * Returns the userDict for a local notification.
  390. *
  391. * @param {NSMutableDictionary} options
  392. * The properties for the local notification
  393. * @return {NSDictionary}
  394. */
  395. - (NSDictionary*) userDict:(NSMutableDictionary*)options
  396. {
  397. NSString* id = [options objectForKey:@"id"];
  398. NSString* ac = [options objectForKey:@"autoCancel"];
  399. NSString* js = [options objectForKey:@"json"];
  400. return [NSDictionary dictionaryWithObjectsAndKeys:
  401. id, @"id", ac, @"autoCancel", js, @"json", nil];
  402. }
  403. /**
  404. * Checks weather the given string is empty or not.
  405. *
  406. * @param {NSString} str The string to be check
  407. * @return {BOOL}
  408. */
  409. - (BOOL) stringIsNullOrEmpty:(NSString*)str
  410. {
  411. if (str == (NSString*)[NSNull null]) {
  412. return YES;
  413. }
  414. if ([str isEqualToString:@""]) {
  415. return YES;
  416. }
  417. return NO;
  418. }
  419. /**
  420. * Checks wether a notification with an ID is scheduled or not.
  421. *
  422. * @param id
  423. * The ID of the notification
  424. * @return BOOL
  425. */
  426. - (BOOL) isNotificationScheduledWithId:(NSString*)id
  427. {
  428. UILocalNotification* notification = [self notificationWithId:id];
  429. return notification != NULL;
  430. }
  431. /**
  432. * Checks wether a notification with an ID was triggered or not.
  433. *
  434. * @param id
  435. * The ID of the notification
  436. * @return BOOL
  437. */
  438. - (BOOL) isNotificationTriggeredWithId:(NSString*)id
  439. {
  440. UILocalNotification* notification = [self notificationWithId:id];
  441. if (notification == NULL) {
  442. return NO;
  443. }
  444. return [self isNotificationTriggered:notification];
  445. }
  446. /**
  447. * Checks wether a notification was triggered or not.
  448. *
  449. * @param notification
  450. * The notification
  451. * @return BOOL
  452. */
  453. - (BOOL) isNotificationTriggered:(UILocalNotification*)notification
  454. {
  455. NSDate* now = [NSDate date];
  456. NSDate* fireDate = notification.fireDate;
  457. bool isLaterThanOrEqualTo = !([now compare:fireDate] == NSOrderedAscending);
  458. return isLaterThanOrEqualTo;
  459. }
  460. /**
  461. * Retrieves the local notification by its ID.
  462. *
  463. * @param {NSString} id
  464. * The ID of the notification
  465. * @return UILocalNotification*
  466. */
  467. - (UILocalNotification*) notificationWithId:(NSString*)id
  468. {
  469. NSArray* notifications = self.scheduledNotifications;
  470. for (UILocalNotification* notification in notifications)
  471. {
  472. NSString* notId = [[notification.userInfo objectForKey:@"id"] stringValue];
  473. if ([notId isEqualToString:id]) {
  474. return notification;
  475. }
  476. }
  477. return NULL;
  478. }
  479. /**
  480. * Retrieves the application state
  481. *
  482. * @return {NSString}
  483. * Either "background" or "foreground"
  484. */
  485. - (NSString*) applicationState
  486. {
  487. UIApplicationState state = [[UIApplication sharedApplication]
  488. applicationState];
  489. bool isActive = state == UIApplicationStateActive;
  490. return isActive ? @"foreground" : @"background";
  491. }
  492. /**
  493. * Retrieves all scheduled notifications.
  494. *
  495. * @return {NSArray}
  496. * A list of all scheduled local notifications
  497. */
  498. - (NSArray*) scheduledNotifications
  499. {
  500. NSMutableArray* notificationsWithoutNIL = [[NSMutableArray alloc]
  501. init];
  502. NSArray* notifications = [[UIApplication sharedApplication]
  503. scheduledLocalNotifications];
  504. for (UILocalNotification* notification in notifications)
  505. {
  506. if (notification) {
  507. [notificationsWithoutNIL addObject:notification];
  508. }
  509. }
  510. return notificationsWithoutNIL;
  511. }
  512. #pragma mark -
  513. #pragma mark Plugin callback methods
  514. /**
  515. * Simply invokes the callback without any parameter.
  516. */
  517. - (void) execCallback:(CDVInvokedUrlCommand*)command
  518. {
  519. CDVPluginResult *result = [CDVPluginResult
  520. resultWithStatus:CDVCommandStatus_OK];
  521. [self.commandDelegate sendPluginResult:result
  522. callbackId:command.callbackId];
  523. }
  524. /**
  525. * Fires the given event.
  526. *
  527. * @param {NSString} event
  528. * The Name of the event
  529. * @param {NSString} id
  530. * The ID of the notification
  531. * @param {NSString} json
  532. * A custom (JSON) string
  533. */
  534. - (void) fireEvent:(NSString*)event id:(NSString*)id json:(NSString*)json
  535. {
  536. NSString* appState = self.applicationState;
  537. NSString* params = [NSString stringWithFormat:
  538. @"\"%@\",\"%@\",\\'%@\\'",
  539. id, appState, json];
  540. NSString* js = [NSString stringWithFormat:
  541. @"setTimeout('plugin.notification.local.on%@(%@)',0)",
  542. event, params];
  543. if (deviceready) {
  544. [self.commandDelegate evalJs:js];
  545. } else {
  546. [self.eventQueue addObject:js];
  547. }
  548. }
  549. @end