Merge pull request #233 from loki-project/notifications
Fix Push Notification Bugs
This commit is contained in:
commit
3075e82d8a
|
@ -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
|
||||
|
|
|
@ -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 }
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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(§ionChanges, 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) {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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:)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue