Merge pull request #233 from loki-project/notifications

Fix Push Notification Bugs
This commit is contained in:
Niels Andriesse 2020-07-29 11:52:10 +10:00 committed by GitHub
commit 3075e82d8a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 257 additions and 360 deletions

View File

@ -16,31 +16,36 @@ final class NotificationServiceExtension : UNNotificationServiceExtension {
self.contentHandler = contentHandler
notificationContent = (request.content.mutableCopy() as? UNMutableNotificationContent)
DispatchQueue.main.sync { self.setUpIfNecessary() }
// The code using DispatchQueue.main.async { self.setUpIfNecessary(){ Modify the notification content } } will somehow cause a freezing when the second PN comes
if let notificationContent = notificationContent {
// Modify the notification content here...
let base64EncodedData = notificationContent.userInfo["ENCRYPTED_DATA"] as! String
let data = Data(base64Encoded: base64EncodedData)!
let envelope = try? MessageWrapper.unwrap(data: data)
let envelopeData = try? envelope?.serializedData()
let decrypter = SSKEnvironment.shared.messageDecrypter
if (envelope != nil && envelopeData != nil) {
decrypter.decryptEnvelope(envelope!,
envelopeData: envelopeData!,
successBlock: { result, transaction in
if (try? SSKProtoEnvelope.parseData(result.envelopeData)) != nil {
self.handleDecryptionResult(result: result, notificationContent: notificationContent, transaction: transaction)
} else {
DispatchQueue.main.sync { self.setUpIfNecessary(){} }
AppReadiness.runNowOrWhenAppDidBecomeReady {
if let notificationContent = self.notificationContent {
// Modify the notification content here...
let base64EncodedData = notificationContent.userInfo["ENCRYPTED_DATA"] as! String
let data = Data(base64Encoded: base64EncodedData)!
let decrypter = SSKEnvironment.shared.messageDecrypter
let messageManager = SSKEnvironment.shared.messageManager
if let envelope = try? MessageWrapper.unwrap(data: data), let data = try? envelope.serializedData() {
let wasReceivedByUD = self.wasReceivedByUD(envelope: envelope)
decrypter.decryptEnvelope(envelope,
envelopeData: data,
successBlock: { result, transaction in
if let envelope = try? SSKProtoEnvelope.parseData(result.envelopeData) {
messageManager.throws_processEnvelope(envelope, plaintextData: result.plaintextData, wasReceivedByUD: wasReceivedByUD, transaction: transaction, serverID: 0)
self.handleDecryptionResult(result: result, notificationContent: notificationContent, transaction: transaction)
} else {
self.completeWithFailure(content: notificationContent)
}
},
failureBlock: {
self.completeWithFailure(content: notificationContent)
}
},
failureBlock: {
self.completeWithFailure(content: notificationContent)
}
)
} else {
self.completeWithFailure(content: notificationContent)
)
} else {
self.completeWithFailure(content: notificationContent)
}
}
}
}
@ -49,31 +54,31 @@ final class NotificationServiceExtension : UNNotificationServiceExtension {
let contentProto = try? SSKProtoContent.parseData(result.plaintextData!)
var thread: TSThread
var newNotificationBody = ""
let masterHexEncodedPublicKey = OWSPrimaryStorage.shared().getMasterHexEncodedPublicKey(for: result.source, in: transaction) ?? result.source
var displayName = masterHexEncodedPublicKey
let masterPublicKey = OWSPrimaryStorage.shared().getMasterHexEncodedPublicKey(for: result.source, in: transaction) ?? result.source
var displayName = masterPublicKey
if let groupID = contentProto?.dataMessage?.group?.id {
thread = TSGroupThread.getOrCreateThread(withGroupId: groupID, groupType: .closedGroup, transaction: transaction)
thread = TSGroupThread.getOrCreateThread(withGroupId: groupID, groupType: .closedGroup, transaction: transaction)
displayName = thread.name()
if displayName.count < 1 {
displayName = MessageStrings.newGroupDefaultTitle
}
let group: SSKProtoGroupContext = (contentProto?.dataMessage?.group!)!
let group: SSKProtoGroupContext = contentProto!.dataMessage!.group!
let oldGroupModel = (thread as! TSGroupThread).groupModel
var removeMembers = Set(arrayLiteral: oldGroupModel.groupMemberIds)
var removedMembers = Set(arrayLiteral: oldGroupModel.groupMemberIds)
let newGroupModel = TSGroupModel.init(title: group.name,
memberIds:group.members,
image: oldGroupModel.groupImage,
groupId: group.id,
groupType: oldGroupModel.groupType,
adminIds: group.admins)
removeMembers.subtract(Set(arrayLiteral: newGroupModel.groupMemberIds))
newGroupModel.removedMembers = NSMutableSet(set: removeMembers)
removedMembers.subtract(Set(arrayLiteral: newGroupModel.groupMemberIds))
newGroupModel.removedMembers = NSMutableSet(set: removedMembers)
switch contentProto?.dataMessage?.group?.type {
case .update:
newNotificationBody = oldGroupModel.getInfoStringAboutUpdate(to: newGroupModel, contactsManager: SSKEnvironment.shared.contactsManager)
break
case .quit:
let nameString = SSKEnvironment.shared.contactsManager.displayName(forPhoneIdentifier: masterHexEncodedPublicKey, transaction: transaction)
let nameString = SSKEnvironment.shared.contactsManager.displayName(forPhoneIdentifier: masterPublicKey, transaction: transaction)
newNotificationBody = NSLocalizedString("GROUP_MEMBER_LEFT", comment: nameString)
break
default:
@ -88,7 +93,7 @@ final class NotificationServiceExtension : UNNotificationServiceExtension {
notificationContent.userInfo = userInfo
notificationContent.badge = 1
if newNotificationBody.count < 1 {
newNotificationBody = contentProto?.dataMessage?.body ?? ""
newNotificationBody = contentProto?.dataMessage?.body ?? "You've got a new message"
}
notificationContent.body = newNotificationBody
if notificationContent.body.count < 1 {
@ -98,7 +103,7 @@ final class NotificationServiceExtension : UNNotificationServiceExtension {
}
}
func setUpIfNecessary() {
func setUpIfNecessary(completion: @escaping () -> Void) {
AssertIsOnMainThread()
// The NSE will often re-use the same process, so if we're
@ -116,8 +121,6 @@ final class NotificationServiceExtension : UNNotificationServiceExtension {
DebugLogger.shared().enableFileLogging()
}
Logger.info("")
_ = AppVersion.sharedInstance()
Cryptography.seedRandom()
@ -131,12 +134,12 @@ final class NotificationServiceExtension : UNNotificationServiceExtension {
AppSetup.setupEnvironment(
appSpecificSingletonBlock: {
// TODO: calls..
SSKEnvironment.shared.callMessageHandler = NoopCallMessageHandler()
SSKEnvironment.shared.notificationsManager = NoopNotificationsManager()
},
migrationCompletion: { [weak self] in
self?.versionMigrationsDidComplete()
completion()
}
)
@ -162,8 +165,6 @@ final class NotificationServiceExtension : UNNotificationServiceExtension {
func versionMigrationsDidComplete() {
AssertIsOnMainThread()
Logger.debug("")
areVersionMigrationsComplete = true
checkIsAppReady()
@ -173,8 +174,6 @@ final class NotificationServiceExtension : UNNotificationServiceExtension {
func storageIsReady() {
AssertIsOnMainThread()
Logger.debug("")
checkIsAppReady()
}
@ -197,7 +196,7 @@ final class NotificationServiceExtension : UNNotificationServiceExtension {
}
func completeWithFailure(content: UNMutableNotificationContent) {
content.body = "You've got a new message."
content.body = "You've got a new message"
content.title = "Session"
let userInfo: [String:Any] = [NotificationServiceExtension.isFromRemoteKey : true]
content.userInfo = userInfo

View File

@ -12,7 +12,7 @@ final class NotificationServiceExtensionContext : NSObject, AppContext {
let isMainAppAndActive = false
var openSystemSettingsAction: UIAlertAction?
var wasWokenUpBySilentPushNotification = true
var wasWokenUpByPushNotification = true
var shouldProcessIncomingMessages: Bool { true }

View File

@ -61,8 +61,6 @@ static NSTimeInterval launchStartedAt;
@property (nonatomic) BOOL hasInitialRootViewController;
@property (nonatomic) BOOL areVersionMigrationsComplete;
@property (nonatomic) BOOL didAppLaunchFail;
// Loki
@property (nonatomic) LKPoller *poller;
@property (nonatomic) LKClosedGroupPoller *closedGroupPoller;
@ -160,7 +158,7 @@ static NSTimeInterval launchStartedAt;
return AppEnvironment.shared.legacyNotificationActionHandler;
}
#pragma mark -
#pragma mark - Lifecycle
- (void)applicationDidEnterBackground:(UIApplication *)application
{
@ -297,6 +295,94 @@ static NSTimeInterval launchStartedAt;
return YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
return;
}
if (CurrentAppContext().isRunningTests) {
return;
}
[self ensureRootViewController];
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
[self handleActivation];
}];
// Clear all notifications whenever we become active.
// When opening the app from a notification,
// AppDelegate.didReceiveLocalNotification will always
// be called _before_ we become active.
[self clearAllNotificationsAndRestoreBadgeCount];
// On every activation, clear old temp directories.
ClearOldTemporaryDirectories();
}
- (void)applicationWillResignActive:(UIApplication *)application
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
return;
}
[self clearAllNotificationsAndRestoreBadgeCount];
[DDLog flushLog];
}
#pragma mark - Orientation
- (UIInterfaceOrientationMask)application:(UIApplication *)application supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window
{
return UIInterfaceOrientationMaskPortrait;
}
#pragma mark - Background Fetching
- (void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler
{
NSLog(@"[Loki] Performing background fetch.");
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
NSMutableArray *promises = [NSMutableArray new];
__block AnyPromise *fetchMessagesPromise = [AppEnvironment.shared.messageFetcherJob run].then(^{
fetchMessagesPromise = nil;
}).catch(^{
fetchMessagesPromise = nil;
});
[promises addObject:fetchMessagesPromise];
[fetchMessagesPromise retainUntilComplete];
__block NSDictionary<NSString *, LKPublicChat *> *publicChats;
[OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
publicChats = [LKDatabaseUtilities getAllPublicChats:transaction];
}];
for (LKPublicChat *publicChat in publicChats) {
if (![publicChat isKindOfClass:LKPublicChat.class]) { continue; }
LKPublicChatPoller *poller = [[LKPublicChatPoller alloc] initForPublicChat:publicChat];
[poller stop];
AnyPromise *fetchGroupMessagesPromise = [poller pollForNewMessages];
[promises addObject:fetchGroupMessagesPromise];
[fetchGroupMessagesPromise retainUntilComplete];
}
PMKJoin(promises).then(^(id results) {
completionHandler(UIBackgroundFetchResultNewData);
}).catch(^(id error) {
completionHandler(UIBackgroundFetchResultFailed);
});
}];
}
#pragma mark - App Readiness
/**
* The user must unlock the device once after reboot before the database encryption key can be accessed.
*/
@ -395,86 +481,6 @@ static NSTimeInterval launchStartedAt;
OWSLogInfo(@"Build Date/Time: %@", buildDetails[@"DateTime"]);
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
return;
}
[self.pushRegistrationManager didReceiveVanillaPushToken:deviceToken];
OWSLogInfo(@"Registering for push notifications with token: %@.", deviceToken);
BOOL isUsingFullAPNs = [NSUserDefaults.standardUserDefaults boolForKey:@"isUsingFullAPNs"];
if (isUsingFullAPNs) {
__unused AnyPromise *promise = [LKPushNotificationManager registerWithToken:deviceToken hexEncodedPublicKey:self.tsAccountManager.localNumber isForcedUpdate:NO];
} else {
__unused AnyPromise *promise = [LKPushNotificationManager registerWithToken:deviceToken isForcedUpdate:NO];
}
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
return;
}
OWSLogError(@"Failed to register push token with error: %@.", error);
#ifdef DEBUG
OWSLogWarn(@"We're in debug mode. Faking success for remote registration with a fake push identifier.");
[self.pushRegistrationManager didReceiveVanillaPushToken:[[NSMutableData dataWithLength:32] copy]];
#else
OWSProdError([OWSAnalyticsEvents appDelegateErrorFailedToRegisterForRemoteNotifications]);
[self.pushRegistrationManager didFailToReceiveVanillaPushTokenWithError:error];
#endif
}
- (void)application:(UIApplication *)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
return;
}
[self.notificationPresenter didRegisterLegacyNotificationSettings];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
return;
}
if (CurrentAppContext().isRunningTests) {
return;
}
[self ensureRootViewController];
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
[self handleActivation];
}];
// Clear all notifications whenever we become active.
// When opening the app from a notification,
// AppDelegate.didReceiveLocalNotification will always
// be called _before_ we become active.
[self clearAllNotificationsAndRestoreBadgeCount];
// On every activation, clear old temp directories.
ClearOldTemporaryDirectories();
}
- (void)enableBackgroundRefreshIfNecessary
{
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
@ -536,7 +542,7 @@ static NSTimeInterval launchStartedAt;
[self.socketManager requestSocketOpen];
[Environment.shared.contactsManager fetchSystemContactsOnceIfAlreadyAuthorized];
NSString *userHexEncodedPublicKey = self.tsAccountManager.localNumber;
NSString *userPublicKey = self.tsAccountManager.localNumber;
[self startPollerIfNeeded];
[self startClosedGroupPollerIfNeeded];
@ -548,15 +554,15 @@ static NSTimeInterval launchStartedAt;
NSDate *lastProfilePictureUpload = (NSDate *)[userDefaults objectForKey:@"lastProfilePictureUpload"];
if (lastProfilePictureUpload != nil && [now timeIntervalSinceDate:lastProfilePictureUpload] > 4 * 24 * 60 * 60) {
OWSProfileManager *profileManager = OWSProfileManager.sharedManager;
NSString *displayName = [profileManager profileNameForRecipientWithID:userHexEncodedPublicKey];
UIImage *profilePicture = [profileManager profileAvatarForRecipientId:userHexEncodedPublicKey];
NSString *displayName = [profileManager profileNameForRecipientWithID:userPublicKey];
UIImage *profilePicture = [profileManager profileAvatarForRecipientId:userPublicKey];
[profileManager updateLocalProfileName:displayName avatarImage:profilePicture success:^{
// Do nothing; the user defaults flag is updated in LokiFileServerAPI
} failure:^(NSError *error) {
// Do nothing
} requiresSync:YES];
}
if (![UIApplication sharedApplication].isRegisteredForRemoteNotifications) {
OWSLogInfo(@"Retrying remote notification registration since user hasn't registered yet.");
// Push tokens don't normally change while the app is launched, so checking once during launch is
@ -571,211 +577,10 @@ static NSTimeInterval launchStartedAt;
}
}
- (void)applicationWillResignActive:(UIApplication *)application
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
return;
}
[self clearAllNotificationsAndRestoreBadgeCount];
[DDLog flushLog];
}
- (void)clearAllNotificationsAndRestoreBadgeCount
{
OWSAssertIsOnMainThread();
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
[AppEnvironment.shared.notificationPresenter clearAllNotifications];
[OWSMessageUtils.sharedManager updateApplicationBadgeCount];
}];
}
- (void)application:(UIApplication *)application
performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem
completionHandler:(void (^)(BOOL succeeded))completionHandler {
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
completionHandler(NO);
return;
}
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
if (![self.tsAccountManager isRegisteredAndReady]) {
UIAlertController *controller =
[UIAlertController alertControllerWithTitle:NSLocalizedString(@"REGISTER_CONTACTS_WELCOME", nil)
message:NSLocalizedString(@"REGISTRATION_RESTRICTED_MESSAGE", nil)
preferredStyle:UIAlertControllerStyleAlert];
[controller addAction:[UIAlertAction actionWithTitle:NSLocalizedString(@"OK", nil)
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *_Nonnull action){
}]];
UIViewController *fromViewController = [[UIApplication sharedApplication] frontmostViewController];
[fromViewController presentViewController:controller
animated:YES
completion:^{
completionHandler(NO);
}];
return;
}
[SignalApp.sharedApp.homeViewController createNewPrivateChat];
completionHandler(YES);
}];
}
#pragma mark - Orientation
- (UIInterfaceOrientationMask)application:(UIApplication *)application
supportedInterfaceOrientationsForWindow:(nullable UIWindow *)window
{
return UIInterfaceOrientationMaskPortrait;
}
#pragma mark Push Notifications Delegate Methods
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
return;
}
OWSLogInfo(@"%@", notification);
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
if (![self.tsAccountManager isRegisteredAndReady]) {
OWSLogInfo(@"Ignoring action; app not ready.");
return;
}
[self.legacyNotificationActionHandler
handleNotificationResponseWithActionIdentifier:OWSLegacyNotificationActionHandler.kDefaultActionIdentifier
notification:notification
responseInfo:@{}
completionHandler:^{
}];
}];
}
- (void)application:(UIApplication *)application
handleActionWithIdentifier:(NSString *)identifier
forLocalNotification:(UILocalNotification *)notification
completionHandler:(void (^)())completionHandler
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
completionHandler();
return;
}
// The docs for handleActionWithIdentifier:... state:
// "You must call [completionHandler] at the end of your method.".
// Nonetheless, it is presumably safe to call the completion handler
// later, after this method returns.
//
// https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623068-application?language=objc
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
if (![self.tsAccountManager isRegisteredAndReady]) {
OWSLogInfo(@"Ignoring action; app not ready.");
completionHandler();
return;
}
[self.legacyNotificationActionHandler handleNotificationResponseWithActionIdentifier:identifier
notification:notification
responseInfo:@{}
completionHandler:completionHandler];
}];
}
- (void)application:(UIApplication *)application
handleActionWithIdentifier:(NSString *)identifier
forLocalNotification:(UILocalNotification *)notification
withResponseInfo:(NSDictionary *)responseInfo
completionHandler:(void (^)())completionHandler
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
completionHandler();
return;
}
// The docs for handleActionWithIdentifier:... state:
// "You must call [completionHandler] at the end of your method.".
// Nonetheless, it is presumably safe to call the completion handler
// later, after this method returns.
//
// https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623068-application?language=objc
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
if (![self.tsAccountManager isRegisteredAndReady]) {
OWSLogInfo(@"Ignoring action; app not ready.");
completionHandler();
return;
}
[self.legacyNotificationActionHandler handleNotificationResponseWithActionIdentifier:identifier
notification:notification
responseInfo:responseInfo
completionHandler:completionHandler];
}];
}
- (void)application:(UIApplication *)application
performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler
{
NSLog(@"[Loki] Performing background fetch.");
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
NSMutableArray *promises = [NSMutableArray new];
__block AnyPromise *fetchMessagesPromise = [AppEnvironment.shared.messageFetcherJob run].then(^{
fetchMessagesPromise = nil;
}).catch(^{
fetchMessagesPromise = nil;
});
[promises addObject:fetchMessagesPromise];
[fetchMessagesPromise retainUntilComplete];
__block NSDictionary<NSString *, LKPublicChat *> *publicChats;
[OWSPrimaryStorage.sharedManager.dbReadConnection readWithBlock:^(YapDatabaseReadTransaction *transaction) {
publicChats = [LKDatabaseUtilities getAllPublicChats:transaction];
}];
for (LKPublicChat *publicChat in publicChats) {
if (![publicChat isKindOfClass:LKPublicChat.class]) { continue; } // For some reason publicChat is sometimes a base 64 encoded string...
LKPublicChatPoller *poller = [[LKPublicChatPoller alloc] initForPublicChat:publicChat];
[poller stop];
AnyPromise *fetchGroupMessagesPromise = [poller pollForNewMessages];
[promises addObject:fetchGroupMessagesPromise];
[fetchGroupMessagesPromise retainUntilComplete];
}
PMKJoin(promises).then(^(id results) {
completionHandler(UIBackgroundFetchResultNewData);
}).catch(^(id error) {
completionHandler(UIBackgroundFetchResultFailed);
});
}];
}
- (void)versionMigrationsDidComplete
{
OWSAssertIsOnMainThread();
OWSLogInfo(@"versionMigrationsDidComplete");
self.areVersionMigrationsComplete = YES;
[self checkIfAppIsReady];
@ -784,7 +589,6 @@ static NSTimeInterval launchStartedAt;
- (void)storageIsReady
{
OWSAssertIsOnMainThread();
OWSLogInfo(@"storageIsReady");
[self checkIfAppIsReady];
}
@ -956,7 +760,7 @@ static NSTimeInterval launchStartedAt;
[UIViewController attemptRotationToDeviceOrientation];
}
#pragma mark - status bar touches
#pragma mark - Status Bar Interaction
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
@ -968,16 +772,93 @@ static NSTimeInterval launchStartedAt;
}
}
#pragma mark - UNUserNotificationsDelegate
#pragma mark - Notifications
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
return;
}
[self.pushRegistrationManager didReceiveVanillaPushToken:deviceToken];
OWSLogInfo(@"Registering for push notifications with token: %@.", deviceToken);
BOOL isUsingFullAPNs = [NSUserDefaults.standardUserDefaults boolForKey:@"isUsingFullAPNs"];
if (isUsingFullAPNs) {
__unused AnyPromise *promise = [LKPushNotificationManager registerWithToken:deviceToken hexEncodedPublicKey:self.tsAccountManager.localNumber isForcedUpdate:NO];
} else {
__unused AnyPromise *promise = [LKPushNotificationManager registerWithToken:deviceToken isForcedUpdate:NO];
}
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
return;
}
OWSLogError(@"Failed to register push token with error: %@.", error);
#ifdef DEBUG
OWSLogWarn(@"We're in debug mode. Faking success for remote registration with a fake push identifier.");
[self.pushRegistrationManager didReceiveVanillaPushToken:[[NSMutableData dataWithLength:32] copy]];
#else
OWSProdError([OWSAnalyticsEvents appDelegateErrorFailedToRegisterForRemoteNotifications]);
[self.pushRegistrationManager didFailToReceiveVanillaPushTokenWithError:error];
#endif
}
- (void)application:(UIApplication *)application
didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
return;
}
[self.notificationPresenter didRegisterLegacyNotificationSettings];
}
- (void)clearAllNotificationsAndRestoreBadgeCount
{
OWSAssertIsOnMainThread();
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
[AppEnvironment.shared.notificationPresenter clearAllNotifications];
[OWSMessageUtils.sharedManager updateApplicationBadgeCount];
}];
}
- (void)application:(UIApplication *)application performActionForShortcutItem:(UIApplicationShortcutItem *)shortcutItem completionHandler:(void (^)(BOOL succeeded))completionHandler
{
OWSAssertIsOnMainThread();
if (self.didAppLaunchFail) {
OWSFailDebug(@"App launch failed");
completionHandler(NO);
return;
}
[AppReadiness runNowOrWhenAppDidBecomeReady:^{
if (![self.tsAccountManager isRegisteredAndReady]) { return; }
[SignalApp.sharedApp.homeViewController createNewPrivateChat];
completionHandler(YES);
}];
}
// The method will be called on the delegate only if the application is in the foreground. If the method is not
// implemented or the handler is not called in a timely manner then the notification will not be presented. The
// application can choose to have the notification presented as a sound, badge, alert and/or in the notification list.
// This decision should be based on whether the information in the notification is otherwise visible to the user.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
__IOS_AVAILABLE(10.0)__TVOS_AVAILABLE(10.0)__WATCHOS_AVAILABLE(3.0)__OSX_AVAILABLE(10.14)
- (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler
__IOS_AVAILABLE(10.0)__TVOS_AVAILABLE(10.0)__WATCHOS_AVAILABLE(3.0)__OSX_AVAILABLE(10.14)
{
if (notification.request.content.userInfo[@"remote"]) {
OWSLogInfo(@"[Loki] Ignoring remote notifications while the app is in the foreground.");
@ -989,8 +870,7 @@ static NSTimeInterval launchStartedAt;
// need to handle this behavior for legacy UINotification users anyway, we "allow" all
// notification options here, and rely on the shared logic in NotificationPresenter to
// honor notification sound preferences for both modern and legacy users.
UNNotificationPresentationOptions options = UNNotificationPresentationOptionAlert
| UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound;
UNNotificationPresentationOptions options = UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound;
completionHandler(options);
}];
}
@ -998,10 +878,8 @@ static NSTimeInterval launchStartedAt;
// The method will be called on the delegate when the user responded to the notification by opening the application,
// dismissing the notification or choosing a UNNotificationAction. The delegate must be set before the application
// returns from application:didFinishLaunchingWithOptions:.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler __IOS_AVAILABLE(10.0)__WATCHOS_AVAILABLE(3.0)
__OSX_AVAILABLE(10.14)__TVOS_PROHIBITED
- (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler __IOS_AVAILABLE(10.0)__WATCHOS_AVAILABLE(3.0)
__OSX_AVAILABLE(10.14)__TVOS_PROHIBITED
{
[AppReadiness runNowOrWhenAppDidBecomeReady:^() {
[self.userNotificationActionHandler handleNotificationResponse:response completionHandler:completionHandler];
@ -1012,14 +890,13 @@ static NSTimeInterval launchStartedAt;
// in-app notification settings. Add UNAuthorizationOptionProvidesAppNotificationSettings as an option in
// requestAuthorizationWithOptions:completionHandler: to add a button to inline notification settings view and the
// notification settings view in Settings. The notification will be nil when opened from Settings.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
openSettingsForNotification:(nullable UNNotification *)notification __IOS_AVAILABLE(12.0)
__OSX_AVAILABLE(10.14)__WATCHOS_PROHIBITED __TVOS_PROHIBITED
- (void)userNotificationCenter:(UNUserNotificationCenter *)center openSettingsForNotification:(nullable UNNotification *)notification __IOS_AVAILABLE(12.0)
__OSX_AVAILABLE(10.14)__WATCHOS_PROHIBITED __TVOS_PROHIBITED
{
}
#pragma mark - Loki
#pragma mark - Polling
- (void)startPollerIfNeeded
{
@ -1055,7 +932,10 @@ static NSTimeInterval launchStartedAt;
- (void)stopOpenGroupPollers { [LKPublicChatManager.shared stopPollers]; }
- (void)handleDataNukeRequested:(NSNotification *)notification {
# pragma mark - Other
- (void)handleDataNukeRequested:(NSNotification *)notification
{
[ThreadUtil deleteAllContent];
[SSKEnvironment.shared.messageSenderJobQueue clearAllJobs];
[SSKEnvironment.shared.identityManager clearIdentityKey];

View File

@ -212,12 +212,16 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
isObservingDatabase = isViewVisible && CurrentAppContext().isAppForegroundAndActive()
}
private func reload() {
AssertIsOnMainThread()
private func updateThreadMapping() {
uiDatabaseConnection.beginLongLivedReadTransaction()
uiDatabaseConnection.read { transaction in
self.threads.update(with: transaction)
}
}
private func reload() {
AssertIsOnMainThread()
updateThreadMapping()
threadViewModelCache.removeAll()
tableView.reloadData()
emptyStateView.isHidden = (threadCount != 0)
@ -235,6 +239,21 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
}
return
}
// Guard for changes made in Notification Service Extension
// Crashes may happen if there is some modification in NSE
// The YapDBModificationNotification cannot cross process
// So the thread mapping won't update itself if DB modification happened in other process like NSE
// With these code we can sync the mapping before asking for changes from YapDB
if (notifications.count > 0) {
if let firstChangeset = notifications[0].userInfo {
let firstSnapshot = firstChangeset[YapDatabaseSnapshotKey] as! UInt64
if (self.threads.snapshotOfLastUpdate != firstSnapshot - 1) {
reload()
return
}
}
}
var sectionChanges = NSArray()
var rowChanges = NSArray()
ext.getSectionChanges(&sectionChanges, rowChanges: &rowChanges, for: notifications, with: threads)
@ -261,6 +280,7 @@ final class HomeVC : BaseVC, UITableViewDataSource, UITableViewDelegate, UIScrol
@objc private func handleApplicationDidBecomeActiveNotification(_ notification: Notification) {
updateIsObservingDatabase()
updateThreadMapping()
}
@objc private func handleApplicationWillResignActiveNotification(_ notification: Notification) {

View File

@ -85,7 +85,7 @@ extension UserNotificationPresenterAdaptee: NotificationPresenterAdaptee {
self.notificationCenter.setNotificationCategories(UserNotificationConfig.allNotificationCategories)
if granted {
Logger.debug("succeeded.")
} else if error != nil {
Logger.error("failed with error: \(error!)")
} else {

View File

@ -28,7 +28,7 @@ NSString *const ReportedApplicationStateDidChangeNotification = @"ReportedApplic
@synthesize mainWindow = _mainWindow;
@synthesize appLaunchTime = _appLaunchTime;
@synthesize wasWokenUpBySilentPushNotification = _wasWokenUpBySilentPushNotification;
@synthesize wasWokenUpByPushNotification = _wasWokenUpByPushNotification;
- (instancetype)init
{
@ -41,7 +41,7 @@ NSString *const ReportedApplicationStateDidChangeNotification = @"ReportedApplic
self.reportedApplicationState = UIApplicationStateInactive;
_appLaunchTime = [NSDate new];
_wasWokenUpBySilentPushNotification = false;
_wasWokenUpByPushNotification = false;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(applicationWillEnterForeground:)

View File

@ -264,7 +264,7 @@ typedef void (^AttachmentDownloadFailure)(NSError *error);
- (void)startDownloadIfPossible
{
if (CurrentAppContext().wasWokenUpBySilentPushNotification) { return; }
if (CurrentAppContext().wasWokenUpByPushNotification) { return; }
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
OWSAttachmentDownloadJob *_Nullable job;

View File

@ -1613,10 +1613,10 @@ NS_ASSUME_NONNULL_BEGIN
// Update thread preview in inbox
[masterThread touchWithTransaction:transaction];
[SSKEnvironment.shared.notificationsManager notifyUserForIncomingMessage:incomingMessage
inThread:masterThread
transaction:transaction];
if (CurrentAppContext().isMainAppAndActive) {
[SSKEnvironment.shared.notificationsManager notifyUserForIncomingMessage:incomingMessage inThread:masterThread transaction:transaction];
}
dispatch_async(dispatch_get_main_queue(), ^{
[self.typingIndicators didReceiveIncomingMessageInThread:masterThread

View File

@ -88,8 +88,6 @@ NSString *const kSessionStoreDBConnectionKey = @"kSessionStoreDBConnectionKey";
OWSAssertDebug(contactIdentifier.length > 0);
OWSAssertDebug(deviceId >= 0);
OWSAssertDebug([protocolContext isKindOfClass:[YapDatabaseReadWriteTransaction class]]);
// FIXME: This needs a comment from Ryan explaining why it's necessary (it has to do with push notifications)
if (!CurrentAppContext().isMainApp) { return; }
YapDatabaseReadWriteTransaction *transaction = protocolContext;

View File

@ -39,7 +39,7 @@ NSString *NSStringForUIApplicationState(UIApplicationState value);
@property (nonatomic, readonly) BOOL isMainAppAndActive;
/// Whether the app was woken up by a silent push notification. This is important for
/// determining whether attachments should be downloaded or not.
@property (nonatomic) BOOL wasWokenUpBySilentPushNotification;
@property (nonatomic) BOOL wasWokenUpByPushNotification;
// Whether the user is using a right-to-left language like Arabic.
@property (nonatomic, readonly) BOOL isRTL;