NCNotificationController.m 38 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804
  1. /**
  2. * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
  3. * SPDX-License-Identifier: GPL-3.0-or-later
  4. */
  5. #import "NCNotificationController.h"
  6. #import <UserNotifications/UserNotifications.h>
  7. #import "CallKitManager.h"
  8. #import "NCAPIController.h"
  9. #import "NCDatabaseManager.h"
  10. #import "NCIntentController.h"
  11. #import "NCNotification.h"
  12. #import "NCRoomsManager.h"
  13. #import "NCSettingsController.h"
  14. #import "NCUserInterfaceController.h"
  15. #import "NCUserStatus.h"
  16. #import "NextcloudTalk-Swift.h"
  17. NSString * const NCNotificationControllerWillPresentNotification = @"NCNotificationControllerWillPresentNotification";
  18. NSString * const NCLocalNotificationJoinChatNotification = @"NCLocalNotificationJoinChatNotification";
  19. NSString * const NCNotificationActionShareRecording = @"SHARE_RECORDING";
  20. NSString * const NCNotificationActionDismissRecordingNotification = @"DISMISS_RECORDING_NOTIFICATION";
  21. NSString * const NCNotificationActionReplyToChat = @"REPLY_CHAT";
  22. NSString * const NCNotificationActionFederationInvitationAccept = @"ACCEPT_FEDERATION_INVITATION";
  23. NSString * const NCNotificationActionFederationInvitationReject = @"REJECT_FEDERATION_INVITATION";
  24. @interface NCNotificationController () <UNUserNotificationCenterDelegate>
  25. @property (nonatomic, strong) UNUserNotificationCenter *notificationCenter;
  26. @property (nonatomic, strong) NSMutableDictionary *serverNotificationsAttempts; // notificationId -> get attempts
  27. @end
  28. @implementation NCNotificationController
  29. + (NCNotificationController *)sharedInstance
  30. {
  31. static dispatch_once_t once;
  32. static NCNotificationController *sharedInstance;
  33. dispatch_once(&once, ^{
  34. sharedInstance = [[self alloc] init];
  35. });
  36. return sharedInstance;
  37. }
  38. - (id)init
  39. {
  40. self = [super init];
  41. if (self) {
  42. _notificationCenter = [UNUserNotificationCenter currentNotificationCenter];
  43. _notificationCenter.delegate = self;
  44. _serverNotificationsAttempts = [[NSMutableDictionary alloc] init];
  45. }
  46. return self;
  47. }
  48. - (void)requestAuthorization
  49. {
  50. UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge;
  51. [_notificationCenter requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) {
  52. if (granted) {
  53. NSLog(@"User notifications permission granted.");
  54. } else {
  55. NSLog(@"User notifications permission denied.");
  56. }
  57. }];
  58. }
  59. - (void)processBackgroundPushNotification:(NCPushNotification *)pushNotification
  60. {
  61. if (pushNotification) {
  62. if (pushNotification.type == NCPushNotificationTypeDelete) {
  63. NSNumber *notificationId = @(pushNotification.notificationId);
  64. [self removeNotificationWithNotificationIds:@[notificationId] forAccountId:pushNotification.accountId];
  65. } else if (pushNotification.type == NCPushNotificationTypeDeleteAll) {
  66. [self removeAllNotificationsForAccountId:pushNotification.accountId];
  67. } else if (pushNotification.type == NCPushNotificationTypeDeleteMultiple) {
  68. [self removeNotificationWithNotificationIds:pushNotification.notificationIds forAccountId:pushNotification.accountId];
  69. } else {
  70. NSLog(@"Push Notification of an unknown type received");
  71. }
  72. }
  73. }
  74. - (void)showLocalNotification:(NCLocalNotificationType)type withUserInfo:(NSDictionary *)userInfo
  75. {
  76. UNMutableNotificationContent *content = [UNMutableNotificationContent new];
  77. switch (type) {
  78. case kNCLocalNotificationTypeMissedCall:
  79. {
  80. NSString *missedCallString = NSLocalizedString(@"Missed call from", nil);
  81. content.body = [NSString stringWithFormat:@"☎️ %@ %@", missedCallString, [userInfo objectForKey:@"displayName"]];
  82. content.userInfo = userInfo;
  83. }
  84. break;
  85. case kNCLocalNotificationTypeCancelledCall:
  86. {
  87. NSString *cancelledCallString = NSLocalizedString(@"Cancelled call from another account", nil);
  88. content.body = [NSString stringWithFormat:@"☎️ %@", cancelledCallString];
  89. content.userInfo = userInfo;
  90. }
  91. break;
  92. case kNCLocalNotificationTypeFailedSendChat:
  93. {
  94. NSString *failedSendChatString = NSLocalizedString(@"Failed to send message", nil);
  95. content.body = [NSString stringWithFormat:@"%@", failedSendChatString];
  96. content.userInfo = userInfo;
  97. }
  98. break;
  99. case kNCLocalNotificationTypeCallFromOldAccount:
  100. {
  101. NSString *receivedCallFromOldAccountString = NSLocalizedString(@"Received call from an old account", nil);
  102. content.body = [NSString stringWithFormat:@"%@", receivedCallFromOldAccountString];
  103. content.userInfo = userInfo;
  104. }
  105. break;
  106. case kNCLocalNotificationTypeFailedToShareRecording:
  107. {
  108. NSString *failedToShareRecordingString = NSLocalizedString(@"Failed to share recording", nil);
  109. content.body = [NSString stringWithFormat:@"%@", failedToShareRecordingString];
  110. content.userInfo = userInfo;
  111. }
  112. break;
  113. case kNCLocalNotificationTypeFailedToAcceptInvitation:
  114. {
  115. NSString *failedToAcceptInvitationString = NSLocalizedString(@"Failed to accept invitation", nil);
  116. content.body = [NSString stringWithFormat:@"%@", failedToAcceptInvitationString];
  117. content.userInfo = userInfo;
  118. }
  119. break;
  120. case kNCLocalNotificationTypeRecordingConsentRequired:
  121. {
  122. NSString *consentRequiredString = NSLocalizedString(@"Recording consent required for joining the call", nil);
  123. content.body = [NSString stringWithFormat:@"⚠️ %@ %@", consentRequiredString, [userInfo objectForKey:@"displayName"]];
  124. content.userInfo = userInfo;
  125. }
  126. break;
  127. default:
  128. break;
  129. }
  130. NSString *identifier = [NSString stringWithFormat:@"Notification-%f", [[NSDate date] timeIntervalSince1970]];
  131. UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.1 repeats:NO];
  132. UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];
  133. [_notificationCenter addNotificationRequest:request withCompletionHandler:nil];
  134. NSString *accountId = [userInfo objectForKey:@"accountId"];
  135. [[NCDatabaseManager sharedInstance] increaseUnreadBadgeNumberForAccountId:accountId];
  136. [self updateAppIconBadgeNumber];
  137. }
  138. - (void)showLocalNotificationForIncomingCallWithPushNotificaion:(NCPushNotification *)pushNotification
  139. {
  140. UNMutableNotificationContent *content = [UNMutableNotificationContent new];
  141. content.body = pushNotification.bodyForRemoteAlerts;
  142. content.threadIdentifier = pushNotification.roomToken;
  143. content.sound = [UNNotificationSound defaultSound];
  144. NSMutableDictionary *userInfo = [[NSMutableDictionary alloc] init];
  145. [userInfo setObject:pushNotification.jsonString forKey:@"pushNotification"];
  146. [userInfo setObject:pushNotification.accountId forKey:@"accountId"];
  147. [userInfo setObject:@(pushNotification.notificationId) forKey:@"notificationId"];
  148. content.userInfo = userInfo;
  149. NSString *identifier = [NSString stringWithFormat:@"Notification-%f", [[NSDate date] timeIntervalSince1970]];
  150. UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.1 repeats:NO];
  151. UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];
  152. [_notificationCenter addNotificationRequest:request withCompletionHandler:nil];
  153. [[NCDatabaseManager sharedInstance] increaseUnreadBadgeNumberForAccountId:pushNotification.accountId];
  154. [self updateAppIconBadgeNumber];
  155. }
  156. - (void)showIncomingCallForPushNotification:(NCPushNotification *)pushNotification
  157. {
  158. if ([CallKitManager isCallKitAvailable]) {
  159. [[CallKitManager sharedInstance] reportIncomingCall:pushNotification.roomToken withDisplayName:@"Incoming call" forAccountId:pushNotification.accountId];
  160. } else {
  161. [[CallKitManager sharedInstance] reportIncomingCallForNonCallKitDevicesWithPushNotification:pushNotification];
  162. }
  163. }
  164. - (void)showIncomingCallForOldAccount
  165. {
  166. [[CallKitManager sharedInstance] reportIncomingCallForOldAccount];
  167. }
  168. - (void)showLocalNotificationForChatNotification:(NCNotification *)notification forAccountId:(NSString *)accountId
  169. {
  170. UNMutableNotificationContent *content = [UNMutableNotificationContent new];
  171. content.title = notification.chatMessageTitle;
  172. content.body = notification.message;
  173. content.summaryArgument = notification.chatMessageAuthor;
  174. content.threadIdentifier = notification.roomToken;
  175. content.sound = [UNNotificationSound defaultSound];
  176. // Currently not supported for local notifications
  177. //content.categoryIdentifier = @"CATEGORY_CHAT";
  178. NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:notification.roomToken forKey:@"roomToken"];
  179. [userInfo setObject:accountId forKey:@"accountId"];
  180. [userInfo setObject:@(notification.notificationId) forKey:@"notificationId"];
  181. [userInfo setValue:@(kNCLocalNotificationTypeChatNotification) forKey:@"localNotificationType"];
  182. content.userInfo = userInfo;
  183. NSString *identifier = [NSString stringWithFormat:@"ChatNotification-%ld", notification.notificationId];
  184. UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.1 repeats:NO];
  185. UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:identifier content:content trigger:trigger];
  186. [_notificationCenter addNotificationRequest:request withCompletionHandler:nil];
  187. [[NCDatabaseManager sharedInstance] increaseUnreadBadgeNumberForAccountId:accountId];
  188. [self updateAppIconBadgeNumber];
  189. }
  190. - (void)updateAppIconBadgeNumber
  191. {
  192. dispatch_async(dispatch_get_main_queue(), ^{
  193. [UIApplication sharedApplication].applicationIconBadgeNumber = [[NCDatabaseManager sharedInstance] numberOfUnreadNotifications];
  194. });
  195. }
  196. - (void)removeAllNotificationsForAccountId:(NSString *)accountId
  197. {
  198. // Check in pending notifications
  199. [_notificationCenter getPendingNotificationRequestsWithCompletionHandler:^(NSArray<UNNotificationRequest *> * _Nonnull requests) {
  200. for (UNNotificationRequest *notificationRequest in requests) {
  201. NSString *notificationAccountId = [notificationRequest.content.userInfo objectForKey:@"accountId"];
  202. if (notificationAccountId && [notificationAccountId isEqualToString:accountId]) {
  203. [self->_notificationCenter removePendingNotificationRequestsWithIdentifiers:@[notificationRequest.identifier]];
  204. }
  205. }
  206. }];
  207. // Check in delivered notifications
  208. [_notificationCenter getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
  209. for (UNNotification *notification in notifications) {
  210. NSString *notificationAccountId = [notification.request.content.userInfo objectForKey:@"accountId"];
  211. if (notificationAccountId && [notificationAccountId isEqualToString:accountId]) {
  212. [self->_notificationCenter removeDeliveredNotificationsWithIdentifiers:@[notification.request.identifier]];
  213. }
  214. }
  215. }];
  216. [[NCDatabaseManager sharedInstance] resetUnreadBadgeNumberForAccountId:accountId];
  217. [self updateAppIconBadgeNumber];
  218. }
  219. - (void)removeNotificationWithNotificationIds:(NSArray *)notificationIds forAccountId:(NSString *)accountId
  220. {
  221. if (!notificationIds) {
  222. return;
  223. }
  224. void(^removeNotification)(UNNotificationRequest *, BOOL) = ^(UNNotificationRequest *notificationRequest, BOOL isPending) {
  225. NSString *notificationAccountId = [notificationRequest.content.userInfo objectForKey:@"accountId"];
  226. NSInteger notificationId = [[notificationRequest.content.userInfo objectForKey:@"notificationId"] integerValue];
  227. if (![notificationAccountId isEqualToString:accountId]) {
  228. return;
  229. }
  230. if ([notificationIds containsObject:@(notificationId)]) {
  231. if (isPending) {
  232. [self->_notificationCenter removePendingNotificationRequestsWithIdentifiers:@[notificationRequest.identifier]];
  233. } else {
  234. [self->_notificationCenter removeDeliveredNotificationsWithIdentifiers:@[notificationRequest.identifier]];
  235. }
  236. [[NCDatabaseManager sharedInstance] decreaseUnreadBadgeNumberForAccountId:accountId];
  237. }
  238. };
  239. __block BOOL expired = NO;
  240. BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"decreaseUnreadBadgeNumberForAccountId" expirationHandler:^(BGTaskHelper *task) {
  241. expired = YES;
  242. }];
  243. dispatch_group_t notificationsGroup = dispatch_group_create();
  244. dispatch_group_enter(notificationsGroup);
  245. // Check in pending notifications
  246. [_notificationCenter getPendingNotificationRequestsWithCompletionHandler:^(NSArray<UNNotificationRequest *> * _Nonnull requests) {
  247. for (UNNotificationRequest *notificationRequest in requests) {
  248. if (expired) {
  249. dispatch_group_leave(notificationsGroup);
  250. return;
  251. }
  252. removeNotification(notificationRequest, YES);
  253. }
  254. [self updateAppIconBadgeNumber];
  255. dispatch_group_leave(notificationsGroup);
  256. }];
  257. dispatch_group_enter(notificationsGroup);
  258. // Check in delivered notifications
  259. [_notificationCenter getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
  260. for (UNNotification *notification in notifications) {
  261. if (expired) {
  262. dispatch_group_leave(notificationsGroup);
  263. return;
  264. }
  265. removeNotification(notification.request, NO);
  266. }
  267. [self updateAppIconBadgeNumber];
  268. dispatch_group_leave(notificationsGroup);
  269. }];
  270. dispatch_group_notify(notificationsGroup, dispatch_get_main_queue(), ^{
  271. [bgTask stopBackgroundTask];
  272. });
  273. }
  274. - (void)checkForNewNotificationsWithCompletionBlock:(CheckForNewNotificationsCompletionBlock)block
  275. {
  276. dispatch_group_t notificationsGroup = dispatch_group_create();
  277. for (TalkAccount *account in [[NCDatabaseManager sharedInstance] allAccounts]) {
  278. ServerCapabilities *serverCapabilities = [[NCDatabaseManager sharedInstance] serverCapabilitiesForAccountId:account.accountId];
  279. if (!serverCapabilities || [serverCapabilities.notificationsCapabilities count] == 0) {
  280. continue;
  281. }
  282. dispatch_group_enter(notificationsGroup);
  283. [[NCAPIController sharedInstance] getServerNotificationsForAccount:account withLastETag:account.lastNotificationETag withCompletionBlock:^(NSArray *notifications, NSString* ETag, NSString *userStatus, NSError *error) {
  284. if (error) {
  285. dispatch_group_leave(notificationsGroup);
  286. return;
  287. }
  288. // Don't show notifications if the user has status "do not disturb"
  289. BOOL suppressNotifications = (serverCapabilities.userStatus && [userStatus isEqualToString:kUserStatusDND]);
  290. NSInteger lastNotificationId = 0;
  291. NSMutableArray *activeServerNotificationsIds = [NSMutableArray new];
  292. for (NSDictionary *notification in notifications) {
  293. NCNotification *serverNotification = [NCNotification notificationWithDictionary:notification];
  294. // Only process Talk notifications
  295. if (!serverNotification || ![serverNotification.app isEqualToString:kNCPNAppIdKey]) {
  296. continue;
  297. }
  298. [activeServerNotificationsIds addObject:@(serverNotification.notificationId)];
  299. if (lastNotificationId < serverNotification.notificationId) {
  300. lastNotificationId = serverNotification.notificationId;
  301. }
  302. if (suppressNotifications || serverNotification.notificationType != kNCNotificationTypeChat) {
  303. continue;
  304. }
  305. if (account.lastNotificationId != 0 && serverNotification.notificationId > account.lastNotificationId) {
  306. // Don't show notifications if this is the first time we retrieve notifications for this account
  307. // Otherwise after adding a new account all unread notifications from the server would be shown
  308. [self showLocalNotificationForChatNotification:serverNotification forAccountId:account.accountId];
  309. }
  310. }
  311. RLMRealm *realm = [RLMRealm defaultRealm];
  312. [realm transactionWithBlock:^{
  313. NSPredicate *query = [NSPredicate predicateWithFormat:@"accountId = %@", account.accountId];
  314. TalkAccount *managedAccount = [TalkAccount objectsWithPredicate:query].firstObject;
  315. managedAccount.lastNotificationETag = ETag;
  316. if (managedAccount.lastNotificationId < lastNotificationId) {
  317. managedAccount.lastNotificationId = lastNotificationId;
  318. }
  319. }];
  320. // Remove notifications that have been treated for the server
  321. [self->_notificationCenter getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
  322. for (UNNotification *notification in notifications) {
  323. NSString *notificationAccountId = [notification.request.content.userInfo objectForKey:@"accountId"];
  324. NSInteger notificationIdentifier = [[notification.request.content.userInfo objectForKey:@"notificationId"]
  325. integerValue];
  326. NCLocalNotificationType localNotificationType = (NCLocalNotificationType)[[notification.request.content.userInfo objectForKey:@"localNotificationType"] integerValue];
  327. if ([notificationAccountId isEqualToString:account.accountId] && ![activeServerNotificationsIds containsObject:@(notificationIdentifier)] && (localNotificationType == 0 || localNotificationType == kNCLocalNotificationTypeChatNotification)) {
  328. [self->_notificationCenter removeDeliveredNotificationsWithIdentifiers:@[notification.request.identifier]];
  329. [[NCDatabaseManager sharedInstance] decreaseUnreadBadgeNumberForAccountId:account.accountId];
  330. }
  331. }
  332. [self updateAppIconBadgeNumber];
  333. dispatch_group_leave(notificationsGroup);
  334. }];
  335. }];
  336. }
  337. dispatch_group_notify(notificationsGroup, dispatch_get_main_queue(), ^{
  338. // Notify backgroundFetch that we're finished
  339. if (block) {
  340. block(nil);
  341. }
  342. });
  343. }
  344. - (void)checkNotificationExistanceWithCompletionBlock:(CheckNotificationExistanceCompletionBlock)block
  345. {
  346. dispatch_group_t notificationsGroup = dispatch_group_create();
  347. for (TalkAccount *account in [[NCDatabaseManager sharedInstance] allAccounts]) {
  348. if (![[NCDatabaseManager sharedInstance] serverHasNotificationsCapability:kNotificationsCapabilityExists forAccountId:account.accountId]) {
  349. continue;
  350. }
  351. dispatch_group_enter(notificationsGroup);
  352. [_notificationCenter getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> * _Nonnull notifications) {
  353. NSMutableArray *notificationIdsOnDevice = [[NSMutableArray alloc] init];
  354. for (UNNotification *notification in notifications) {
  355. UNNotificationRequest *notificationRequest = notification.request;
  356. NSString *notificationAccountId = [notificationRequest.content.userInfo objectForKey:@"accountId"];
  357. NSInteger notificationId = [[notificationRequest.content.userInfo objectForKey:@"notificationId"] integerValue];
  358. if (![notificationAccountId isEqualToString:account.accountId]) {
  359. continue;
  360. }
  361. [notificationIdsOnDevice addObject:@(notificationId)];
  362. }
  363. if ([notificationIdsOnDevice count] == 0) {
  364. // No notifications for this account are currently shown on the system -> no need to check anything
  365. dispatch_group_leave(notificationsGroup);
  366. return;
  367. }
  368. [[NCAPIController sharedInstance] checkNotificationExistance:notificationIdsOnDevice forAccount:account withCompletionBlock:^(NSArray *notificationIds, NSError *error) {
  369. if (error) {
  370. dispatch_group_leave(notificationsGroup);
  371. return;
  372. }
  373. // Remove all notificationIds which are still on the server
  374. for (id notificationId in notificationIds) {
  375. [notificationIdsOnDevice removeObject:notificationId];
  376. }
  377. // In case there are still notifications on the device (that are not on the server anymore) remove them
  378. if ([notificationIdsOnDevice count] > 0) {
  379. [self removeNotificationWithNotificationIds:notificationIdsOnDevice forAccountId:account.accountId];
  380. }
  381. dispatch_group_leave(notificationsGroup);
  382. }];
  383. }];
  384. }
  385. dispatch_group_notify(notificationsGroup, dispatch_get_main_queue(), ^{
  386. // Notify backgroundFetch that we're finished
  387. if (block) {
  388. block(nil);
  389. }
  390. });
  391. }
  392. #pragma mark - UNUserNotificationCenter delegate
  393. - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
  394. {
  395. // Called when a notification is delivered to a foreground app.
  396. [[NSNotificationCenter defaultCenter] postNotificationName:NCNotificationControllerWillPresentNotification object:self userInfo:nil];
  397. completionHandler(UNNotificationPresentationOptionList | UNNotificationPresentationOptionBanner);
  398. // Remove the notification from Notification Center if it is from the active account
  399. NSString *notificationAccountId = [notification.request.content.userInfo objectForKey:@"accountId"];
  400. if (notificationAccountId && [[[NCDatabaseManager sharedInstance] activeAccount].accountId isEqualToString:notificationAccountId]) {
  401. [self removeAllNotificationsForAccountId:notificationAccountId];
  402. }
  403. }
  404. - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler
  405. {
  406. UNNotificationRequest *notificationRequest = response.notification.request;
  407. NSDictionary *userInfo = notificationRequest.content.userInfo;
  408. NCLocalNotificationType localNotificationType = (NCLocalNotificationType)[[userInfo objectForKey:@"localNotificationType"] integerValue];
  409. NSString *notificationString = [userInfo objectForKey:@"pushNotification"];
  410. NSString *notificationAccountId = [userInfo objectForKey:@"accountId"];
  411. NCPushNotification *pushNotification = [NCPushNotification pushNotificationFromDecryptedString:notificationString withAccountId:notificationAccountId];
  412. // Handle notification response
  413. if (pushNotification) {
  414. if ([response isKindOfClass:[UNTextInputNotificationResponse class]]) {
  415. UNTextInputNotificationResponse *textInputResponse = (UNTextInputNotificationResponse *)response;
  416. pushNotification.responseUserText = textInputResponse.userText;
  417. [self handlePushNotificationResponseWithUserText:pushNotification];
  418. } else if (pushNotification.type == NCPushNotificationTypeRecording) {
  419. [self handlePushNotificationResponseForRecording:response];
  420. } else if (pushNotification.type == NCPUshNotificationTypeFederation) {
  421. [self handlePushNotificationResponseForFederation:response];
  422. } else if (pushNotification.type == NCPushNotificationTypeReminder) {
  423. [self handlePushNotificationResponseForReminder:response];
  424. } else {
  425. [self handlePushNotificationResponse:pushNotification];
  426. }
  427. } else if (localNotificationType > 0) {
  428. [self handleLocalNotificationResponse:notificationRequest.content.userInfo];
  429. }
  430. completionHandler();
  431. }
  432. - (void)handlePushNotificationResponseWithUserText:(NCPushNotification *)pushNotification
  433. {
  434. NSLog(@"Recevied push-notification with user input -> sending chat message");
  435. TalkAccount *pushAccount = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:pushNotification.accountId];
  436. UIApplication *application = [UIApplication sharedApplication];
  437. __block UIBackgroundTaskIdentifier sendTask = [application beginBackgroundTaskWithExpirationHandler:^{
  438. [application endBackgroundTask:sendTask];
  439. sendTask = UIBackgroundTaskInvalid;
  440. }];
  441. dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
  442. [[NCAPIController sharedInstance] sendChatMessage:pushNotification.responseUserText toRoom:pushNotification.roomToken displayName:nil replyTo:-1 referenceId:nil silently:NO forAccount:pushAccount withCompletionBlock:^(NSError *error) {
  443. if (error) {
  444. NSLog(@"Could not send chat message. Error: %@", error.description);
  445. // Display local push-notification to inform user
  446. NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:pushNotification.roomToken forKey:@"roomToken"];
  447. [userInfo setValue:@(kNCLocalNotificationTypeFailedSendChat) forKey:@"localNotificationType"];
  448. [userInfo setObject:pushNotification.accountId forKey:@"accountId"];
  449. [userInfo setObject:pushNotification.responseUserText forKey:@"responseUserText"];
  450. [[NCNotificationController sharedInstance] showLocalNotification:kNCLocalNotificationTypeFailedSendChat withUserInfo:userInfo];
  451. } else {
  452. // We replied to the message, so we can assume, we read it as well
  453. [[NCDatabaseManager sharedInstance] decreaseUnreadBadgeNumberForAccountId:pushNotification.accountId];
  454. [self updateAppIconBadgeNumber];
  455. NCRoom *room = [[NCDatabaseManager sharedInstance] roomWithToken:pushNotification.roomToken forAccountId:pushNotification.accountId];
  456. if (room) {
  457. [[NCIntentController sharedInstance] donateSendMessageIntentForRoom:room];
  458. }
  459. }
  460. [application endBackgroundTask:sendTask];
  461. sendTask = UIBackgroundTaskInvalid;
  462. }];
  463. });
  464. }
  465. - (void)handlePushNotificationResponseForFederation:(UNNotificationResponse *)response
  466. {
  467. BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"handlePushNotificationResponseForFederation" expirationHandler:^(BGTaskHelper *task) {
  468. [NCUtils log:@"ExpirationHandler called - handlePushNotificationResponseForFederation"];
  469. }];
  470. UNNotificationRequest *notificationRequest = response.notification.request;
  471. NSDictionary *userInfo = notificationRequest.content.userInfo;
  472. NSString *notificationAccountId = [userInfo objectForKey:@"accountId"];
  473. NSDictionary *serverNotificationDict = [userInfo objectForKey:@"serverNotification"];
  474. TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:notificationAccountId];
  475. NCNotification *serverNotification = [NCNotification notificationWithDictionary:serverNotificationDict];
  476. if (!account || !serverNotification) {
  477. [bgTask stopBackgroundTask];
  478. return;
  479. }
  480. if ([response.actionIdentifier isEqualToString:NCNotificationActionFederationInvitationAccept]) {
  481. FederationInvitation *invitation = [[FederationInvitation alloc] initWithNotification:serverNotification for:account.accountId];
  482. [[NCAPIController sharedInstance] acceptFederationInvitationFor:account.accountId with:invitation.invitationId completionBlock:^(BOOL success) {
  483. if (!success) {
  484. NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:serverNotification.roomToken forKey:@"roomToken"];
  485. [userInfo setValue:@(kNCLocalNotificationTypeFailedToAcceptInvitation) forKey:@"localNotificationType"];
  486. [userInfo setObject:notificationAccountId forKey:@"accountId"];
  487. [self showLocalNotification:kNCLocalNotificationTypeFailedToAcceptInvitation withUserInfo:userInfo];
  488. }
  489. [[NCDatabaseManager sharedInstance] decreasePendingFederationInvitationForAccountId:account.accountId];
  490. [bgTask stopBackgroundTask];
  491. }];
  492. } else if ([response.actionIdentifier isEqualToString:NCNotificationActionFederationInvitationReject]) {
  493. FederationInvitation *invitation = [[FederationInvitation alloc] initWithNotification:serverNotification for:account.accountId];
  494. [[NCAPIController sharedInstance] rejectFederationInvitationFor:account.accountId with:invitation.invitationId completionBlock:^(BOOL success) {
  495. [[NCDatabaseManager sharedInstance] decreasePendingFederationInvitationForAccountId:account.accountId];
  496. [bgTask stopBackgroundTask];
  497. }];
  498. } else {
  499. [bgTask stopBackgroundTask];
  500. UIAlertController *alert = [UIAlertController alertControllerWithTitle:serverNotification.subject
  501. message:serverNotification.message
  502. preferredStyle:UIAlertControllerStyleAlert];
  503. for (NCNotificationAction *notificationAction in [serverNotification notificationActions]) {
  504. UIAlertAction* tempButton = [UIAlertAction actionWithTitle:notificationAction.actionLabel
  505. style:UIAlertActionStyleDefault
  506. handler:^(UIAlertAction * _Nonnull action) {
  507. [[NCDatabaseManager sharedInstance] decreasePendingFederationInvitationForAccountId:account.accountId];
  508. [[NCAPIController sharedInstance] executeNotificationAction:notificationAction forAccount:account withCompletionBlock:nil];
  509. }];
  510. [alert addAction:tempButton];
  511. }
  512. dispatch_async(dispatch_get_main_queue(), ^{
  513. [[NCUserInterfaceController sharedInstance] presentAlertViewController:alert];
  514. });
  515. }
  516. }
  517. - (void)handlePushNotificationResponseForRecording:(UNNotificationResponse *)response
  518. {
  519. BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"handlePushNotificationResponseForRecording" expirationHandler:^(BGTaskHelper *task) {
  520. [NCUtils log:@"ExpirationHandler called - handlePushNotificationResponseForRecording"];
  521. }];
  522. UNNotificationRequest *notificationRequest = response.notification.request;
  523. NSDictionary *userInfo = notificationRequest.content.userInfo;
  524. NSString *notificationAccountId = [userInfo objectForKey:@"accountId"];
  525. NSDictionary *serverNotificationDict = [userInfo objectForKey:@"serverNotification"];
  526. TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:notificationAccountId];
  527. NCNotification *serverNotification = [NCNotification notificationWithDictionary:serverNotificationDict];
  528. if (!account || !serverNotification) {
  529. [bgTask stopBackgroundTask];
  530. return;
  531. }
  532. NSTimeInterval notificationTimeInterval = [serverNotification.datetime timeIntervalSince1970];
  533. NSString *notificationTimestamp = [NSString stringWithFormat:@"%.0f", notificationTimeInterval];
  534. if ([response.actionIdentifier isEqualToString:NCNotificationActionShareRecording]) {
  535. NSDictionary *fileParameters = [serverNotification.messageRichParameters objectForKey:@"file"];
  536. if (!fileParameters || ![fileParameters objectForKey:@"id"]) {
  537. [bgTask stopBackgroundTask];
  538. return;
  539. }
  540. NSString *fileId = [fileParameters objectForKey:@"id"];
  541. [[NCAPIController sharedInstance] shareStoredRecordingWithTimestamp:notificationTimestamp
  542. withFileId:fileId
  543. forRoom:serverNotification.roomToken
  544. forAccount:account
  545. withCompletionBlock:^(NSError *error) {
  546. if (error) {
  547. NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:serverNotification.roomToken forKey:@"roomToken"];
  548. [userInfo setValue:@(kNCLocalNotificationTypeFailedToShareRecording) forKey:@"localNotificationType"];
  549. [userInfo setObject:notificationAccountId forKey:@"accountId"];
  550. [self showLocalNotification:kNCLocalNotificationTypeFailedToShareRecording withUserInfo:userInfo];
  551. }
  552. [bgTask stopBackgroundTask];
  553. }];
  554. } else if ([response.actionIdentifier isEqualToString:NCNotificationActionDismissRecordingNotification]) {
  555. [[NCAPIController sharedInstance] dismissStoredRecordingNotificationWithTimestamp:notificationTimestamp
  556. forRoom:serverNotification.roomToken
  557. forAccount:account
  558. withCompletionBlock:^(NSError *error) {
  559. [bgTask stopBackgroundTask];
  560. }];
  561. } else {
  562. [bgTask stopBackgroundTask];
  563. UIAlertController *alert = [UIAlertController alertControllerWithTitle:serverNotification.subject
  564. message:serverNotification.message
  565. preferredStyle:UIAlertControllerStyleAlert];
  566. for (NCNotificationAction *notificationAction in [serverNotification notificationActions]) {
  567. UIAlertAction* tempButton = [UIAlertAction actionWithTitle:notificationAction.actionLabel
  568. style:UIAlertActionStyleDefault
  569. handler:^(UIAlertAction * _Nonnull action) {
  570. [[NCAPIController sharedInstance] executeNotificationAction:notificationAction forAccount:account withCompletionBlock:nil];
  571. }];
  572. [alert addAction:tempButton];
  573. }
  574. dispatch_async(dispatch_get_main_queue(), ^{
  575. [[NCUserInterfaceController sharedInstance] presentAlertViewController:alert];
  576. });
  577. }
  578. }
  579. - (void)handlePushNotificationResponseForReminder:(UNNotificationResponse *)response
  580. {
  581. if ([NCRoomsManager sharedInstance].callViewController) {
  582. return;
  583. }
  584. BGTaskHelper *bgTask = [BGTaskHelper startBackgroundTaskWithName:@"handlePushNotificationResponseForReminder" expirationHandler:^(BGTaskHelper *task) {
  585. [NCUtils log:@"ExpirationHandler called - handlePushNotificationResponseForReminder"];
  586. }];
  587. UNNotificationRequest *notificationRequest = response.notification.request;
  588. NSDictionary *userInfo = notificationRequest.content.userInfo;
  589. NSString *notificationAccountId = [userInfo objectForKey:@"accountId"];
  590. NSDictionary *serverNotificationDict = [userInfo objectForKey:@"serverNotification"];
  591. TalkAccount *account = [[NCDatabaseManager sharedInstance] talkAccountForAccountId:notificationAccountId];
  592. NCNotification *serverNotification = [NCNotification notificationWithDictionary:serverNotificationDict];
  593. if (!account || !serverNotification) {
  594. [bgTask stopBackgroundTask];
  595. return;
  596. }
  597. // Open the conversation for the reminder
  598. [[NCRoomsManager sharedInstance] startChatWithRoomToken:serverNotification.roomToken];
  599. // After opening the notification, we need to execute the DELETE action
  600. for (NSDictionary *dict in serverNotification.actions) {
  601. NCNotificationAction *notificationAction = [[NCNotificationAction alloc] initWithDictionary:dict];
  602. if (notificationAction && notificationAction.actionType == NCNotificationActionTypeKNotificationActionTypeDelete) {
  603. [[NCAPIController sharedInstance] executeNotificationAction:notificationAction forAccount:account withCompletionBlock:nil];
  604. }
  605. }
  606. }
  607. - (void)handlePushNotificationResponse:(NCPushNotification *)pushNotification
  608. {
  609. if ([NCRoomsManager sharedInstance].callViewController) {
  610. return;
  611. }
  612. if (pushNotification) {
  613. switch (pushNotification.type) {
  614. case NCPushNotificationTypeCall:
  615. {
  616. [[NCUserInterfaceController sharedInstance] presentAlertForPushNotification:pushNotification];
  617. }
  618. break;
  619. case NCPushNotificationTypeRoom:
  620. case NCPushNotificationTypeChat:
  621. {
  622. [[NCUserInterfaceController sharedInstance] presentChatForPushNotification:pushNotification];
  623. }
  624. break;
  625. default:
  626. break;
  627. }
  628. }
  629. }
  630. - (void)handleLocalNotificationResponse:(NSDictionary *)notificationUserInfo
  631. {
  632. if ([NCRoomsManager sharedInstance].callViewController) {
  633. return;
  634. }
  635. NCLocalNotificationType localNotificationType = (NCLocalNotificationType)[[notificationUserInfo objectForKey:@"localNotificationType"] integerValue];
  636. if (localNotificationType > 0) {
  637. switch (localNotificationType) {
  638. case kNCLocalNotificationTypeMissedCall:
  639. case kNCLocalNotificationTypeCancelledCall:
  640. case kNCLocalNotificationTypeFailedSendChat:
  641. case kNCLocalNotificationTypeChatNotification:
  642. case kNCLocalNotificationTypeRecordingConsentRequired:
  643. {
  644. [[NCUserInterfaceController sharedInstance] presentChatForLocalNotification:notificationUserInfo];
  645. }
  646. break;
  647. case kNCLocalNotificationTypeCallFromOldAccount:
  648. {
  649. [[NCUserInterfaceController sharedInstance] presentSettingsViewController];
  650. }
  651. break;
  652. default:
  653. break;
  654. }
  655. }
  656. }
  657. @end