diff --git a/Session/Meta/AppDelegate.m b/Session/Meta/AppDelegate.m index 8fb513275..0e4ccd8fd 100644 --- a/Session/Meta/AppDelegate.m +++ b/Session/Meta/AppDelegate.m @@ -224,6 +224,10 @@ static NSTimeInterval launchStartedAt; OWSLogInfo(@"application: didFinishLaunchingWithOptions completed."); [self setUpCallHandling]; + if (@available(iOS 13.0, *)) { + [self registerBackgroundTasks]; + } + return YES; } diff --git a/Session/Meta/AppDelegate.swift b/Session/Meta/AppDelegate.swift index b744061c5..c6958b7cc 100644 --- a/Session/Meta/AppDelegate.swift +++ b/Session/Meta/AppDelegate.swift @@ -2,11 +2,13 @@ import PromiseKit import WebRTC import SessionUIKit import UIKit +import BackgroundTasks +import SessionUtilitiesKit extension AppDelegate { - @objc - func setUpCallHandling() { + // MARK: Call handling + @objc func setUpCallHandling() { // Offer messages MessageReceiver.handleOfferCallMessage = { message in DispatchQueue.main.async { @@ -45,6 +47,7 @@ extension AppDelegate { } } + // MARK: Configuration message @objc(syncConfigurationIfNeeded) func syncConfigurationIfNeeded() { guard Storage.shared.getUser()?.name != nil else { return } @@ -75,6 +78,7 @@ extension AppDelegate { return promise } + // MARK: Closed group poller @objc func startClosedGroupPoller() { guard OWSIdentityManager.shared().identityKeyPair() != nil else { return } ClosedGroupPoller.shared.start() @@ -84,6 +88,7 @@ extension AppDelegate { ClosedGroupPoller.shared.stop() } + // MARK: Theme @objc func getAppModeOrSystemDefault() -> AppMode { let userDefaults = UserDefaults.standard if userDefaults.dictionaryRepresentation().keys.contains("appMode") { @@ -98,4 +103,46 @@ extension AppDelegate { } } + // MARK: Background tasks + @available(iOS 13.0, *) + @objc func registerBackgroundTasks() { + BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.loki-project.loki-messenger.refresh", using: nil) { task in + self.handleAppRefresh(task: task as! BGAppRefreshTask) + } + + BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.loki-project.loki-messenger.vibrate", using: nil) { task in + Vibration.shared.startVibration() + DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 60, execute: { + Vibration.shared.stopVibrationIfPossible() + task.setTaskCompleted(success: true) + }) + } + } + + @available(iOS 13.0, *) + @objc func cancelAllPendingBGTask() { + BGTaskScheduler.shared.cancelAllTaskRequests() + } + + @available(iOS 13.0, *) + @objc func scheduleAppRefresh() { + let request = BGAppRefreshTaskRequest(identifier: "com.loki-project.loki-messenger.refresh") + // Fetch no earlier than 15 minutes from now. + request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) + + do { + try BGTaskScheduler.shared.submit(request) + } catch { + print("Could not schedule app refresh: \(error)") + } + } + + @available(iOS 13.0, *) + private func handleAppRefresh(task: BGAppRefreshTask) { + // Schedule a new refresh task. + scheduleAppRefresh() + + + } + } diff --git a/Session/Meta/Session-Info.plist b/Session/Meta/Session-Info.plist index 55fb6700d..3771b748c 100644 --- a/Session/Meta/Session-Info.plist +++ b/Session/Meta/Session-Info.plist @@ -2,6 +2,11 @@ + BGTaskSchedulerPermittedIdentifiers + + com.loki-project.loki-messenger.vibrate + com.loki-project.loki-messenger.refresh + BuildDetails CarthageVersion @@ -90,7 +95,7 @@ NSContactsUsageDescription Signal uses your contacts to find users you know. We do not store your contacts on the server. NSFaceIDUsageDescription - Session's Screen Lock feature uses Face ID. + Session's Screen Lock feature uses Face ID. NSMicrophoneUsageDescription Session needs access to your microphone to record media. NSPhotoLibraryAddUsageDescription @@ -119,6 +124,7 @@ UIBackgroundModes fetch + processing remote-notification UILaunchStoryboardName @@ -135,5 +141,7 @@ UIViewControllerBasedStatusBarAppearance + NSHumanReadableCopyright + com.loki-project.loki-messenger diff --git a/SessionNotificationServiceExtension/NotificationServiceExtension.swift b/SessionNotificationServiceExtension/NotificationServiceExtension.swift index 078db2e3f..b4eb44d02 100644 --- a/SessionNotificationServiceExtension/NotificationServiceExtension.swift +++ b/SessionNotificationServiceExtension/NotificationServiceExtension.swift @@ -1,4 +1,5 @@ import UserNotifications +import BackgroundTasks import SessionMessagingKit import SignalUtilitiesKit @@ -93,7 +94,7 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension notificationContent.badge = 1 notificationContent.title = "Session" notificationContent.body = "\(senderDisplayName) is calling..." - return self.handleSuccess(for: notificationContent) + return self.handleSuccessForIncomingCall(for: notificationContent) default: return self.completeSilenty() } if (senderPublicKey == userPublicKey) { @@ -119,7 +120,10 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension } self.handleSuccess(for: notificationContent) } catch { - self.handleFailure(for: notificationContent) + if let error = error as? MessageReceiver.Error, error.isRetryable { + self.handleFailure(for: notificationContent) + } + self.completeSilenty() } } } @@ -209,6 +213,19 @@ public final class NotificationServiceExtension : UNNotificationServiceExtension private func completeSilenty() { contentHandler!(.init()) } + + private func handleSuccessForIncomingCall(for content: UNMutableNotificationContent) { + // TODO: poll for the real offer, play incoming call ring + if #available(iOSApplicationExtension 13.0, *) { + let request = BGAppRefreshTaskRequest(identifier: "com.loki-project.loki-messenger.refresh") + do { + try BGTaskScheduler.shared.submit(request) + } catch { + print("Could not schedule app refresh: \(error)") + } + } + contentHandler!(content) + } private func handleSuccess(for content: UNMutableNotificationContent) { contentHandler!(content)