APPLocalNotification.m 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669
  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. if (notification==nil) {
  226. NSLog(@"cancelNotification: Notification equals nil");
  227. }else{
  228. [[UIApplication sharedApplication]
  229. cancelLocalNotification:notification];
  230. }
  231. if (fireEvent) {
  232. [self fireEvent:@"cancel" id:id json:json];
  233. }
  234. }
  235. /**
  236. * Cancels all local notification with are older then
  237. * a specific amount of seconds
  238. *
  239. * @param {float} seconds
  240. * The time interval in seconds
  241. */
  242. - (void) cancelAllNotificationsWhichAreOlderThen:(float)seconds
  243. {
  244. NSDate* now = [NSDate date];
  245. NSArray* notifications = self.scheduledNotifications;
  246. for (UILocalNotification* notification in notifications)
  247. {
  248. NSDate* fireDate = notification.fireDate;
  249. NSTimeInterval fireDateDistance = [now timeIntervalSinceDate:
  250. fireDate];
  251. if (notification.repeatInterval == NSCalendarUnitEra
  252. && fireDateDistance > seconds) {
  253. [self cancelNotification:notification fireEvent:YES];
  254. }
  255. }
  256. }
  257. /**
  258. * Creates an notification object based on the given properties.
  259. *
  260. * @param {NSMutableDictionary} properties
  261. * The properties for the local notification
  262. * @return {UILocalNotification}
  263. */
  264. - (UILocalNotification*) notificationWithProperties:(NSMutableDictionary*)options
  265. {
  266. UILocalNotification* notification = [[UILocalNotification alloc] init];
  267. double timestamp = [[options objectForKey:@"date"] doubleValue];
  268. NSString* msg = [options objectForKey:@"message"];
  269. NSString* title = [options objectForKey:@"title"];
  270. NSString* sound = [options objectForKey:@"sound"];
  271. NSString* repeat = [options objectForKey:@"repeat"];
  272. NSInteger badge = [[options objectForKey:@"badge"] intValue];
  273. notification.fireDate = [NSDate dateWithTimeIntervalSince1970:timestamp];
  274. notification.timeZone = [NSTimeZone defaultTimeZone];
  275. notification.userInfo = [self userDict:options];
  276. notification.applicationIconBadgeNumber = badge;
  277. notification.repeatInterval = [[[self repeatDict] objectForKey:repeat]
  278. intValue];
  279. if (![self stringIsNullOrEmpty:msg])
  280. {
  281. if (![self stringIsNullOrEmpty:title]) {
  282. notification.alertBody = [NSString stringWithFormat:
  283. @"%@\n%@", title, msg];
  284. } else {
  285. notification.alertBody = msg;
  286. }
  287. }
  288. if (sound != (NSString*)[NSNull null])
  289. {
  290. if ([sound isEqualToString:@""]) {
  291. notification.soundName = UILocalNotificationDefaultSoundName;
  292. } else {
  293. notification.soundName = sound;
  294. }
  295. }
  296. return notification;
  297. }
  298. #pragma mark -
  299. #pragma mark Plugin delegate and life cycle methods
  300. /**
  301. * Calls the cancel or trigger event after a local notification was received.
  302. * Cancels the local notification if autoCancel was set to true.
  303. */
  304. - (void) didReceiveLocalNotification:(NSNotification*)localNotification
  305. {
  306. UILocalNotification* notification = [localNotification object];
  307. NSDictionary* userInfo = notification.userInfo;
  308. NSString* id = [userInfo objectForKey:@"id"];
  309. NSString* json = [userInfo objectForKey:@"json"];
  310. BOOL autoCancel = [[userInfo objectForKey:@"autoCancel"] boolValue];
  311. NSDate* now = [NSDate date];
  312. NSDate* fireDate = notification.fireDate;
  313. NSTimeInterval fireDateDistance = [now timeIntervalSinceDate:fireDate];
  314. NSString* event = (fireDateDistance < 1) ? @"trigger" : @"click";
  315. if ([[self applicationState] isEqualToString:@"foreground"]) {
  316. event = @"trigger";
  317. }
  318. if (autoCancel && [event isEqualToString:@"click"]) {
  319. [self cancelNotification:notification fireEvent:YES];
  320. }
  321. [self fireEvent:event id:id json:json];
  322. }
  323. /**
  324. * Calls the cancel or trigger event after a local notification was received.
  325. */
  326. - (void) didFinishLaunchingWithOptions:(NSNotification*)notification
  327. {
  328. NSDictionary* launchOptions = [notification userInfo];
  329. UILocalNotification* localNotification = [launchOptions objectForKey:
  330. UIApplicationLaunchOptionsLocalNotificationKey];
  331. if (localNotification) {
  332. [self didReceiveLocalNotification:
  333. [NSNotification notificationWithName:CDVLocalNotification
  334. object:localNotification]];
  335. }
  336. }
  337. /**
  338. * Registers obervers for the following events after plugin was initialized.
  339. * didReceiveLocalNotification:
  340. * didFinishLaunchingWithOptions:
  341. */
  342. - (void) pluginInitialize
  343. {
  344. NSNotificationCenter* notificationCenter = [NSNotificationCenter
  345. defaultCenter];
  346. eventQueue = [[NSMutableArray alloc] init];
  347. [notificationCenter addObserver:self
  348. selector:@selector(didReceiveLocalNotification:)
  349. name:CDVLocalNotification
  350. object:nil];
  351. [notificationCenter addObserver:self
  352. selector:@selector(didFinishLaunchingWithOptions:)
  353. name:UIApplicationDidFinishLaunchingNotification
  354. object:nil];
  355. }
  356. /**
  357. * Clears all single repeating notifications which are older then 5 days
  358. * before the app terminates.
  359. */
  360. - (void) onAppTerminate
  361. {
  362. [self cancelAllNotificationsWhichAreOlderThen:432000];
  363. }
  364. #pragma mark -
  365. #pragma mark Plugin helper methods
  366. /**
  367. * Retrurns a key-value dictionary for repeat intervals.
  368. *
  369. * @return {NSMutableDictionary}
  370. */
  371. - (NSMutableDictionary*) repeatDict
  372. {
  373. NSMutableDictionary* repeatDict = [[NSMutableDictionary alloc] init];
  374. [repeatDict setObject:
  375. [NSNumber numberWithInt:NSCalendarUnitSecond] forKey:@"secondly"];
  376. [repeatDict setObject:
  377. [NSNumber numberWithInt:NSCalendarUnitMinute] forKey:@"minutely"];
  378. [repeatDict setObject:
  379. [NSNumber numberWithInt:NSCalendarUnitHour] forKey:@"hourly"];
  380. [repeatDict setObject:
  381. [NSNumber numberWithInt:NSCalendarUnitDay] forKey:@"daily"];
  382. [repeatDict setObject:
  383. [NSNumber numberWithInt:NSWeekCalendarUnit] forKey:@"weekly"];
  384. [repeatDict setObject:
  385. [NSNumber numberWithInt:NSCalendarUnitMonth] forKey:@"monthly"];
  386. [repeatDict setObject:
  387. [NSNumber numberWithInt:NSCalendarUnitYear] forKey:@"yearly"];
  388. [repeatDict setObject:
  389. [NSNumber numberWithInt:NSCalendarUnitEra] forKey:@""];
  390. return repeatDict;
  391. }
  392. /**
  393. * Returns the userDict for a local notification.
  394. *
  395. * @param {NSMutableDictionary} options
  396. * The properties for the local notification
  397. * @return {NSDictionary}
  398. */
  399. - (NSDictionary*) userDict:(NSMutableDictionary*)options
  400. {
  401. NSString* id = [options objectForKey:@"id"];
  402. NSString* ac = [options objectForKey:@"autoCancel"];
  403. NSString* js = [options objectForKey:@"json"];
  404. return [NSDictionary dictionaryWithObjectsAndKeys:
  405. id, @"id", ac, @"autoCancel", js, @"json", nil];
  406. }
  407. /**
  408. * Checks weather the given string is empty or not.
  409. *
  410. * @param {NSString} str The string to be check
  411. * @return {BOOL}
  412. */
  413. - (BOOL) stringIsNullOrEmpty:(NSString*)str
  414. {
  415. if (str == (NSString*)[NSNull null]) {
  416. return YES;
  417. }
  418. if ([str isEqualToString:@""]) {
  419. return YES;
  420. }
  421. return NO;
  422. }
  423. /**
  424. * Checks wether a notification with an ID is scheduled or not.
  425. *
  426. * @param id
  427. * The ID of the notification
  428. * @return BOOL
  429. */
  430. - (BOOL) isNotificationScheduledWithId:(NSString*)id
  431. {
  432. UILocalNotification* notification = [self notificationWithId:id];
  433. return notification != NULL;
  434. }
  435. /**
  436. * Checks wether a notification with an ID was triggered or not.
  437. *
  438. * @param id
  439. * The ID of the notification
  440. * @return BOOL
  441. */
  442. - (BOOL) isNotificationTriggeredWithId:(NSString*)id
  443. {
  444. UILocalNotification* notification = [self notificationWithId:id];
  445. if (notification == NULL) {
  446. return NO;
  447. }
  448. return [self isNotificationTriggered:notification];
  449. }
  450. /**
  451. * Checks wether a notification was triggered or not.
  452. *
  453. * @param notification
  454. * The notification
  455. * @return BOOL
  456. */
  457. - (BOOL) isNotificationTriggered:(UILocalNotification*)notification
  458. {
  459. NSDate* now = [NSDate date];
  460. NSDate* fireDate = notification.fireDate;
  461. bool isLaterThanOrEqualTo = !([now compare:fireDate] == NSOrderedAscending);
  462. return isLaterThanOrEqualTo;
  463. }
  464. /**
  465. * Retrieves the local notification by its ID.
  466. *
  467. * @param {NSString} id
  468. * The ID of the notification
  469. * @return UILocalNotification*
  470. */
  471. - (UILocalNotification*) notificationWithId:(NSString*)id
  472. {
  473. NSArray* notifications = self.scheduledNotifications;
  474. for (UILocalNotification* notification in notifications)
  475. {
  476. NSString* notId = [notification.userInfo objectForKey:@"id"];
  477. if ([notId isEqualToString:id]) {
  478. return notification;
  479. }
  480. }
  481. return NULL;
  482. }
  483. /**
  484. * Retrieves the application state
  485. *
  486. * @return {NSString}
  487. * Either "background" or "foreground"
  488. */
  489. - (NSString*) applicationState
  490. {
  491. UIApplicationState state = [[UIApplication sharedApplication]
  492. applicationState];
  493. bool isActive = state == UIApplicationStateActive;
  494. return isActive ? @"foreground" : @"background";
  495. }
  496. /**
  497. * Retrieves all scheduled notifications.
  498. *
  499. * @return {NSArray}
  500. * A list of all scheduled local notifications
  501. */
  502. - (NSArray*) scheduledNotifications
  503. {
  504. NSMutableArray* notificationsWithoutNIL = [[NSMutableArray alloc]
  505. init];
  506. NSArray* notifications = [[UIApplication sharedApplication]
  507. scheduledLocalNotifications];
  508. for (UILocalNotification* notification in notifications)
  509. {
  510. if (notification) {
  511. [notificationsWithoutNIL addObject:notification];
  512. }
  513. }
  514. return notificationsWithoutNIL;
  515. }
  516. #pragma mark -
  517. #pragma mark Plugin callback methods
  518. /**
  519. * Simply invokes the callback without any parameter.
  520. */
  521. - (void) execCallback:(CDVInvokedUrlCommand*)command
  522. {
  523. CDVPluginResult *result = [CDVPluginResult
  524. resultWithStatus:CDVCommandStatus_OK];
  525. [self.commandDelegate sendPluginResult:result
  526. callbackId:command.callbackId];
  527. }
  528. /**
  529. * Fires the given event.
  530. *
  531. * @param {NSString} event
  532. * The Name of the event
  533. * @param {NSString} id
  534. * The ID of the notification
  535. * @param {NSString} json
  536. * A custom (JSON) string
  537. */
  538. - (void) fireEvent:(NSString*)event id:(NSString*)id json:(NSString*)json
  539. {
  540. NSString* appState = self.applicationState;
  541. NSString* params = [NSString stringWithFormat:
  542. @"\"%@\",\"%@\",\\'%@\\'",
  543. id, appState, json];
  544. NSString* js = [NSString stringWithFormat:
  545. @"setTimeout('plugin.notification.local.on%@(%@)',0)",
  546. event, params];
  547. if (deviceready) {
  548. [self.commandDelegate evalJs:js];
  549. } else {
  550. [self.eventQueue addObject:js];
  551. }
  552. }
  553. @end