diff --git a/NotificationContent/NotificationViewController.swift b/NotificationContent/NotificationViewController.swift index 3ac6e3e46c..76abbbf2f0 100644 --- a/NotificationContent/NotificationViewController.swift +++ b/NotificationContent/NotificationViewController.swift @@ -117,7 +117,7 @@ class NotificationViewController: UIViewController, UNNotificationContentExtensi let appVersion = (Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String) ?? "unknown" - sharedAccountContext = SharedAccountContext(mainWindow: nil, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), rootPath: rootPath, apsNotificationToken: .never(), voipNotificationToken: .never()) + sharedAccountContext = SharedAccountContext(mainWindow: nil, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), rootPath: rootPath, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }) } } diff --git a/Share/ShareRootController.swift b/Share/ShareRootController.swift index 44c1b4940d..74ff67f1a2 100644 --- a/Share/ShareRootController.swift +++ b/Share/ShareRootController.swift @@ -152,7 +152,7 @@ class ShareRootController: UIViewController { }) semaphore.wait() - let sharedContext = SharedAccountContext(mainWindow: nil, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), rootPath: rootPath, apsNotificationToken: .never(), voipNotificationToken: .never()) + let sharedContext = SharedAccountContext(mainWindow: nil, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), rootPath: rootPath, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }) account = accountManager.transaction { transaction -> (SharedAccountContext, LoggingSettings) in return (sharedContext, transaction.getSharedData(SharedDataKeys.loggingSettings) as? LoggingSettings ?? LoggingSettings.defaultSettings) diff --git a/Telegram-iOS/AppDelegate.swift b/Telegram-iOS/AppDelegate.swift index 7204215291..0c2230bfe9 100644 --- a/Telegram-iOS/AppDelegate.swift +++ b/Telegram-iOS/AppDelegate.swift @@ -600,7 +600,14 @@ private final class SharedApplicationContext { self.window?.rootViewController?.dismiss(animated: true, completion: nil) }) + // Move back to signal let accountManager = AccountManager(basePath: rootPath + "/accounts-metadata") + let upgradeSemaphore = DispatchSemaphore(value: 0) + let _ = upgradedAccounts(accountManager: accountManager, rootPath: rootPath).start(completed: { + upgradeSemaphore.signal() + }) + upgradeSemaphore.wait() + var initialPresentationDataAndSettings: InitialPresentationDataAndSettings? let semaphore = DispatchSemaphore(value: 0) let _ = currentPresentationDataAndSettings(accountManager: accountManager).start(next: { value in @@ -609,7 +616,10 @@ private final class SharedApplicationContext { }) semaphore.wait() - let sharedContext = SharedAccountContext(mainWindow: self.mainWindow, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: networkArguments, rootPath: rootPath, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init)) + var setPresentationCall: ((PresentationCall?) -> Void)? + let sharedContext = SharedAccountContext(mainWindow: self.mainWindow, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: networkArguments, rootPath: rootPath, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init), setNotificationCall: { call in + setPresentationCall?(call) + }) sharedContext.presentGlobalController = { [weak self] c, a in guard let strongSelf = self else { return @@ -634,7 +644,10 @@ private final class SharedApplicationContext { } let notificationManager = SharedNotificationManager(episodeId: self.episodeId, clearNotificationsManager: clearNotificationsManager, inForeground: applicationBindings.applicationInForeground, accounts: sharedContext.activeAccounts |> map { primary, accounts, _ in Array(accounts.values.map({ ($0, $0.id == primary?.id) })) }) - let wakeupManager = SharedWakeupManager(activeAccounts: sharedContext.activeAccounts |> map { ($0.0, $0.1) }, inForeground: applicationBindings.applicationInForeground, hasActiveAudioSession: hasActiveAudioSession.get(), notificationManager: notificationManager) + setPresentationCall = { call in + notificationManager.setNotificationCall(call, strings: sharedContext.currentPresentationData.with({ $0 }).strings) + } + let wakeupManager = SharedWakeupManager(activeAccounts: sharedContext.activeAccounts |> map { ($0.0, $0.1) }, inForeground: applicationBindings.applicationInForeground, hasActiveAudioSession: hasActiveAudioSession.get(), notificationManager: notificationManager, mediaManager: sharedContext.mediaManager, callManager: sharedContext.callManager) let sharedApplicationContext = SharedApplicationContext(sharedContext: sharedContext, notificationManager: notificationManager, wakeupManager: wakeupManager) self.sharedContextPromise.set( accountManager.transaction { transaction -> (SharedApplicationContext, LoggingSettings) in diff --git a/Telegram-iOS/ApplicationContext.swift b/Telegram-iOS/ApplicationContext.swift index 0dbba5a865..9e4e4931e7 100644 --- a/Telegram-iOS/ApplicationContext.swift +++ b/Telegram-iOS/ApplicationContext.swift @@ -215,7 +215,7 @@ final class AuthorizedApplicationContext { context.account.postbox.setCanBeginTransactions(next) } })*/ - context.account.shouldExplicitelyKeepWorkerConnections.set(backgroundAudioActive) + //context.account.shouldExplicitelyKeepWorkerConnections.set(backgroundAudioActive) let cache = TGCache(cachesPath: legacyBasePath + "/Caches")! diff --git a/Telegram-iOS/SharedNotificationManager.swift b/Telegram-iOS/SharedNotificationManager.swift index 38e0d83db6..3c97781ab6 100644 --- a/Telegram-iOS/SharedNotificationManager.swift +++ b/Telegram-iOS/SharedNotificationManager.swift @@ -1,7 +1,10 @@ import Foundation +import UIKit +import UserNotifications import SwiftSignalKit import Postbox import TelegramCore +import TelegramUI private final class PollStateContext { let subscribers = Bag<(Bool) -> Void>() @@ -110,6 +113,10 @@ final class SharedNotificationManager { } let previousDisposable = context.disposable context.disposable = (account.stateManager.pollStateUpdateCompletion() + |> mapToSignal { messageIds -> Signal<[MessageId], NoError> in + return .single(messageIds) + |> delay(1.0, queue: Queue.mainQueue()) + } |> deliverOnMainQueue).start(next: { [weak self, weak context] _ in guard let strongSelf = self else { return @@ -358,4 +365,105 @@ final class SharedNotificationManager { } } } + + private var currentNotificationCall: (peer: Peer?, internalId: CallSessionInternalId)? + private func updateNotificationCall(call: (peer: Peer?, internalId: CallSessionInternalId)?, strings: PresentationStrings) { + if let previousCall = currentNotificationCall { + if #available(iOS 10.0, *) { + let center = UNUserNotificationCenter.current() + center.removeDeliveredNotifications(withIdentifiers: ["call_\(previousCall.internalId)"]) + } else { + if let notifications = UIApplication.shared.scheduledLocalNotifications { + for notification in notifications { + if let userInfo = notification.userInfo, let callId = userInfo["callId"] as? String, callId == String(describing: previousCall.internalId) { + UIApplication.shared.cancelLocalNotification(notification) + } + } + } + } + } + self.currentNotificationCall = call + + if let notificationCall = call { + let rawText = strings.PUSH_PHONE_CALL_REQUEST(notificationCall.peer?.displayTitle ?? "").0 + let title: String? + let body: String + if let index = rawText.firstIndex(of: "|") { + title = String(rawText[rawText.startIndex ..< index]) + body = String(rawText[rawText.index(after: index)...]) + } else { + title = nil + body = rawText + } + + if #available(iOS 10.0, *) { + let content = UNMutableNotificationContent() + if let title = title { + content.title = title + } + content.body = body + content.sound = UNNotificationSound(named: "0.m4a") + content.categoryIdentifier = "incomingCall" + content.userInfo = [:] + + let request = UNNotificationRequest(identifier: "call_\(notificationCall.internalId)", content: content, trigger: nil) + + let center = UNUserNotificationCenter.current() + Logger.shared.log("NotificationManager", "adding call \(notificationCall.internalId)") + center.add(request, withCompletionHandler: { error in + if let error = error { + Logger.shared.log("NotificationManager", "error adding call \(notificationCall.internalId), error: \(String(describing: error))") + } + }) + + } else { + let notification = UILocalNotification() + if #available(iOS 8.2, *) { + notification.alertTitle = title + notification.alertBody = body + } else { + if let title = title { + notification.alertBody = "\(title): \(body)" + } else { + notification.alertBody = body + } + } + notification.category = "incomingCall" + notification.userInfo = ["callId": String(describing: notificationCall.internalId)] + notification.soundName = "0.m4a" + UIApplication.shared.presentLocalNotificationNow(notification) + } + } + } + + private let notificationCallStateDisposable = MetaDisposable() + private(set) var notificationCall: PresentationCall? + + func setNotificationCall(_ call: PresentationCall?, strings: PresentationStrings) { + if self.notificationCall?.internalId != call?.internalId { + self.notificationCall = call + if let notificationCall = self.notificationCall { + let peer = notificationCall.peer + let internalId = notificationCall.internalId + let isIntegratedWithCallKit = notificationCall.isIntegratedWithCallKit + self.notificationCallStateDisposable.set((notificationCall.state + |> map { state -> (Peer?, CallSessionInternalId)? in + if isIntegratedWithCallKit { + return nil + } + if case .ringing = state { + return (peer, internalId) + } else { + return nil + } + } + |> distinctUntilChanged(isEqual: { $0?.1 == $1?.1 })).start(next: { [weak self] peerAndInternalId in + self?.updateNotificationCall(call: peerAndInternalId, strings: strings) + })) + } else { + self.notificationCallStateDisposable.set(nil) + self.updateNotificationCall(call: nil, strings: strings) + } + } + } } diff --git a/Telegram-iOS/SharedWakeupManager.swift b/Telegram-iOS/SharedWakeupManager.swift index 95b97d5014..23d4b33310 100644 --- a/Telegram-iOS/SharedWakeupManager.swift +++ b/Telegram-iOS/SharedWakeupManager.swift @@ -3,12 +3,15 @@ import UIKit import SwiftSignalKit import Postbox import TelegramCore +import TelegramUI private struct AccountTasks { let stateSynchronization: Bool let importantTasks: AccountRunningImportantTasks let backgroundLocation: Bool let backgroundDownloads: Bool + let backgroundAudio: Bool + let activeCalls: Bool var isEmpty: Bool { if self.stateSynchronization { @@ -23,6 +26,12 @@ private struct AccountTasks { if self.backgroundDownloads { return false } + if self.backgroundAudio { + return false + } + if self.activeCalls { + return false + } return true } } @@ -40,7 +49,7 @@ final class SharedWakeupManager { private var accountsAndTasks: [(Account, Bool, AccountTasks)] = [] - init(activeAccounts: Signal<(primary: Account?, accounts: [AccountRecordId: Account]), NoError>, inForeground: Signal, hasActiveAudioSession: Signal, notificationManager: SharedNotificationManager) { + init(activeAccounts: Signal<(primary: Account?, accounts: [AccountRecordId: Account]), NoError>, inForeground: Signal, hasActiveAudioSession: Signal, notificationManager: SharedNotificationManager, mediaManager: MediaManager, callManager: PresentationCallManager?) { assert(Queue.mainQueue().isCurrent()) self.inForegroundDisposable = (inForeground @@ -65,9 +74,37 @@ final class SharedWakeupManager { |> deliverOnMainQueue |> mapToSignal { primary, accounts -> Signal<[(Account, Bool, AccountTasks)], NoError> in let signals: [Signal<(Account, Bool, AccountTasks), NoError>] = accounts.values.map { account in - return combineLatest(queue: .mainQueue(), account.importantTasksRunning, notificationManager.isPollingState(accountId: account.id)) - |> map { importantTasksRunning, isPollingState -> (Account, Bool, AccountTasks) in - return (account, primary?.id == account.id, AccountTasks(stateSynchronization: isPollingState, importantTasks: importantTasksRunning, backgroundLocation: false, backgroundDownloads: false)) + let hasActiveMedia = mediaManager.activeGlobalMediaPlayerAccountId + |> map { id -> Bool in + return id == account.id + } + |> distinctUntilChanged + let isPlayingBackgroundAudio = combineLatest(queue: .mainQueue(), hasActiveMedia, hasActiveAudioSession) + |> map { hasActiveMedia, hasActiveAudioSession -> Bool in + return hasActiveMedia && hasActiveAudioSession + } + |> distinctUntilChanged + + let hasActiveCalls = (callManager?.currentCallSignal ?? .single(nil)) + |> map { call in + return call?.account.id == account.id + } + |> distinctUntilChanged + let isPlayingBackgroundActiveCall = combineLatest(queue: .mainQueue(), hasActiveCalls, hasActiveAudioSession) + |> map { hasActiveCalls, hasActiveAudioSession -> Bool in + return hasActiveCalls && hasActiveAudioSession + } + |> distinctUntilChanged + + let hasActiveAudio = combineLatest(queue: .mainQueue(), isPlayingBackgroundAudio, isPlayingBackgroundActiveCall) + |> map { isPlayingBackgroundAudio, isPlayingBackgroundActiveCall in + return isPlayingBackgroundAudio || isPlayingBackgroundActiveCall + } + |> distinctUntilChanged + + return combineLatest(queue: .mainQueue(), account.importantTasksRunning, notificationManager.isPollingState(accountId: account.id), hasActiveAudio, hasActiveCalls) + |> map { importantTasksRunning, isPollingState, hasActiveAudio, hasActiveCalls -> (Account, Bool, AccountTasks) in + return (account, primary?.id == account.id, AccountTasks(stateSynchronization: isPollingState, importantTasks: importantTasksRunning, backgroundLocation: false, backgroundDownloads: false, backgroundAudio: hasActiveAudio, activeCalls: hasActiveCalls)) } } return combineLatest(signals) @@ -165,11 +202,12 @@ final class SharedWakeupManager { private func updateAccounts() { if self.inForeground || self.hasActiveAudioSession || self.isInBackgroundExtension { for (account, primary, tasks) in self.accountsAndTasks { - if primary || !tasks.isEmpty { + if (self.inForeground && primary) || !tasks.isEmpty { account.shouldBeServiceTaskMaster.set(.single(.always)) } else { account.shouldBeServiceTaskMaster.set(.single(.never)) } + account.shouldExplicitelyKeepWorkerConnections.set(.single(tasks.backgroundAudio)) account.shouldKeepOnlinePresence.set(.single(primary && self.inForeground)) account.shouldKeepBackgroundDownloadConnections.set(.single(tasks.backgroundDownloads)) } diff --git a/submodules/Display b/submodules/Display index 076af3926c..947810ca9c 160000 --- a/submodules/Display +++ b/submodules/Display @@ -1 +1 @@ -Subproject commit 076af3926c4a5d55bcad4ffece41464059c891d9 +Subproject commit 947810ca9cef45f04497d064ea6be876cfb524f5 diff --git a/submodules/Postbox b/submodules/Postbox index 57821c9c36..1f94086baf 160000 --- a/submodules/Postbox +++ b/submodules/Postbox @@ -1 +1 @@ -Subproject commit 57821c9c362f56e8ea652b51086fda516e791999 +Subproject commit 1f94086baf4e43cc5c459aab1f56027e3bf4edf3 diff --git a/submodules/TelegramCore b/submodules/TelegramCore index 3e4cc1a26f..0bf9357d91 160000 --- a/submodules/TelegramCore +++ b/submodules/TelegramCore @@ -1 +1 @@ -Subproject commit 3e4cc1a26fb677682de98117cf4c918331f5160d +Subproject commit 0bf9357d914ced42ee61414670671da3083e327a diff --git a/submodules/TelegramUI b/submodules/TelegramUI index 867d73ba99..112d1ed974 160000 --- a/submodules/TelegramUI +++ b/submodules/TelegramUI @@ -1 +1 @@ -Subproject commit 867d73ba99f1caac56d997762415948de633ca8d +Subproject commit 112d1ed974497fadee7220ee4552035c0cfd113d