Factorize and fix some issues with local notifications code

This commit is contained in:
SBiOSoftWhare 2019-03-22 10:21:16 +01:00
parent 93af475cec
commit 9b41cfc6fd

View file

@ -1163,8 +1163,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
else
{
// Clear existing token
MXKAccountManager* accountManager = [MXKAccountManager sharedManager];
[accountManager setPushDeviceToken:nil withPushOptions:nil];
[self clearPushNotificationToken];
}
}];
}
@ -1211,8 +1210,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
else
{
// Clear existing token
MXKAccountManager* accountManager = [MXKAccountManager sharedManager];
[accountManager setPushDeviceToken:nil withPushOptions:nil];
[self clearPushNotificationToken];
}
}
@ -1221,126 +1219,82 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
{
UNNotification *notification = response.notification;
UNNotificationContent *content = notification.request.content;
if ([[response actionIdentifier] isEqualToString:@"inline-reply"]) {
UNTextInputNotificationResponse *textResponse = (UNTextInputNotificationResponse *) response;
NSString *actionIdentifier = [response actionIdentifier];
NSString *roomId = content.userInfo[@"room_id"];
if (roomId.length) {
NSArray* mxAccounts = [MXKAccountManager sharedManager].activeAccounts;
if ([actionIdentifier isEqualToString:@"inline-reply"])
{
if ([response isKindOfClass:[UNTextInputNotificationResponse class]])
{
UNTextInputNotificationResponse *textInputNotificationResponse = (UNTextInputNotificationResponse *)response;
NSString *responseText = [textInputNotificationResponse userText];
[self handleNotificationInlineReplyForRoomId:roomId withResponseText:responseText success:^(NSString *eventId) {
completionHandler();
} failure:^(NSError *error) {
MXKRoomDataSourceManager* manager;
for (MXKAccount* account in mxAccounts)
{
MXRoom* room = [account.mxSession roomWithRoomId:roomId];
if (room)
{
manager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:account.mxSession];
if (manager)
{
break;
}
}
}
if (manager == nil)
{
NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: room with id %@ not found", roomId);
}
else
{
[manager roomDataSourceForRoom:roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) {
NSString* responseText = [textResponse userText];
if (responseText != nil && responseText.length != 0)
{
NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: sending message to room: %@", roomId);
[roomDataSource sendTextMessage:responseText success:^(NSString* eventId) {} failure:^(NSError* error) {
UNMutableNotificationContent *failureNotificationContent = [[UNMutableNotificationContent alloc] init];
failureNotificationContent.userInfo = content.userInfo;
failureNotificationContent.body = NSLocalizedStringFromTable(@"room_event_failed_to_send", @"Vector", nil);
UNNotificationRequest *failureNotificationRequest = [UNNotificationRequest
requestWithIdentifier:@"failureNotification"
NSString *uuid = [[NSUUID UUID] UUIDString];
UNNotificationRequest *failureNotificationRequest = [UNNotificationRequest requestWithIdentifier:uuid
content:failureNotificationContent
trigger:nil];
[center addNotificationRequest:failureNotificationRequest withCompletionHandler:nil];
NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: error sending text message: %@", error);
}];
}
completionHandler();
}];
return;
}
}
}
else if ([[response actionIdentifier] isEqualToString:UNNotificationDefaultActionIdentifier])
else
{
NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: error, expect a response of type UNTextInputNotificationResponse");
completionHandler();
}
}
else if ([actionIdentifier isEqualToString:UNNotificationDefaultActionIdentifier])
{
NSString *roomId = content.userInfo[@"room_id"];
[self navigateToRoomById:roomId];
completionHandler();
}
else
{
NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: unhandled identifier %@", [response actionIdentifier]);
}
completionHandler();
}
}
// DEPRECATED, for iOS 9
// "This block is not a prototype" - don't fix this, or it won't match Apple's definition
- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forLocalNotification:(UILocalNotification *)notification withResponseInfo:(NSDictionary *)responseInfo completionHandler:(void (^)())completionHandler
{
NSString* roomId = notification.userInfo[@"room_id"];
if ([identifier isEqualToString: @"inline-reply"])
{
NSString* roomId = notification.userInfo[@"room_id"];
if (roomId.length)
{
NSArray* mxAccounts = [MXKAccountManager sharedManager].activeAccounts;
MXKRoomDataSource* roomDataSource = nil;
MXKRoomDataSourceManager* manager;
for (MXKAccount* account in mxAccounts)
{
MXRoom* room = [account.mxSession roomWithRoomId:roomId];
if (room)
{
manager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:account.mxSession];
if (manager)
{
break;
}
}
}
if (manager == nil)
{
NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: room with id %@ not found", roomId);
}
else
{
[manager roomDataSourceForRoom:roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) {
NSString* responseText = responseInfo[UIUserNotificationActionResponseTypedTextKey];
if (responseText != nil && responseText.length != 0)
{
NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: sending message to room: %@", roomId);
[roomDataSource sendTextMessage:responseText success:^(NSString* eventId) {} failure:^(NSError* error) {
[self handleNotificationInlineReplyForRoomId:roomId withResponseText:responseText success:^(NSString *eventId) {
completionHandler();
} failure:^(NSError *error) {
UILocalNotification* failureNotification = [[UILocalNotification alloc] init];
failureNotification.alertBody = NSLocalizedStringFromTable(@"room_event_failed_to_send", @"Vector", nil);
failureNotification.userInfo = notification.userInfo;
[[UIApplication sharedApplication] scheduleLocalNotification: failureNotification];
NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: error sending text message: %@", error);
}];
}
completionHandler();
}];
return;
}
}
}
else
{
NSLog(@"[AppDelegate][Push] handleActionWithIdentifier: unhandled identifier %@", identifier);
}
completionHandler();
}
}
// iOS 10+, this is called when a notification is about to display in foreground.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
@ -1423,8 +1377,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
- (void)pushRegistry:(PKPushRegistry *)registry didInvalidatePushTokenForType:(PKPushType)type
{
MXKAccountManager* accountManager = [MXKAccountManager sharedManager];
[accountManager setPushDeviceToken:nil withPushOptions:nil];
[self clearPushNotificationToken];
}
- (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(PKPushType)type
@ -1533,7 +1486,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
// Ignore event already notified to the user
// only necessary on iOS 9, iOS 10 will just overwrite notifications with identical IDs
if (!@available(iOS 10, *) && [self displayedLocalNotificationForEvent:eventId andUser:account.mxCredentials.userId type:nil])
if (@available(iOS 10, *)) {}
else if ([self displayedLocalNotificationForEvent:eventId andUser:account.mxCredentials.userId type:nil])
{
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId);
continue;
@ -1627,62 +1581,55 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
}
}
if (@available(iOS 10, *)) {
[self notificationContentForEvent:event pushRule:rule inAccount:account onComplete:^(UNMutableNotificationContent * _Nullable notificationContent)
{
if (notificationContent)
{
// Printf style escape characters are stripped from the string prior to display;
// to include a percent symbol (%) in the message, use two percent symbols (%%).
// TODO: https://developer.apple.com/documentation/foundation/nsstring/1649585-localizedusernotificationstringf?language=objc
// use this - maybe not necessary to replace %s
notificationContent.body = [notificationContent.body stringByReplacingOccurrencesOfString:@"%" withString:@"%%"];
notificationContent.userInfo = notificationUserInfo;
notificationContent.categoryIdentifier = categoryIdentifier;
notificationContent.sound = [UNNotificationSound soundNamed:soundName];
UNNotificationRequest *request = [UNNotificationRequest
requestWithIdentifier:event.eventId
content:notificationContent
trigger:nil];
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId);
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil];
scheduledNotifications++;
}
else
{
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationContent. Event id: %@", event.eventId);
}
}];
}
else
{
[self notificationBodyForEvent:event pushRule:rule inAccount:account onComplete:^(NSString *_Nullable notificationBody)
{
if (notificationBody)
{
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId);
// Printf style escape characters are stripped from the string prior to display;
// to include a percent symbol (%) in the message, use two percent symbols (%%).
notificationBody = [notificationBody stringByReplacingOccurrencesOfString:@"%" withString:@"%%"];
// TODO: https://developer.apple.com/documentation/foundation/nsstring/1649585-localizedusernotificationstringf?language=objc
// use this - maybe not necessary to replace %s
NSString *fixedNotificationBody = [notificationBody stringByReplacingOccurrencesOfString:@"%" withString:@"%%"];
if (@available(iOS 10, *))
{
UNMutableNotificationContent *notificationContent = [[UNMutableNotificationContent alloc] init];
notificationContent.body = fixedNotificationBody;
notificationContent.userInfo = notificationUserInfo;
notificationContent.categoryIdentifier = categoryIdentifier;
if (soundName)
{
notificationContent.sound = [UNNotificationSound soundNamed:soundName];
}
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:event.eventId
content:notificationContent
trigger:nil];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil];
}
else
{
UILocalNotification *eventNotification = [[UILocalNotification alloc] init];
eventNotification.alertBody = notificationBody;
eventNotification.alertBody = fixedNotificationBody;
eventNotification.userInfo = notificationUserInfo;
eventNotification.category = categoryIdentifier;
eventNotification.soundName = soundName;
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId);
[[UIApplication sharedApplication] scheduleLocalNotification:eventNotification];
}
scheduledNotifications++;
} else
}
else
{
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationBody. Event id: %@", event.eventId);
}
}];
}
}
}
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Sent %tu local notifications for %tu events", scheduledNotifications, eventsArray.count);
@ -1815,136 +1762,6 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
}];
}
// iOS 10+, does the same thing as notificationBodyForEvent:pushRule:inAccount:onComplete: for now
- (void)notificationContentForEvent:(MXEvent *)event pushRule:(MXPushRule *)rule inAccount:(MXKAccount *)account onComplete:(void (^)(UNMutableNotificationContent * _Nullable notificationContent))onComplete;
{
if (!event.content || !event.content.count)
{
NSLog(@"[AppDelegate][Push] notificationContentForEvent: empty event content");
onComplete (nil);
return;
}
MXRoom *room = [account.mxSession roomWithRoomId:event.roomId];
if (!room)
{
NSLog(@"[AppDelegate][Push] notificationBodyForEvent: Unknown room");
onComplete (nil);
return;
}
[room state:^(MXRoomState *roomState) {
NSString *notificationBody;
NSString *eventSenderName = [roomState.members memberName:event.sender];
if (event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted)
{
if (room.isMentionsOnly)
{
// A local notification will be displayed only for highlighted notification.
BOOL isHighlighted = NO;
// Check whether is there an highlight tweak on it
for (MXPushRuleAction *ruleAction in rule.actions)
{
if (ruleAction.actionType == MXPushRuleActionTypeSetTweak)
{
if ([ruleAction.parameters[@"set_tweak"] isEqualToString:@"highlight"])
{
// Check the highlight tweak "value"
// If not present, highlight. Else check its value before highlighting
if (nil == ruleAction.parameters[@"value"] || YES == [ruleAction.parameters[@"value"] boolValue])
{
isHighlighted = YES;
break;
}
}
}
}
if (!isHighlighted)
{
// Ignore this notif.
NSLog(@"[AppDelegate][Push] notificationBodyForEvent: Ignore non highlighted notif in mentions only room");
onComplete(nil);
return;
}
}
NSString *msgType = event.content[@"msgtype"];
NSString *content = event.content[@"body"];
if (event.isEncrypted && !RiotSettings.shared.showDecryptedContentInNotifications)
{
// Hide the content
msgType = nil;
}
NSString *roomDisplayName = room.summary.displayname;
// Display the room name only if it is different than the sender name
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
{
if ([msgType isEqualToString:@"m.text"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM_WITH_CONTENT", nil), eventSenderName,roomDisplayName, content];
else if ([msgType isEqualToString:@"m.emote"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER_IN_ROOM", nil), roomDisplayName, eventSenderName, content];
else if ([msgType isEqualToString:@"m.image"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER_IN_ROOM", nil), eventSenderName, content, roomDisplayName];
else
// Encrypted messages falls here
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName];
}
else
{
if ([msgType isEqualToString:@"m.text"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_WITH_CONTENT", nil), eventSenderName, content];
else if ([msgType isEqualToString:@"m.emote"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER", nil), eventSenderName, content];
else if ([msgType isEqualToString:@"m.image"])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"IMAGE_FROM_USER", nil), eventSenderName, content];
else
// Encrypted messages falls here
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName];
}
}
else if (event.eventType == MXEventTypeCallInvite)
{
NSString *sdp = event.content[@"offer"][@"sdp"];
BOOL isVideoCall = [sdp rangeOfString:@"m=video"].location != NSNotFound;
if (!isVideoCall)
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VOICE_CALL_FROM_USER", nil), eventSenderName];
else
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"VIDEO_CALL_FROM_USER", nil), eventSenderName];
}
else if (event.eventType == MXEventTypeRoomMember)
{
NSString *roomDisplayName = room.summary.displayname;
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_NAMED_ROOM", nil), eventSenderName, roomDisplayName];
else
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"USER_INVITE_TO_CHAT", nil), eventSenderName];
}
else if (event.eventType == MXEventTypeSticker)
{
NSString *roomDisplayName = room.summary.displayname;
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM", nil), eventSenderName, roomDisplayName];
else
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER", nil), eventSenderName];
}
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
[content setBody:notificationBody];
onComplete(content);
}];
}
/**
Display "limited" notifications for events the app was not able to get data
(because of /sync failure).
@ -1972,7 +1789,8 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
{
// Ignore event already notified to the user
// only necessary on iOS 9, iOS 10 will just overwrite notifications with identical IDs
if (!@available(iOS 10, *) && [self displayedLocalNotificationForEvent:eventId andUser:userId type:nil])
if (@available(iOS 10, *)) {}
else if ([self displayedLocalNotificationForEvent:eventId andUser:userId type:nil])
{
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId);
continue;
@ -2099,6 +1917,66 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
[UIApplication sharedApplication].applicationIconBadgeNumber = count;
}
- (void)handleNotificationInlineReplyForRoomId:(NSString*)roomId
withResponseText:(NSString*)responseText
success:(void(^)(NSString *eventId))success
failure:(void(^)(NSError *error))failure
{
if (!roomId.length)
{
failure(nil);
return;
}
NSArray* mxAccounts = [MXKAccountManager sharedManager].activeAccounts;
MXKRoomDataSourceManager* manager;
for (MXKAccount* account in mxAccounts)
{
MXRoom* room = [account.mxSession roomWithRoomId:roomId];
if (room)
{
manager = [MXKRoomDataSourceManager sharedManagerForMatrixSession:account.mxSession];
if (manager)
{
break;
}
}
}
if (manager == nil)
{
NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: room with id %@ not found", roomId);
failure(nil);
}
else
{
[manager roomDataSourceForRoom:roomId create:YES onComplete:^(MXKRoomDataSource *roomDataSource) {
if (responseText != nil && responseText.length != 0)
{
NSLog(@"[AppDelegate][Push] didReceiveNotificationResponse: sending message to room: %@", roomId);
[roomDataSource sendTextMessage:responseText success:^(NSString* eventId) {
success(eventId);
} failure:^(NSError* error) {
failure(error);
}];
}
else
{
failure(nil);
}
}];
}
}
- (void)clearPushNotificationToken
{
// Clear existing token
MXKAccountManager* accountManager = [MXKAccountManager sharedManager];
[accountManager setPushDeviceToken:nil withPushOptions:nil];
}
#pragma mark - Universal link
- (BOOL)handleUniversalLink:(NSUserActivity*)userActivity