diff --git a/Signal/src/AppDelegate.m b/Signal/src/AppDelegate.m index 29ba36141..d91a2e887 100644 --- a/Signal/src/AppDelegate.m +++ b/Signal/src/AppDelegate.m @@ -145,11 +145,11 @@ } -(void)applicationDidBecomeActive:(UIApplication *)application { - if ([TSAccountManager isRegistered] && [self applicationIsActive]) { + if ([TSAccountManager isRegistered]) { // We're double checking that the app is active, to be sure since we can't verify in production env due to code signing. [TSSocketManager becomeActiveFromForeground]; } - + [self removeScreenProtection]; } @@ -159,6 +159,7 @@ if ([TSAccountManager isRegistered]) { [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; [self updateBadge]; + [TSSocketManager resignActivity]; } } @@ -168,10 +169,6 @@ } } -- (void)applicationDidEnterBackground:(UIApplication *)application{ - [TSSocketManager resignActivity]; -} - - (void)prepareScreenshotProtection{ self.blankWindow = ({ UIWindow *window = [[UIWindow alloc] initWithFrame:self.window.bounds]; diff --git a/Signal/src/network/PushManager.m b/Signal/src/network/PushManager.m index a9d9c770d..d1a225d2e 100644 --- a/Signal/src/network/PushManager.m +++ b/Signal/src/network/PushManager.m @@ -174,8 +174,8 @@ } -(void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type -{ - [self application:[UIApplication sharedApplication] didReceiveRemoteNotification:payload.dictionaryPayload]; +{ + [self application:[UIApplication sharedApplication] didReceiveRemoteNotification:payload.dictionaryPayload]; } - (TOCFuture*)registerPushKitNotificationFuture{ @@ -374,7 +374,7 @@ - (TOCFuture *)registerForPushFutureWithToken:(NSData *)pushToken voipToken:(NSData*)voipToken { self.registerWithServerFutureSource = [TOCFutureSource new]; - + [RPServerRequestsManager.sharedInstance performRequest:[RPAPICall registerPushNotificationWithPushToken:pushToken voipToken:voipToken] success:^(NSURLSessionDataTask *task, id responseObject) { if ([task.response isKindOfClass:NSHTTPURLResponse.class]) { diff --git a/Signal/src/textsecure/Messages/TSMessagesManager.m b/Signal/src/textsecure/Messages/TSMessagesManager.m index 0436c5b5c..c8b707738 100644 --- a/Signal/src/textsecure/Messages/TSMessagesManager.m +++ b/Signal/src/textsecure/Messages/TSMessagesManager.m @@ -413,46 +413,46 @@ NSString *messageDescription = message.description; if ([UIApplication sharedApplication].applicationState != UIApplicationStateActive && messageDescription) { - UILocalNotification *notification = [[UILocalNotification alloc] init]; - notification.category = Signal_Message_Category; - notification.userInfo = @{Signal_Thread_UserInfo_Key:thread.uniqueId}; - - switch ([[Environment preferences] notificationPreviewType]) { - case NotificationNamePreview: - if ([thread isGroupThread]) { - NSString *sender = [[Environment getCurrent].contactsManager nameStringForPhoneIdentifier:message.authorId]; - if (!sender) { - sender = message.authorId; - } - - notification.alertBody = [NSString stringWithFormat:@"New message from %@ in group \"%@\": %@", sender, name, messageDescription]; - } else { - notification.alertBody = [NSString stringWithFormat:@"%@: %@", name, messageDescription]; - } - break; - case NotificationNameNoPreview:{ - if ([thread isGroupThread]) { + UILocalNotification *notification = [[UILocalNotification alloc] init]; + notification.category = Signal_Message_Category; + notification.userInfo = @{Signal_Thread_UserInfo_Key:thread.uniqueId}; + notification.soundName = @"NewMessage.aifc"; + + switch ([[Environment preferences] notificationPreviewType]) { + case NotificationNamePreview: + if ([thread isGroupThread]) { + NSString *sender = [[Environment getCurrent].contactsManager nameStringForPhoneIdentifier:message.authorId]; + if (!sender) { + sender = message.authorId; + } + + notification.alertBody = [NSString stringWithFormat:@"New message from %@ in group \"%@\": %@", sender, name, messageDescription]; + } else { + notification.alertBody = [NSString stringWithFormat:@"%@: %@", name, messageDescription]; + } + break; + case NotificationNameNoPreview:{ + if ([thread isGroupThread]) { notification.alertBody = [NSString stringWithFormat:@"%@ \"%@\"", NSLocalizedString(@"APN_MESSAGE_IN_GROUP",nil), name]; - } else { + } else { notification.alertBody = [NSString stringWithFormat:@"%@ %@", NSLocalizedString(@"APN_MESSAGE_FROM", nil), name]; - } - break; - } - case NotificationNoNameNoPreview: - notification.alertBody = NSLocalizedString(@"APN_Message", nil); - break; - default: - notification.alertBody = NSLocalizedString(@"APN_Message", nil); - break; - } - - [[UIApplication sharedApplication] presentLocalNotificationNow:notification]; - AudioServicesPlayAlertSound(_newMessageSound); - } else { - if ([Environment.preferences soundInForeground]) { - AudioServicesPlayAlertSound(_newMessageSound); - } - } + } + break; + } + case NotificationNoNameNoPreview: + notification.alertBody = NSLocalizedString(@"APN_Message", nil); + break; + default: + notification.alertBody = NSLocalizedString(@"APN_Message", nil); + break; + } + + [[UIApplication sharedApplication] presentLocalNotificationNow:notification]; + } else { + if ([Environment.preferences soundInForeground]) { + AudioServicesPlayAlertSound(_newMessageSound); + } + } } - (void)dealloc { diff --git a/Signal/src/textsecure/Network/WebSockets/TSSocketManager.m b/Signal/src/textsecure/Network/WebSockets/TSSocketManager.m index d1540a164..ad77cebd9 100644 --- a/Signal/src/textsecure/Network/WebSockets/TSSocketManager.m +++ b/Signal/src/textsecure/Network/WebSockets/TSSocketManager.m @@ -22,6 +22,7 @@ #define kWebSocketHeartBeat 30 #define kWebSocketReconnectTry 5 #define kBackgroundConnectTimer 120 +#define kBackgroundConnectKeepAlive 20 NSString * const SocketOpenedNotification = @"SocketOpenedNotification"; NSString * const SocketClosedNotification = @"SocketClosedNotification"; @@ -30,11 +31,11 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification"; @interface TSSocketManager () @property (nonatomic, retain) NSTimer *pingTimer; @property (nonatomic, retain) NSTimer *reconnectTimer; +@property (nonatomic, retain) NSTimer *backgroundKeepAliveTimer; @property (nonatomic, retain) SRWebSocket *websocket; @property (nonatomic) SocketStatus status; -@property (nonatomic, retain) NSTimer *backgroundConnectTimer; @property (nonatomic) UIBackgroundTaskIdentifier fetchingTaskIdentifier; @property BOOL didFetchInBackground; @@ -59,6 +60,8 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification"; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ sharedMyManager = [[self alloc] init]; + sharedMyManager.fetchingTaskIdentifier = UIBackgroundTaskInvalid; + sharedMyManager.didFetchInBackground = FALSE; }); return sharedMyManager; } @@ -112,26 +115,31 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification"; #pragma mark - Delegate methods -- (void) webSocketDidOpen:(SRWebSocket *)webSocket { +- (void)webSocketDidOpen:(SRWebSocket *)webSocket { self.pingTimer = [NSTimer scheduledTimerWithTimeInterval:kWebSocketHeartBeat target:self selector:@selector(webSocketHeartBeat) userInfo:nil repeats:YES]; + + // Additionally, we want the ping timer to work in the background too. + [[NSRunLoop mainRunLoop] addTimer:self.pingTimer + forMode:NSDefaultRunLoopMode]; + self.status = kSocketStatusOpen; [self.reconnectTimer invalidate]; self.reconnectTimer = nil; } -- (void) webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { +- (void)webSocket:(SRWebSocket *)webSocket didFailWithError:(NSError *)error { DDLogError(@"Error connecting to socket %@", error); [self.pingTimer invalidate]; self.status = kSocketStatusClosed; [self scheduleRetry]; } -- (void) webSocket:(SRWebSocket *)webSocket didReceiveMessage:(NSData*)data { +- (void)webSocket:(SRWebSocket *)webSocket didReceiveMessage:(NSData*)data { WebSocketMessage *wsMessage = [WebSocketMessage parseFromData:data]; self.didFetchInBackground = YES; @@ -149,6 +157,8 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification"; [self sendWebSocketMessageAcknowledgement:message]; + [self keepAliveBackground]; + if ([message.path isEqualToString:@"/api/v1/message"] && [message.verb isEqualToString:@"PUT"]){ NSData *decryptedPayload = [Cryptography decryptAppleMessagePayload:message.body withSignalingKey:TSStorageManager.signalingKey]; @@ -166,6 +176,22 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification"; } } +- (void)keepAliveBackground { + if (self.fetchingTaskIdentifier) { + [self.backgroundKeepAliveTimer invalidate]; + + self.backgroundKeepAliveTimer = [NSTimer scheduledTimerWithTimeInterval:kBackgroundConnectKeepAlive + target:self + selector:@selector(backgroundTimeExpired) + userInfo:nil + repeats:NO]; + // Additionally, we want the reconnect timer to work in the background too. + [[NSRunLoop mainRunLoop] addTimer:self.backgroundKeepAliveTimer + forMode:NSDefaultRunLoopMode]; + + } +} + - (void)processWebSocketResponseMessage:(WebSocketResponseMessage*)message { DDLogWarn(@"Client should not receive WebSocket Respond messages"); } @@ -215,6 +241,9 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification"; selector:@selector(becomeActive) userInfo:nil repeats:YES]; + // Additionally, we want the reconnect timer to work in the background too. + [[NSRunLoop mainRunLoop] addTimer:self.reconnectTimer + forMode:NSDefaultRunLoopMode]; } } @@ -222,10 +251,11 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification"; + (void)becomeActiveFromForeground { TSSocketManager *sharedInstance = [self sharedManager]; + [sharedInstance.backgroundKeepAliveTimer invalidate]; - [sharedInstance.backgroundConnectTimer invalidate]; - sharedInstance.backgroundConnectTimer = nil; - sharedInstance.fetchingTaskIdentifier = 0; + if (sharedInstance.fetchingTaskIdentifier != UIBackgroundTaskInvalid) { + [sharedInstance closeBackgroundTask]; + } [self becomeActive]; } @@ -234,25 +264,34 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification"; + (void)becomeActiveFromBackground { TSSocketManager *sharedInstance = [TSSocketManager sharedManager]; - if (sharedInstance.fetchingTaskIdentifier == 0) { + if (sharedInstance.fetchingTaskIdentifier == UIBackgroundTaskInvalid) { + [sharedInstance.backgroundKeepAliveTimer invalidate]; sharedInstance.didFetchInBackground = NO; sharedInstance.fetchingTaskIdentifier = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{ - if (!sharedInstance.didFetchInBackground) { - [sharedInstance backgroundConnectTimedOut]; - } - sharedInstance.fetchingTaskIdentifier = 0; [TSSocketManager resignActivity]; + [sharedInstance closeBackgroundTask]; }]; [self becomeActive]; + } else { + DDLogWarn(@"Got called to become active in the background but there was already a background task running."); } } +- (void)backgroundTimeExpired { + [[self class] resignActivity]; + [self closeBackgroundTask]; +} + - (void)closeBackgroundTask { UIBackgroundTaskIdentifier identifier = self.fetchingTaskIdentifier; - self.fetchingTaskIdentifier = 0; + self.fetchingTaskIdentifier = UIBackgroundTaskInvalid; + [self.backgroundKeepAliveTimer invalidate]; + + if (!self.didFetchInBackground) { + [self backgroundConnectTimedOut]; + } - [TSSocketManager resignActivity]; [[UIApplication sharedApplication] endBackgroundTask:identifier]; } @@ -262,7 +301,6 @@ NSString * const SocketConnectingNotification = @"SocketConnectingNotification"; [[UIApplication sharedApplication] presentLocalNotificationNow:notification]; } - #pragma mark UI Delegates - (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context { diff --git a/Signal/translations/en.lproj/Localizable.strings b/Signal/translations/en.lproj/Localizable.strings index 8852f1663..de5351703 100644 Binary files a/Signal/translations/en.lproj/Localizable.strings and b/Signal/translations/en.lproj/Localizable.strings differ