Update iOS 10+ notification titles

This commit is contained in:
SBiOSoftWhare
2019-04-05 10:59:18 +02:00
parent 089d67c03b
commit c0cdcf170d
+183 -120
View File
@@ -1478,19 +1478,21 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
- (void)handleLocalNotificationsForAccount:(MXKAccount*)account - (void)handleLocalNotificationsForAccount:(MXKAccount*)account
{ {
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: %@", account.mxCredentials.userId); NSString *userId = account.mxCredentials.userId;
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: %@", userId);
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: eventsToNotify: %@", eventsToNotify[@(account.mxSession.hash)]); NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: eventsToNotify: %@", eventsToNotify[@(account.mxSession.hash)]);
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: incomingPushEventIds: %@", self.incomingPushEventIds[@(account.mxSession.hash)]); NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: incomingPushEventIds: %@", self.incomingPushEventIds[@(account.mxSession.hash)]);
__block NSUInteger scheduledNotifications = 0; __block NSUInteger scheduledNotifications = 0;
// The call invite are handled here only when the callkit is not active. // The call invite are handled here only when the callkit is not active.
BOOL isCallKitActive = [MXCallKitAdapter callKitAvailable] && [MXKAppSettings standardAppSettings].isCallKitEnabled; BOOL isCallKitActive = [MXCallKitAdapter callKitAvailable] && [MXKAppSettings standardAppSettings].isCallKitEnabled;
NSMutableArray *eventsArray = eventsToNotify[@(account.mxSession.hash)]; NSMutableArray *eventsArray = eventsToNotify[@(account.mxSession.hash)];
NSMutableArray<NSString*> *redactedEventIds = [NSMutableArray array]; NSMutableArray<NSString*> *redactedEventIds = [NSMutableArray array];
// Display a local notification for each event retrieved by the bg sync. // Display a local notification for each event retrieved by the bg sync.
for (NSUInteger index = 0; index < eventsArray.count; index++) for (NSUInteger index = 0; index < eventsArray.count; index++)
{ {
@@ -1499,11 +1501,11 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
NSString *roomId = eventDict[@"room_id"]; NSString *roomId = eventDict[@"room_id"];
BOOL checkReadEvent = YES; BOOL checkReadEvent = YES;
MXEvent *event; MXEvent *event;
// Ignore event already notified to the user // Ignore event already notified to the user
// only necessary on iOS 9, iOS 10 will just overwrite notifications with identical IDs // only necessary on iOS 9, iOS 10 will just overwrite notifications with identical IDs
if (@available(iOS 10, *)) {} if (@available(iOS 10, *)) {}
else if ([self displayedLocalNotificationForEvent:eventId andUser:account.mxCredentials.userId type:nil]) else if ([self displayedLocalNotificationForEvent:eventId andUser:userId type:nil])
{ {
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId); NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event already displayed in a notification. Event id: %@", eventId);
continue; continue;
@@ -1526,7 +1528,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
else else
{ {
// Ignore redacted event. // Ignore redacted event.
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip redacted event. Event id: %@", event.eventId); NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip redacted event. Event id: %@", eventId);
} }
continue; continue;
} }
@@ -1537,7 +1539,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
// Ignore call invite when callkit is active. // Ignore call invite when callkit is active.
if (isCallKitActive) if (isCallKitActive)
{ {
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip call event. Event id: %@", event.eventId); NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip call event. Event id: %@", eventId);
continue; continue;
} }
else else
@@ -1557,13 +1559,13 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
if (checkReadEvent) if (checkReadEvent)
{ {
// Ignore event which has been read on another device. // Ignore event which has been read on another device.
MXReceiptData *readReceipt = [account.mxSession.store getReceiptInRoom:roomId forUserId:account.mxCredentials.userId]; MXReceiptData *readReceipt = [account.mxSession.store getReceiptInRoom:roomId forUserId:userId];
if (readReceipt) if (readReceipt)
{ {
MXEvent *readReceiptEvent = [account.mxSession.store eventWithEventId:readReceipt.eventId inRoom:roomId]; MXEvent *readReceiptEvent = [account.mxSession.store eventWithEventId:readReceipt.eventId inRoom:roomId];
if (event.originServerTs <= readReceiptEvent.originServerTs) if (event.originServerTs <= readReceiptEvent.originServerTs)
{ {
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip already read event. Event id: %@", event.eventId); NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip already read event. Event id: %@", eventId);
continue; continue;
} }
} }
@@ -1571,108 +1573,137 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
// Prepare the local notification // Prepare the local notification
MXPushRule *rule = eventDict[@"push_rule"]; MXPushRule *rule = eventDict[@"push_rule"];
NSDictionary *notificationUserInfo = @{ if (@available(iOS 10, *))
@"type": @"full",
@"room_id": event.roomId,
@"event_id": event.eventId,
@"user_id": account.mxCredentials.userId
};
BOOL isNotificationContentShown = !event.isEncrypted || RiotSettings.shared.showDecryptedContentInNotifications;
NSString *categoryIdentifier;
if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown)
{ {
categoryIdentifier = @"QUICK_REPLY"; [self notificationContentForEvent:event pushRule:rule inAccount:account onComplete:^(UNNotificationContent * _Nullable notificationContent) {
}
if (notificationContent)
NSString *soundName;
// Set sound name based on the value provided in action of MXPushRule
for (MXPushRuleAction *action in rule.actions)
{
if (action.actionType == MXPushRuleActionTypeSetTweak)
{
if ([action.parameters[@"set_tweak"] isEqualToString:@"sound"])
{ {
soundName = action.parameters[@"value"]; UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:eventId
if ([soundName isEqualToString:@"default"]) content:notificationContent
soundName = @"message.mp3"; trigger:nil];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (error)
{
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Fail to display notification for event %@ with error: %@", eventId, error);
}
else
{
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", eventId);
}
}];
scheduledNotifications++;
} }
} else
{
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated content. Event id: %@", eventId);
}
}];
} }
else
[self notificationBodyForEvent:event pushRule:rule inAccount:account onComplete:^(NSString *_Nullable notificationBody) {
{ [self notificationBodyForEvent:event pushRule:rule inAccount:account onComplete:^(NSString *_Nullable notificationBody)
if (notificationBody)
{ {
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for event %@", event.eventId); if (notificationBody)
// 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
NSString *fixedNotificationBody = [notificationBody stringByReplacingOccurrencesOfString:@"%" withString:@"%%"];
if (@available(iOS 10, *))
{
UNMutableNotificationContent *notificationContent = [[UNMutableNotificationContent alloc] init];
notificationContent.body = fixedNotificationBody;
notificationContent.userInfo = notificationUserInfo;
notificationContent.categoryIdentifier = categoryIdentifier;
notificationContent.threadIdentifier = roomId;
if (soundName)
{
notificationContent.sound = [UNNotificationSound soundNamed:soundName];
}
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:event.eventId
content:notificationContent
trigger:nil];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:nil];
}
else
{ {
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Display notification for 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 (%%).
NSString *fixedNotificationBody = [notificationBody stringByReplacingOccurrencesOfString:@"%" withString:@"%%"];
NSDictionary *notificationUserInfo = [self notificationUserInfoForEvent:event andUserId:userId];
NSString *categoryIdentifier = [self notificationCategoryIdentifierForEvent:event];
NSString *soundName = [self notificationSoundNameFromPushRule:rule];
UILocalNotification *eventNotification = [[UILocalNotification alloc] init]; UILocalNotification *eventNotification = [[UILocalNotification alloc] init];
eventNotification.alertBody = fixedNotificationBody; eventNotification.alertBody = fixedNotificationBody;
eventNotification.userInfo = notificationUserInfo; eventNotification.userInfo = notificationUserInfo;
eventNotification.category = categoryIdentifier; eventNotification.category = categoryIdentifier;
eventNotification.soundName = soundName; eventNotification.soundName = soundName;
[[UIApplication sharedApplication] scheduleLocalNotification:eventNotification]; [[UIApplication sharedApplication] scheduleLocalNotification:eventNotification];
scheduledNotifications++;
} }
else
scheduledNotifications++; {
} NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationBody. Event id: %@", eventId);
else }
{ }];
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Skip event with empty generated notificationBody. Event id: %@", event.eventId); }
}
}];
} }
} }
if (@available(iOS 10, *)) if (@available(iOS 10, *))
{ {
// Remove possible pending and delivered notifications having a redacted event id // Remove possible pending and delivered notifications having a redacted event id
if (redactedEventIds.count) if (redactedEventIds.count)
{ {
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Remove possible notification with redacted event ids: %@", redactedEventIds); NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Remove possible notification with redacted event ids: %@", redactedEventIds);
[[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:redactedEventIds]; [[UNUserNotificationCenter currentNotificationCenter] removePendingNotificationRequestsWithIdentifiers:redactedEventIds];
[[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:redactedEventIds]; [[UNUserNotificationCenter currentNotificationCenter] removeDeliveredNotificationsWithIdentifiers:redactedEventIds];
} }
} }
NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Sent %tu local notifications for %tu events", scheduledNotifications, eventsArray.count); NSLog(@"[AppDelegate][Push] handleLocalNotificationsForAccount: Sent %tu local notifications for %tu events", scheduledNotifications, eventsArray.count);
[eventsArray removeAllObjects]; [eventsArray removeAllObjects];
} }
- (NSString*)notificationSoundNameFromPushRule:(MXPushRule*)pushRule
{
NSString *soundName;
// Set sound name based on the value provided in action of MXPushRule
for (MXPushRuleAction *action in pushRule.actions)
{
if (action.actionType == MXPushRuleActionTypeSetTweak)
{
if ([action.parameters[@"set_tweak"] isEqualToString:@"sound"])
{
soundName = action.parameters[@"value"];
if ([soundName isEqualToString:@"default"])
{
soundName = @"message.mp3";
}
}
}
}
return soundName;
}
- (NSString*)notificationCategoryIdentifierForEvent:(MXEvent*)event
{
BOOL isNotificationContentShown = !event.isEncrypted || RiotSettings.shared.showDecryptedContentInNotifications;
NSString *categoryIdentifier;
if ((event.eventType == MXEventTypeRoomMessage || event.eventType == MXEventTypeRoomEncrypted) && isNotificationContentShown)
{
categoryIdentifier = @"QUICK_REPLY";
}
return categoryIdentifier;
}
- (NSDictionary*)notificationUserInfoForEvent:(MXEvent*)event andUserId:(NSString*)userId
{
NSDictionary *notificationUserInfo = @{
@"type": @"full",
@"room_id": event.roomId,
@"event_id": event.eventId,
@"user_id": userId
};
return notificationUserInfo;
}
- (void)notificationBodyForEvent:(MXEvent *)event pushRule:(MXPushRule*)rule inAccount:(MXKAccount*)account onComplete:(void (^)(NSString * _Nullable notificationBody))onComplete; - (void)notificationBodyForEvent:(MXEvent *)event pushRule:(MXPushRule*)rule inAccount:(MXKAccount*)account onComplete:(void (^)(NSString * _Nullable notificationBody))onComplete;
{ {
if (!event.content || !event.content.count) if (!event.content || !event.content.count)
@@ -1800,7 +1831,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
} }
// iOS 10+, does the same thing as notificationBodyForEvent:pushRule:inAccount:onComplete:, except with more features // iOS 10+, does the same thing as notificationBodyForEvent:pushRule:inAccount:onComplete:, except with more features
- (void)notificationContentForEvent:(MXEvent *)event pushRule:(MXPushRule *)rule inAccount:(MXKAccount *)account onComplete:(void (^)(UNMutableNotificationContent * _Nullable notificationContent))onComplete; - (void)notificationContentForEvent:(MXEvent *)event pushRule:(MXPushRule *)rule inAccount:(MXKAccount *)account onComplete:(void (^)(UNNotificationContent * _Nullable notificationContent))onComplete;
{ {
if (!event.content || !event.content.count) if (!event.content || !event.content.count)
{ {
@@ -1858,97 +1889,129 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN
return; return;
} }
} }
NSString *msgType = event.content[@"msgtype"]; NSString *msgType = event.content[@"msgtype"];
NSString *messageContent = event.content[@"body"]; NSString *messageContent = event.content[@"body"];
if (event.isEncrypted && !RiotSettings.shared.showDecryptedContentInNotifications) if (event.isEncrypted && !RiotSettings.shared.showDecryptedContentInNotifications)
{ {
// Hide the content // Hide the content
msgType = nil; msgType = nil;
} }
NSString *roomDisplayName = room.summary.displayname; NSString *roomDisplayName = room.summary.displayname;
// Display the room name only if it is different than the sender name // Display the room name only if it is different than the sender name
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
{ {
notificationTitle = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM_TITLE", nil), eventSenderName, roomDisplayName]; notificationTitle = [NSString localizedUserNotificationStringForKey:@"MSG_FROM_USER_IN_ROOM_TITLE" arguments:@[eventSenderName, roomDisplayName]];//
if ([msgType isEqualToString:@"m.text"]) if ([msgType isEqualToString:@"m.text"])
{
notificationBody = messageContent; notificationBody = messageContent;
}
else if ([msgType isEqualToString:@"m.emote"]) else if ([msgType isEqualToString:@"m.emote"])
{ {
notificationTitle = roomDisplayName; notificationBody = [NSString localizedUserNotificationStringForKey:@"ACTION_FROM_USER" arguments:@[eventSenderName, messageContent]];
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION_FROM_USER", nil), eventSenderName, messageContent];
} }
else if ([msgType isEqualToString:@"m.image"]) else if ([msgType isEqualToString:@"m.image"])
notificationBody = NSLocalizedString(@"IMAGE_TEXT_WITH_TITLE", nil); {
notificationBody = [NSString localizedUserNotificationStringForKey:@"IMAGE_FROM_USER" arguments:@[eventSenderName, messageContent]];
}
else else
{
// Encrypted messages falls here // Encrypted messages falls here
notificationBody = NSLocalizedString(@"MSG_TEXT_WITH_TITLE", nil); notificationBody = [NSString localizedUserNotificationStringForKey:@"MSG_FROM_USER" arguments:@[eventSenderName]];
}
} }
else else
{ {
notificationTitle = eventSenderName; notificationTitle = eventSenderName;
if ([msgType isEqualToString:@"m.text"]) if ([msgType isEqualToString:@"m.text"])
{
notificationBody = messageContent; notificationBody = messageContent;
}
else if ([msgType isEqualToString:@"m.emote"]) else if ([msgType isEqualToString:@"m.emote"])
{ {
notificationTitle = eventSenderName; notificationBody = [NSString localizedUserNotificationStringForKey:@"ACTION_FROM_USER" arguments:@[eventSenderName, messageContent]];
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"ACTION", nil), messageContent];
} }
else if ([msgType isEqualToString:@"m.image"]) else if ([msgType isEqualToString:@"m.image"])
notificationBody = NSLocalizedString(@"IMAGE_TEXT_WITH_TITLE", nil); {
notificationBody = [NSString localizedUserNotificationStringForKey:@"IMAGE_FROM_USER" arguments:@[eventSenderName, messageContent]];
}
else else
{
// Encrypted messages falls here // Encrypted messages falls here
notificationBody = NSLocalizedString(@"MSG_TEXT_WITH_TITLE", nil); notificationBody = [NSString localizedUserNotificationStringForKey:@"MSG_FROM_USER" arguments:@[eventSenderName]];
}
} }
} }
else if (event.eventType == MXEventTypeCallInvite) else if (event.eventType == MXEventTypeCallInvite)
{ {
NSString *sdp = event.content[@"offer"][@"sdp"]; NSString *sdp = event.content[@"offer"][@"sdp"];
BOOL isVideoCall = [sdp rangeOfString:@"m=video"].location != NSNotFound; BOOL isVideoCall = [sdp rangeOfString:@"m=video"].location != NSNotFound;
notificationTitle = eventSenderName;
if (!isVideoCall) if (!isVideoCall)
notificationBody = NSLocalizedString(@"VOICE_CALL", nil); {
notificationBody = [NSString localizedUserNotificationStringForKey:@"VOICE_CALL_FROM_USER" arguments:@[eventSenderName]];
}
else else
notificationBody = NSLocalizedString(@"VIDEO_CALL", nil); {
notificationBody = [NSString localizedUserNotificationStringForKey:@"VIDEO_CALL_FROM_USER" arguments:@[eventSenderName]];
}
// call notifications should stand out from normal messages, so we don't stack them // call notifications should stand out from normal messages, so we don't stack them
threadIdentifier = nil; threadIdentifier = nil;
} }
else if (event.eventType == MXEventTypeRoomMember) else if (event.eventType == MXEventTypeRoomMember)
{ {
NSString *roomDisplayName = room.summary.displayname; NSString *roomDisplayName = room.summary.displayname;
notificationTitle = roomDisplayName;
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
notificationBody = [NSString stringWithFormat:NSLocalizedString(@"INVITE_BY_USER_TO_ROOM", nil), eventSenderName]; {
notificationBody = [NSString localizedUserNotificationStringForKey:@"USER_INVITE_TO_NAMED_ROOM" arguments:@[eventSenderName, roomDisplayName]];
}
else else
notificationBody = NSLocalizedString(@"INVITE_TO_CHAT", nil); {
notificationBody = [NSString localizedUserNotificationStringForKey:@"USER_INVITE_TO_CHAT" arguments:@[eventSenderName]];
}
} }
else if (event.eventType == MXEventTypeSticker) else if (event.eventType == MXEventTypeSticker)
{ {
NSString *roomDisplayName = room.summary.displayname; NSString *roomDisplayName = room.summary.displayname;
if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName]) if (roomDisplayName.length && ![roomDisplayName isEqualToString:eventSenderName])
notificationTitle = [NSString stringWithFormat:NSLocalizedString(@"MSG_FROM_USER_IN_ROOM_TITLE", nil), eventSenderName, roomDisplayName]; {
notificationTitle = [NSString localizedUserNotificationStringForKey:@"MSG_FROM_USER_IN_ROOM_TITLE" arguments:@[eventSenderName, roomDisplayName]];
}
else else
{
notificationTitle = eventSenderName; notificationTitle = eventSenderName;
}
notificationBody = NSLocalizedString(@"STICKER_TEXT_WITH_TITLE", nil);
notificationBody = [NSString localizedUserNotificationStringForKey:@"STICKER_FROM_USER" arguments:@[eventSenderName]];
} }
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init]; UNMutableNotificationContent *notificationContent = [[UNMutableNotificationContent alloc] init];
[content setTitle:notificationTitle]; NSDictionary *notificationUserInfo = [self notificationUserInfoForEvent:event andUserId:account.mxCredentials.userId];
[content setBody:notificationBody]; NSString *notificationSoundName = [self notificationSoundNameFromPushRule:rule];
[content setThreadIdentifier:threadIdentifier]; NSString *categoryIdentifier = [self notificationCategoryIdentifierForEvent:event];
onComplete(content); notificationContent.title = notificationTitle;
notificationContent.subtitle = notificationSubTitle;
notificationContent.body = notificationBody;
notificationContent.threadIdentifier = threadIdentifier;
notificationContent.userInfo = notificationUserInfo;
notificationContent.categoryIdentifier = categoryIdentifier;
if (notificationSoundName)
{
notificationContent.sound = [UNNotificationSound soundNamed:notificationSoundName];
}
onComplete([notificationContent copy]);
}]; }];
} }