diff --git a/Share/ShareRootController.swift b/Share/ShareRootController.swift index d94db8fe34..c98ac3c643 100644 --- a/Share/ShareRootController.swift +++ b/Share/ShareRootController.swift @@ -5,7 +5,7 @@ import TelegramUI import SwiftSignalKit import Postbox -private var accountCache: (Account, AccountManager)? +private var accountCache: (SharedAccountContext, Account)? private var installedSharedLogger = false @@ -136,7 +136,7 @@ class ShareRootController: UIViewController { }, dismissNativeController: { }) - let account: Signal<(Account, AccountManager), ShareAuthorizationError> + let account: Signal<(SharedAccountContext, Account), ShareAuthorizationError> if let accountCache = accountCache { account = .single(accountCache) } else { @@ -145,33 +145,38 @@ class ShareRootController: UIViewController { initializeAccountManagement() account = accountManager(basePath: rootPath + "/accounts-metadata") |> take(1) - |> mapToSignal { accountManager -> Signal<(AccountManager, LoggingSettings), NoError> in - return accountManager.transaction { transaction -> (AccountManager, LoggingSettings) in - return (accountManager, transaction.getSharedData(SharedDataKeys.loggingSettings) as? LoggingSettings ?? LoggingSettings.defaultSettings) + |> mapToSignal { accountManager -> Signal<(SharedAccountContext, LoggingSettings), NoError> in + let sharedContext = SharedAccountContext(accountManager: accountManager, applicationBindings: applicationBindings, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), rootPath: rootPath, apsNotificationToken: .never(), voipNotificationToken: .never()) + + return accountManager.transaction { transaction -> (SharedAccountContext, LoggingSettings) in + return (sharedContext, transaction.getSharedData(SharedDataKeys.loggingSettings) as? LoggingSettings ?? LoggingSettings.defaultSettings) } } |> introduceError(ShareAuthorizationError.self) - |> mapToSignal { accountManager, loggingSettings -> Signal<(Account, AccountManager), ShareAuthorizationError> in + |> mapToSignal { sharedContext, loggingSettings -> Signal<(SharedAccountContext, Account), ShareAuthorizationError> in Logger.shared.logToFile = loggingSettings.logToFile Logger.shared.logToConsole = loggingSettings.logToConsole Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData - return currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: telegramAccountAuxiliaryMethods) - |> introduceError(ShareAuthorizationError.self) |> mapToSignal { account -> Signal<(Account, AccountManager), ShareAuthorizationError> in + preconditionFailure() + + /*return currentAccount(allocateIfNotExists: false, networkArguments: , supplementary: true, manager: sharedContext.accountManager, rootPath: rootPath, auxiliaryMethods: telegramAccountAuxiliaryMethods) + |> introduceError(ShareAuthorizationError.self) + |> mapToSignal { account -> Signal<(SharedAccountContext, Account), ShareAuthorizationError> in if let account = account { switch account { case .upgrading: return .complete() case let .authorized(account): - return .single((account, accountManager)) + return .single((sharedContext, account)) case .unauthorized: return .fail(.unauthorized) } } else { return .complete() } - } + }*/ } |> take(1) } @@ -180,14 +185,14 @@ class ShareRootController: UIViewController { let shouldBeMaster = self.shouldBeMaster let applicationInterface = account - |> mapToSignal { account, accountManager -> Signal<(AccountContext, PostboxAccessChallengeData), ShareAuthorizationError> in + |> mapToSignal { sharedContext, account -> Signal<(AccountContext, PostboxAccessChallengeData), ShareAuthorizationError> in return combineLatest(currentPresentationDataAndSettings(postbox: account.postbox), account.postbox.combinedView(keys: [.accessChallengeData, preferencesKey]) |> take(1)) |> deliverOnMainQueue |> introduceError(ShareAuthorizationError.self) |> map { dataAndSettings, data -> (AccountContext, PostboxAccessChallengeData) in - accountCache = (account, accountManager) + accountCache = (sharedContext, account) updateLegacyLocalization(strings: dataAndSettings.presentationData.strings) - let context = AccountContext(account: account, applicationBindings: applicationBindings, accountManager: accountManager, initialPresentationDataAndSettings: dataAndSettings, postbox: account.postbox) + let context = AccountContext(sharedContext: sharedContext, account: account, initialPresentationDataAndSettings: dataAndSettings) return (context, (data.views[.accessChallengeData] as! AccessChallengeDataView).data) } } diff --git a/Telegram-iOS/AppDelegate.swift b/Telegram-iOS/AppDelegate.swift index b9ee65d047..61160a9e0c 100644 --- a/Telegram-iOS/AppDelegate.swift +++ b/Telegram-iOS/AppDelegate.swift @@ -134,7 +134,7 @@ private enum QueuedWakeup: Int32 { private var isActiveValue = false let hasActiveAudioSession = Promise(false) - private let accountManagerPromise = Promise() + private let sharedContextPromise = Promise() private let watchCommunicationManagerPromise = Promise() private var contextValue: ApplicationContext? @@ -444,86 +444,6 @@ private enum QueuedWakeup: Int32 { self.hasActiveAudioSession.set(MediaManager.globalAudioSession.isActive()) initializeAccountManagement() - self.accountManagerPromise.set(accountManager(basePath: rootPath + "/accounts-metadata") - |> mapToSignal { accountManager -> Signal<(AccountManager, LoggingSettings), NoError> in - return accountManager.transaction { transaction -> (AccountManager, LoggingSettings) in - return (accountManager, transaction.getSharedData(SharedDataKeys.loggingSettings) as? LoggingSettings ?? LoggingSettings.defaultSettings) - } - } - |> mapToSignal { accountManager, loggingSettings -> Signal in - AccountStore.initialize(accountManager: accountManager) - - Logger.shared.logToFile = loggingSettings.logToFile - Logger.shared.logToConsole = loggingSettings.logToConsole - Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData - - return importedLegacyAccount(basePath: appGroupUrl.path, accountManager: accountManager, present: { controller in - self.window?.rootViewController?.present(controller, animated: true, completion: nil) - }) - |> `catch` { _ -> Signal in - return Signal { subscriber in - let alertView = UIAlertView(title: "", message: "An error occured while trying to upgrade application data. Would you like to logout?", delegate: self, cancelButtonTitle: "No", otherButtonTitles: "Yes") - self.alertActions = (primary: { - let statusPath = appGroupUrl.path + "/Documents/importcompleted" - let _ = try? FileManager.default.createDirectory(atPath: appGroupUrl.path + "/Documents", withIntermediateDirectories: true, attributes: nil) - let _ = try? Data().write(to: URL(fileURLWithPath: statusPath)) - subscriber.putNext(.result(nil)) - subscriber.putCompletion() - }, other: { - exit(0) - }) - alertView.show() - - return EmptyDisposable - } |> runOn(Queue.mainQueue()) - } - |> mapToSignal { event -> Signal in - switch event { - case let .progress(type, value): - Queue.mainQueue().async { - if self.dataImportSplash == nil { - self.dataImportSplash = LegacyDataImportSplash() - self.dataImportSplash?.serviceAction = { - self.debugPressed() - } - self.mainWindow.coveringView = self.dataImportSplash - } - self.dataImportSplash?.progress = (type, value) - } - return .complete() - case let .result(temporaryId): - Queue.mainQueue().async { - if let _ = self.dataImportSplash { - self.dataImportSplash = nil - self.mainWindow.coveringView = nil - } - } - if let temporaryId = temporaryId { - Queue.mainQueue().after(1.0, { - let statusPath = appGroupUrl.path + "/Documents/importcompleted" - let _ = try? FileManager.default.createDirectory(atPath: appGroupUrl.path + "/Documents", withIntermediateDirectories: true, attributes: nil) - let _ = try? Data().write(to: URL(fileURLWithPath: statusPath)) - }) - return accountManager.transaction { transaction -> AccountManager in - transaction.setCurrentId(temporaryId) - transaction.updateRecord(temporaryId, { record in - if let record = record { - return AccountRecord(id: record.id, attributes: record.attributes, temporarySessionId: nil) - } - return record - }) - return accountManager - } - } - return .single(accountManager) - } - } - }) - - let _ = (self.accountManagerPromise.get() - |> mapToSignal { manager in - return managedCleanupAccounts(networkArguments: networkArguments, accountManager: manager, rootPath: rootPath, auxiliaryMethods: telegramAccountAuxiliaryMethods) - }).start() let applicationBindings = TelegramApplicationBindings(isMainApp: true, containerPath: appGroupUrl.path, appSpecificScheme: BuildConfig.shared().appSpecificUrlScheme, openUrl: { url in var parsedUrl = URL(string: url) @@ -663,25 +583,143 @@ private enum QueuedWakeup: Int32 { self.window?.rootViewController?.dismiss(animated: true, completion: nil) }) - let watchManagerArgumentsPromise = Promise() - - self.context.set(self.accountManagerPromise.get() + self.sharedContextPromise.set(accountManager(basePath: rootPath + "/accounts-metadata") |> deliverOnMainQueue - |> mapToSignal { accountManager -> Signal in - let replyFromNotificationsActive = self.replyFromNotificationsTokensPromise.get() - |> map { - !$0.isEmpty + |> mapToSignal { accountManager -> Signal<(SharedAccountContext, LoggingSettings), NoError> in + let sharedContext = SharedAccountContext(accountManager: accountManager, applicationBindings: applicationBindings, networkArguments: networkArguments, rootPath: rootPath, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init)) + return accountManager.transaction { transaction -> (SharedAccountContext, LoggingSettings) in + return (sharedContext, transaction.getSharedData(SharedDataKeys.loggingSettings) as? LoggingSettings ?? LoggingSettings.defaultSettings) } - |> distinctUntilChanged - return applicationContext(networkArguments: networkArguments, applicationBindings: applicationBindings, replyFromNotificationsActive: replyFromNotificationsActive, backgroundAudioActive: self.hasActiveAudioSession.get() |> distinctUntilChanged, watchManagerArguments: watchManagerArgumentsPromise.get(), accountManager: accountManager, rootPath: rootPath, legacyBasePath: appGroupUrl.path, mainWindow: self.mainWindow, reinitializedNotificationSettings: { - let _ = (self.context.get() - |> take(1) - |> deliverOnMainQueue).start(next: { value in - if let value = value, case let .authorized(context) = value { - self.registerForNotifications(context: context.context, authorize: false) - } - }) + } + |> mapToSignal { sharedContext, loggingSettings -> Signal in + Logger.shared.logToFile = loggingSettings.logToFile + Logger.shared.logToConsole = loggingSettings.logToConsole + Logger.shared.redactSensitiveData = loggingSettings.redactSensitiveData + + return importedLegacyAccount(basePath: appGroupUrl.path, accountManager: sharedContext.accountManager, present: { controller in + self.window?.rootViewController?.present(controller, animated: true, completion: nil) }) + |> `catch` { _ -> Signal in + return Signal { subscriber in + let alertView = UIAlertView(title: "", message: "An error occured while trying to upgrade application data. Would you like to logout?", delegate: self, cancelButtonTitle: "No", otherButtonTitles: "Yes") + self.alertActions = (primary: { + let statusPath = appGroupUrl.path + "/Documents/importcompleted" + let _ = try? FileManager.default.createDirectory(atPath: appGroupUrl.path + "/Documents", withIntermediateDirectories: true, attributes: nil) + let _ = try? Data().write(to: URL(fileURLWithPath: statusPath)) + subscriber.putNext(.result(nil)) + subscriber.putCompletion() + }, other: { + exit(0) + }) + alertView.show() + + return EmptyDisposable + } |> runOn(Queue.mainQueue()) + } + |> mapToSignal { event -> Signal in + switch event { + case let .progress(type, value): + Queue.mainQueue().async { + if self.dataImportSplash == nil { + self.dataImportSplash = LegacyDataImportSplash() + self.dataImportSplash?.serviceAction = { + self.debugPressed() + } + self.mainWindow.coveringView = self.dataImportSplash + } + self.dataImportSplash?.progress = (type, value) + } + return .complete() + case let .result(temporaryId): + Queue.mainQueue().async { + if let _ = self.dataImportSplash { + self.dataImportSplash = nil + self.mainWindow.coveringView = nil + } + } + if let temporaryId = temporaryId { + Queue.mainQueue().after(1.0, { + let statusPath = appGroupUrl.path + "/Documents/importcompleted" + let _ = try? FileManager.default.createDirectory(atPath: appGroupUrl.path + "/Documents", withIntermediateDirectories: true, attributes: nil) + let _ = try? Data().write(to: URL(fileURLWithPath: statusPath)) + }) + return sharedContext.accountManager.transaction { transaction -> SharedAccountContext in + transaction.setCurrentId(temporaryId) + transaction.updateRecord(temporaryId, { record in + if let record = record { + return AccountRecord(id: record.id, attributes: record.attributes, temporarySessionId: nil) + } + return record + }) + return sharedContext + } + } else { + return .single(sharedContext) + } + } + } + }) + + let watchManagerArgumentsPromise = Promise() + + let replyFromNotificationsActive = self.replyFromNotificationsTokensPromise.get() + |> map { + !$0.isEmpty + } + |> distinctUntilChanged + + let backgroundAudioActive = self.hasActiveAudioSession.get() + |> distinctUntilChanged + + self.context.set(self.sharedContextPromise.get() + |> deliverOnMainQueue + |> mapToSignal { sharedContext -> Signal in + return sharedContext.activeAccounts + |> map { primary, _, auth -> (AnyObject?, Bool) in + return (auth ?? primary, primary != nil) + } + |> distinctUntilChanged(isEqual: { lhs, rhs in + if lhs.0 !== rhs.0 { + return false + } + return true + }) + |> mapToSignal { account, hasOther -> Signal<(AnyObject, InitialPresentationDataAndSettings, Bool)?, NoError> in + if let account = account as? Account { + return currentPresentationDataAndSettings(postbox: account.postbox) + |> map { initialSettings in + return (account, initialSettings, hasOther) + } + } else if let account = account as? UnauthorizedAccount { + return currentPresentationDataAndSettings(postbox: account.postbox) + |> map { initialSettings in + return (account, initialSettings, hasOther) + } + } else { + return .single(nil) + } + } + |> deliverOnMainQueue + |> map { accountAndSettings -> ApplicationContext? in + return accountAndSettings.flatMap { account, initialSettings, hasOther in + if let account = account as? Account { + let context = AccountContext(sharedContext: sharedContext, account: account, initialPresentationDataAndSettings: initialSettings) + return ApplicationContext.authorized(AuthorizedApplicationContext(mainWindow: self.mainWindow, replyFromNotificationsActive: replyFromNotificationsActive, backgroundAudioActive: backgroundAudioActive, watchManagerArguments: watchManagerArgumentsPromise.get(), context: context, accountManager: sharedContext.accountManager, legacyBasePath: appGroupUrl.path, showCallsTab: initialSettings.callListSettings.showTab, reinitializedNotificationSettings: { + let _ = (self.context.get() + |> take(1) + |> deliverOnMainQueue).start(next: { value in + if let value = value, case let .authorized(context) = value { + self.registerForNotifications(context: context.context, authorize: false) + } + }) + })) + } else if let account = account as? UnauthorizedAccount { + return ApplicationContext.unauthorized(UnauthorizedApplicationContext(sharedContext: sharedContext, account: account, hasOtherAccounts: hasOther)) + } else { + return nil + } + } + } }) let contextReadyDisposable = MetaDisposable() @@ -695,8 +733,6 @@ private enum QueuedWakeup: Int32 { network = unauthorized.account.network case let .authorized(authorized): network = authorized.context.account.network - default: - break } } @@ -712,8 +748,6 @@ private enum QueuedWakeup: Int32 { authorized.context.account.shouldKeepOnlinePresence.set(.single(false)) authorized.context.account.shouldExplicitelyKeepWorkerConnections.set(.single(false)) authorized.context.account.shouldKeepBackgroundDownloadConnections.set(.single(false)) - default: - break } } self.contextValue = context @@ -743,14 +777,13 @@ private enum QueuedWakeup: Int32 { authorizeNotifications = false } self.registerForNotifications(context: context.context, authorize: authorizeNotifications) - context.context.account.notificationToken.set(self.notificationTokenPromise.get()) - context.context.account.voipToken.set(self.voipTokenPromise.get()) case .unauthorized: break } })) } else { self.mainWindow.viewController = nil + self.mainWindow.topLevelOverlayControllers = [] contextReadyDisposable.set(nil) } })) @@ -1311,8 +1344,6 @@ private enum QueuedWakeup: Int32 { } else if let confirmationCode = parseConfirmationCodeUrl(url) { context.rootController.applyConfirmationCode(confirmationCode) } - default: - break } }) } @@ -1324,7 +1355,7 @@ private enum QueuedWakeup: Int32 { if let handle = contact.personHandle?.value { if let userId = Int32(handle) { if let contextValue = self.contextValue, case let .authorized(context) = contextValue { - let _ = context.context.callManager?.requestCall(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), endCurrentIfAny: false) + let _ = context.context.callManager?.requestCall(account: context.context.account, peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: userId), endCurrentIfAny: false) } } } @@ -1582,9 +1613,9 @@ private enum QueuedWakeup: Int32 { context.wakeupManager.wakeupForIncomingMessages(completion: { messageIds -> Signal in if let contextValue = self.contextValue, case let .authorized(context) = contextValue { if handleVoipNotifications { - return context.notificationManager.commitRemoteNotification(originalRequestId: requestId, messageIds: messageIds) + return context.notificationManager.commitRemoteNotification(context: context.context, originalRequestId: requestId, messageIds: messageIds) } else { - return context.notificationManager.commitRemoteNotification(originalRequestId: nil, messageIds: []) + return context.notificationManager.commitRemoteNotification(context: context.context, originalRequestId: nil, messageIds: []) } } else { Logger.shared.log("App \(self.episodeId)", "Couldn't process remote notifications wakeup result") diff --git a/Telegram-iOS/ApplicationContext.swift b/Telegram-iOS/ApplicationContext.swift index 9e67e1116c..c8f9397c1c 100644 --- a/Telegram-iOS/ApplicationContext.swift +++ b/Telegram-iOS/ApplicationContext.swift @@ -7,7 +7,7 @@ import TelegramCore import Display import LegacyComponents -func applicationContext(networkArguments: NetworkInitializationArguments, applicationBindings: TelegramApplicationBindings, replyFromNotificationsActive: Signal, backgroundAudioActive: Signal, watchManagerArguments: Signal, accountManager: AccountManager, rootPath: String, legacyBasePath: String, mainWindow: Window1, reinitializedNotificationSettings: @escaping () -> Void) -> Signal { +/*func applicationContext(networkArguments: NetworkInitializationArguments, applicationBindings: TelegramApplicationBindings, replyFromNotificationsActive: Signal, backgroundAudioActive: Signal, watchManagerArguments: Signal, sharedContext: SharedAccountContext, accountManager: AccountManager, rootPath: String, legacyBasePath: String, mainWindow: Window1, reinitializedNotificationSettings: @escaping () -> Void) -> Signal { return currentAccount(allocateIfNotExists: true, networkArguments: networkArguments, supplementary: false, manager: accountManager, rootPath: rootPath, auxiliaryMethods: telegramAccountAuxiliaryMethods) |> filter { $0 != nil } |> deliverOnMainQueue @@ -26,14 +26,14 @@ func applicationContext(networkArguments: NetworkInitializationArguments, applic return currentPresentationDataAndSettings(postbox: account.postbox) |> deliverOnMainQueue |> map { dataAndSettings -> ApplicationContext? in - return .authorized(AuthorizedApplicationContext(mainWindow: mainWindow, replyFromNotificationsActive: replyFromNotificationsActive, backgroundAudioActive: backgroundAudioActive, watchManagerArguments: watchManagerArguments, context: AccountContext(account: account, applicationBindings: applicationBindings, accountManager: accountManager, initialPresentationDataAndSettings: dataAndSettings, postbox: account.postbox), accountManager: accountManager, legacyBasePath: legacyBasePath, showCallsTab: dataAndSettings.callListSettings.showTab, reinitializedNotificationSettings: reinitializedNotificationSettings)) + return .authorized(AuthorizedApplicationContext(mainWindow: mainWindow, replyFromNotificationsActive: replyFromNotificationsActive, backgroundAudioActive: backgroundAudioActive, watchManagerArguments: watchManagerArguments, context: AccountContext(sharedContext: sharedContext, account: account, initialPresentationDataAndSettings: dataAndSettings), accountManager: accountManager, legacyBasePath: legacyBasePath, showCallsTab: dataAndSettings.callListSettings.showTab, reinitializedNotificationSettings: reinitializedNotificationSettings)) } } } else { return .single(nil) } } -} +}*/ func isAccessLocked(data: PostboxAccessChallengeData, at timestamp: Int32) -> Bool { if data.isLockable, let autolockDeadline = data.autolockDeadline, autolockDeadline <= timestamp { @@ -85,18 +85,20 @@ enum ApplicationContext { } final class UnauthorizedApplicationContext { + let sharedContext: SharedAccountContext let account: UnauthorizedAccount var strings: PresentationStrings let rootController: AuthorizationSequenceController - init(account: UnauthorizedAccount, applicationBindings: TelegramApplicationBindings) { + init(sharedContext: SharedAccountContext, account: UnauthorizedAccount, hasOtherAccounts: Bool) { + self.sharedContext = sharedContext self.account = account self.strings = defaultPresentationStrings - self.rootController = AuthorizationSequenceController(account: account, strings: self.strings, openUrl: applicationBindings.openUrl, apiId: BuildConfig.shared().apiId, apiHash: BuildConfig.shared().apiHash) + self.rootController = AuthorizationSequenceController(sharedContext: sharedContext, account: account, hasOtherAccounts: hasOtherAccounts, strings: self.strings, openUrl: sharedContext.applicationBindings.openUrl, apiId: BuildConfig.shared().apiId, apiHash: BuildConfig.shared().apiHash) - account.shouldBeServiceTaskMaster.set(applicationBindings.applicationInForeground |> map { value -> AccountServiceTaskMasterMode in + account.shouldBeServiceTaskMaster.set(sharedContext.applicationBindings.applicationInForeground |> map { value -> AccountServiceTaskMasterMode in if value { return .always } else { @@ -234,13 +236,12 @@ final class AuthorizedApplicationContext { } |> distinctUntilChanged - self.wakeupManager = WakeupManager(inForeground: context.applicationBindings.applicationInForeground, runningServiceTasks: context.account.importantTasksRunning, runningBackgroundLocationTasks: runningBackgroundLocationTasks, runningWatchTasks: runningWatchTasksPromise.get(), runningDownloadTasks: runningDownloadTasks) + self.wakeupManager = WakeupManager(inForeground: context.sharedContext.applicationBindings.applicationInForeground, runningServiceTasks: context.account.importantTasksRunning, runningBackgroundLocationTasks: runningBackgroundLocationTasks, runningWatchTasks: runningWatchTasksPromise.get(), runningDownloadTasks: runningDownloadTasks) self.wakeupManager.account = context.account self.showCallsTab = showCallsTab self.notificationManager = NotificationManager() - self.notificationManager.context = context self.notificationManager.isApplicationInForeground = false self.overlayMediaController = OverlayMediaController() @@ -254,11 +255,11 @@ final class AuthorizedApplicationContext { }, { openSettingsImpl?() }) - }, networkType: context.account.networkType, audioSession: context.mediaManager.audioSession, callSessionManager: context.account.callSessionManager) + }, networkType: context.account.networkType, audioSession: context.sharedContext.mediaManager.audioSession, activeAccounts: .single([context.account])) context.callManager = callManager context.hasOngoingCall = self.hasOngoingCall.get() - let shouldBeServiceTaskMaster = combineLatest(context.applicationBindings.applicationInForeground, self.wakeupManager.isWokenUp, replyFromNotificationsActive, backgroundAudioActive, callManager.hasActiveCalls) + let shouldBeServiceTaskMaster = combineLatest(context.sharedContext.applicationBindings.applicationInForeground, self.wakeupManager.isWokenUp, replyFromNotificationsActive, backgroundAudioActive, callManager.hasActiveCalls) |> map { foreground, wokenUp, replyFromNotificationsActive, backgroundAudioActive, hasActiveCalls -> AccountServiceTaskMasterMode in if foreground || wokenUp || replyFromNotificationsActive || hasActiveCalls { return .always @@ -288,7 +289,7 @@ final class AuthorizedApplicationContext { }) context.account.shouldExplicitelyKeepWorkerConnections.set(backgroundAudioActive) context.account.shouldKeepBackgroundDownloadConnections.set(context.fetchManager.hasUserInitiatedEntries) - context.account.shouldKeepOnlinePresence.set(context.applicationBindings.applicationInForeground) + context.account.shouldKeepOnlinePresence.set(context.sharedContext.applicationBindings.applicationInForeground) let cache = TGCache(cachesPath: legacyBasePath + "/Caches")! @@ -324,7 +325,7 @@ final class AuthorizedApplicationContext { context.keyShortcutsController = keyShortcutsController } - self.applicationInForegroundDisposable = context.applicationBindings.applicationInForeground.start(next: { [weak self] value in + self.applicationInForegroundDisposable = context.sharedContext.applicationBindings.applicationInForeground.start(next: { [weak self] value in Queue.mainQueue().async { self?.notificationManager.isApplicationInForeground = value } @@ -372,14 +373,14 @@ final class AuthorizedApplicationContext { self?.mainWindow.present(c, on: .root) } openSettingsImpl = { [weak context] in - context?.applicationBindings.openSettings() + context?.sharedContext.applicationBindings.openSettings() } let previousPasscodeState = Atomic(value: nil) let preferencesKey: PostboxViewKey = .preferences(keys: Set([ApplicationSpecificPreferencesKeys.presentationPasscodeSettings])) - self.passcodeStatusDisposable.set((combineLatest(queue: Queue.mainQueue(), context.account.postbox.combinedView(keys: [.accessChallengeData, preferencesKey]), context.applicationBindings.applicationIsActive) + self.passcodeStatusDisposable.set((combineLatest(queue: Queue.mainQueue(), context.account.postbox.combinedView(keys: [.accessChallengeData, preferencesKey]), context.sharedContext.applicationBindings.applicationIsActive) |> map { view, isActive -> (PostboxAccessChallengeData, PresentationPasscodeSettings?, Bool) in let accessChallengeData = (view.views[.accessChallengeData] as? AccessChallengeDataView)?.data ?? PostboxAccessChallengeData.none let passcodeSettings = (view.views[preferencesKey] as! PreferencesView).values[ApplicationSpecificPreferencesKeys.presentationPasscodeSettings] as? PresentationPasscodeSettings @@ -669,11 +670,20 @@ final class AuthorizedApplicationContext { } if !strongSelf.isLocked { - if inAppNotificationSettings.playSounds { - serviceSoundManager.playIncomingMessageSound() - } - if inAppNotificationSettings.vibrate { - serviceSoundManager.playVibrationSound() + let isMuted = firstMessage.attributes.contains(where: { attribute in + if let attribute = attribute as? NotificationInfoMessageAttribute { + return attribute.flags.contains(.muted) + } else { + return false + } + }) + if !isMuted { + if inAppNotificationSettings.playSounds { + serviceSoundManager.playIncomingMessageSound() + } + if inAppNotificationSettings.vibrate { + serviceSoundManager.playVibrationSound() + } } } @@ -913,7 +923,8 @@ final class AuthorizedApplicationContext { })) } - self.displayAlertsDisposable = (context.account.stateManager.displayAlerts |> deliverOnMainQueue).start(next: { [weak self] alerts in + self.displayAlertsDisposable = (context.account.stateManager.displayAlerts + |> deliverOnMainQueue).start(next: { [weak self] alerts in if let strongSelf = self{ for text in alerts { let presentationData = strongSelf.context.currentPresentationData.with { $0 } @@ -926,7 +937,7 @@ final class AuthorizedApplicationContext { self.removeNotificationsDisposable = (context.account.stateManager.appliedIncomingReadMessages |> deliverOnMainQueue).start(next: { [weak self] ids in if let strongSelf = self { - strongSelf.context.applicationBindings.clearMessageNotifications(ids) + strongSelf.context.sharedContext.applicationBindings.clearMessageNotifications(ids) } }) @@ -946,11 +957,11 @@ final class AuthorizedApplicationContext { strongSelf.callState.set(call.state |> map(Optional.init)) strongSelf.hasOngoingCall.set(true) - strongSelf.notificationManager.notificationCall = call + strongSelf.notificationManager.setNotificationCall(call, strings: strongSelf.context.currentPresentationData.with({ $0 }).strings) } else { strongSelf.callState.set(.single(nil)) strongSelf.hasOngoingCall.set(false) - strongSelf.notificationManager.notificationCall = nil + strongSelf.notificationManager.setNotificationCall(nil, strings: strongSelf.context.currentPresentationData.with({ $0 }).strings) } } } @@ -1004,7 +1015,7 @@ final class AuthorizedApplicationContext { self.context.account.resetStateManagement() let contactSynchronizationPreferencesKey = PostboxViewKey.preferences(keys: Set([ApplicationSpecificPreferencesKeys.contactSynchronizationSettings])) - let importableContacts = self.context.contactDataManager.importable() + let importableContacts = self.context.sharedContext.contactDataManager?.importable() ?? .single([:]) self.context.account.importableContacts.set(self.context.account.postbox.combinedView(keys: [contactSynchronizationPreferencesKey]) |> mapToSignal { preferences -> Signal<[DeviceContactNormalizedPhoneNumber: ImportableDeviceContactData], NoError> in let settings: ContactSynchronizationSettings = ((preferences.views[contactSynchronizationPreferencesKey] as? PreferencesView)?.values[ApplicationSpecificPreferencesKeys.contactSynchronizationSettings] as? ContactSynchronizationSettings) ?? .defaultSettings @@ -1054,7 +1065,7 @@ final class AuthorizedApplicationContext { strongSelf.context.watchManager = watchManager runningWatchTasksPromise.set(watchManager.runningTasks) - strongSelf.watchNavigateToMessageDisposable.set((strongSelf.context.applicationBindings.applicationInForeground |> mapToSignal({ applicationInForeground -> Signal<(Bool, MessageId), NoError> in + strongSelf.watchNavigateToMessageDisposable.set((strongSelf.context.sharedContext.applicationBindings.applicationInForeground |> mapToSignal({ applicationInForeground -> Signal<(Bool, MessageId), NoError> in return watchManager.navigateToMessageRequested |> map { messageId in return (applicationInForeground, messageId) @@ -1080,7 +1091,7 @@ final class AuthorizedApplicationContext { (strongSelf.rootController.viewControllers.last as? ViewController)?.present(controller, in: .window(.root)) } } else { - strongSelf.notificationManager.presentWatchContinuityNotification(messageId: messageId) + strongSelf.notificationManager.presentWatchContinuityNotification(context: strongSelf.context, messageId: messageId) } } })) diff --git a/Telegram-iOS/NotificationManager.swift b/Telegram-iOS/NotificationManager.swift index d0e9797d54..1d0bce13f0 100644 --- a/Telegram-iOS/NotificationManager.swift +++ b/Telegram-iOS/NotificationManager.swift @@ -44,32 +44,6 @@ enum NotificationManagedNotificationRequestId: Hashable { } return nil } - - var hashValue: Int { - switch self { - case let .messageId(messageId): - return messageId.id.hashValue - case let .globallyUniqueId(id, _): - return id.hashValue - } - } - - static func ==(lhs: NotificationManagedNotificationRequestId, rhs: NotificationManagedNotificationRequestId) -> Bool { - switch lhs { - case let .messageId(id): - if case .messageId(id) = rhs { - return true - } else { - return false - } - case let .globallyUniqueId(id, peerId): - if case .globallyUniqueId(id, peerId) = rhs { - return true - } else { - return false - } - } - } } private func processedSoundName(_ name: String) -> String { @@ -83,7 +57,7 @@ private func processedSoundName(_ name: String) -> String { final class NotificationManager { private var processedMessages = Set() - var context: AccountContext? { + /*var context: AccountContext? { didSet { assert(Queue.mainQueue().isCurrent()) @@ -103,17 +77,19 @@ final class NotificationManager { self.notificationMessagesDisposable.set(nil) } } - } + }*/ private let notificationCallStateDisposable = MetaDisposable() - var notificationCall: PresentationCall? { - didSet { - if self.notificationCall?.internalId != oldValue?.internalId { - if let notificationCall = self.notificationCall { - let peer = notificationCall.peer - let internalId = notificationCall.internalId - let isIntegratedWithCallKit = notificationCall.isIntegratedWithCallKit - self.notificationCallStateDisposable.set((notificationCall.state + 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 @@ -125,12 +101,11 @@ final class NotificationManager { } } |> distinctUntilChanged(isEqual: { $0?.1 == $1?.1 })).start(next: { [weak self] peerAndInternalId in - self?.updateNotificationCall(call: peerAndInternalId) + self?.updateNotificationCall(call: peerAndInternalId, strings: strings) })) - } else { - self.notificationCallStateDisposable.set(nil) - self.updateNotificationCall(call: nil) - } + } else { + self.notificationCallStateDisposable.set(nil) + self.updateNotificationCall(call: nil, strings: strings) } } } @@ -264,98 +239,90 @@ final class NotificationManager { } } - func commitRemoteNotification(originalRequestId: NotificationManagedNotificationRequestId?, messageIds: [MessageId]) -> Signal { - if let context = self.context { - return context.account.postbox.transaction { transaction -> ([(MessageId, [Message], Bool, PeerMessageSound, Bool)], Bool) in - var isLocked = false - if isAccessLocked(data: transaction.getAccessChallengeData(), at: Int32(CFAbsoluteTimeGetCurrent())) { - isLocked = true - } - - var results: [(MessageId, [Message], Bool, PeerMessageSound, Bool)] = [] - var updatedMessageIds = messageIds - if let originalRequestId = originalRequestId { - switch originalRequestId { - case let .messageId(id): - if !updatedMessageIds.contains(id) { - updatedMessageIds.append(id) - } - case .globallyUniqueId: - break - } - } - for id in updatedMessageIds { - let (messages, notify, sound, displayContents) = messagesForNotification(transaction: transaction, id: id, alwaysReturnMessage: true) - - if results.contains(where: { result in - return result.1.contains(where: { message in - return messages.contains(where: { - message.id == $0.id - }) - }) - }) { - continue - } - results.append((id, messages, notify, sound, displayContents)) - } - return (results, isLocked) + func commitRemoteNotification(context: AccountContext, originalRequestId: NotificationManagedNotificationRequestId?, messageIds: [MessageId]) -> Signal { + return context.account.postbox.transaction { transaction -> ([(MessageId, [Message], Bool, PeerMessageSound, Bool)], Bool) in + var isLocked = false + if isAccessLocked(data: transaction.getAccessChallengeData(), at: Int32(CFAbsoluteTimeGetCurrent())) { + isLocked = true } - |> deliverOnMainQueue - |> beforeNext { - [weak self] results, isLocked in - if let strongSelf = self { - let delayUntilTimestamp: Int32 = strongSelf.context?.account.stateManager.getDelayNotificatonsUntil() ?? 0 - - for (id, messages, notify, sound, displayContents) in results { - let requestId: NotificationManagedNotificationRequestId = .messageId(id) - if let message = messages.first, message.id.peerId.namespace != Namespaces.Peer.SecretChat, !strongSelf.processedRequestIds.contains(requestId) { - let notificationRequestTimeout = strongSelf.notificationRequests[requestId] - if notificationRequestTimeout == nil || CFAbsoluteTimeGetCurrent() < notificationRequestTimeout! { - if #available(iOS 10.0, *) { - let center = UNUserNotificationCenter.current() - center.removePendingNotificationRequests(withIdentifiers: [notificationKey(requestId)]) - } else { - let key = notificationKey(requestId) - if let notifications = UIApplication.shared.scheduledLocalNotifications { - for notification in notifications { - if let userInfo = notification.userInfo, let id = userInfo["id"] as? String { - if id == key { - UIApplication.shared.cancelLocalNotification(notification) - break - } + + var results: [(MessageId, [Message], Bool, PeerMessageSound, Bool)] = [] + var updatedMessageIds = messageIds + if let originalRequestId = originalRequestId { + switch originalRequestId { + case let .messageId(id): + if !updatedMessageIds.contains(id) { + updatedMessageIds.append(id) + } + case .globallyUniqueId: + break + } + } + for id in updatedMessageIds { + let (messages, notify, sound, displayContents) = messagesForNotification(transaction: transaction, id: id, alwaysReturnMessage: true) + + if results.contains(where: { result in + return result.1.contains(where: { message in + return messages.contains(where: { + message.id == $0.id + }) + }) + }) { + continue + } + results.append((id, messages, notify, sound, displayContents)) + } + return (results, isLocked) + } + |> deliverOnMainQueue + |> beforeNext { + [weak self] results, isLocked in + if let strongSelf = self { + let delayUntilTimestamp: Int32 = context.account.stateManager.getDelayNotificatonsUntil() ?? 0 + + for (id, messages, notify, sound, displayContents) in results { + let requestId: NotificationManagedNotificationRequestId = .messageId(id) + if let message = messages.first, message.id.peerId.namespace != Namespaces.Peer.SecretChat, !strongSelf.processedRequestIds.contains(requestId) { + let notificationRequestTimeout = strongSelf.notificationRequests[requestId] + if notificationRequestTimeout == nil || CFAbsoluteTimeGetCurrent() < notificationRequestTimeout! { + if #available(iOS 10.0, *) { + let center = UNUserNotificationCenter.current() + center.removePendingNotificationRequests(withIdentifiers: [notificationKey(requestId)]) + } else { + let key = notificationKey(requestId) + if let notifications = UIApplication.shared.scheduledLocalNotifications { + for notification in notifications { + if let userInfo = notification.userInfo, let id = userInfo["id"] as? String { + if id == key { + UIApplication.shared.cancelLocalNotification(notification) + break } } } } + } + + if !strongSelf.processedRequestIds.contains(requestId) { + strongSelf.processedRequestIds.insert(requestId) - if !strongSelf.processedRequestIds.contains(requestId) { - strongSelf.processedRequestIds.insert(requestId) - - if notify { - var delayMessage = false - if message.timestamp <= delayUntilTimestamp && message.id.peerId.namespace != Namespaces.Peer.SecretChat { - delayMessage = true - } - strongSelf.processNotificationMessages([(messages, sound, displayContents, delayMessage)], isLocked: isLocked) + if notify { + var delayMessage = false + if message.timestamp <= delayUntilTimestamp && message.id.peerId.namespace != Namespaces.Peer.SecretChat { + delayMessage = true } + strongSelf.processNotificationMessages(context: context, messageList: [(messages, sound, displayContents, delayMessage)], isLocked: isLocked) } } } } } - } |> map { _ in - return Void() } - } else { - return .complete() + } |> map { _ in + return Void() } } - private func processNotificationMessages(_ messageList: [([Message], PeerMessageSound, Bool, Bool)], isLocked: Bool) { - guard let context = self.context else { - Logger.shared.log("NotificationManager", "context missing") - return - } + private func processNotificationMessages(context: AccountContext, messageList: [([Message], PeerMessageSound, Bool, Bool)], isLocked: Bool) { let presentationData = (context.currentPresentationData.with { $0 }) let strings = presentationData.strings let nameDisplayOrder = presentationData.nameDisplayOrder @@ -554,7 +521,7 @@ final class NotificationManager { loop: for media in firstMessage.media { if let image = media as? TelegramMediaImage { mediaRepresentations = image.representations - if !firstMessage.containsSecretMedia, let context = self.context, let smallest = smallestImageRepresentation(image.representations), let largest = largestImageRepresentation(image.representations) { + if !firstMessage.containsSecretMedia, let smallest = smallestImageRepresentation(image.representations), let largest = largestImageRepresentation(image.representations) { var imageInfo: [String: Any] = [:] var thumbnailInfo: [String: Any] = [:] @@ -675,7 +642,7 @@ final class NotificationManager { content.threadIdentifier = "peer_\(firstMessage.id.peerId.toInt64())" if mediaInfo != nil, let mediaRepresentations = mediaRepresentations { - if let context = self.context, let smallest = smallestImageRepresentation(mediaRepresentations) { + if let smallest = smallestImageRepresentation(mediaRepresentations) { /*if let path = account.postbox.mediaBox.completedResourcePath(smallest.resource) { var randomId: Int64 = 0 arc4random_buf(&randomId, 8) @@ -752,7 +719,7 @@ final class NotificationManager { private var currentNotificationCall: (peer: Peer?, internalId: CallSessionInternalId)? - private func updateNotificationCall(call: (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() @@ -769,14 +736,24 @@ final class NotificationManager { } self.currentNotificationCall = call - guard let context = self.context else { - return - } - let presentationData = context.currentPresentationData.with { $0 } 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() - content.body = presentationData.strings.PUSH_PHONE_CALL_REQUEST(notificationCall.peer?.displayTitle ?? "").0 + if let title = title { + content.title = title + } + content.body = body content.sound = UNNotificationSound(named: "0.m4a") content.categoryIdentifier = "incomingCall" content.userInfo = [:] @@ -793,7 +770,16 @@ final class NotificationManager { } else { let notification = UILocalNotification() - notification.alertBody = presentationData.strings.PUSH_PHONE_CALL_REQUEST(notificationCall.peer?.displayTitle ?? "").0 + 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" @@ -802,7 +788,7 @@ final class NotificationManager { } } - func presentWatchContinuityNotification(messageId: MessageId) { + func presentWatchContinuityNotification(context: AccountContext, messageId: MessageId) { if #available(iOS 10.0, *) { let center = UNUserNotificationCenter.current() center.removeDeliveredNotifications(withIdentifiers: ["watch"]) @@ -815,9 +801,6 @@ final class NotificationManager { } } } - guard let context = self.context else { - return - } let presentationData = context.currentPresentationData.with { $0 } var userInfo: [AnyHashable : Any] = [:] diff --git a/Telegram-iOS/SnapshotEnvironment.swift b/Telegram-iOS/SnapshotEnvironment.swift index 74d70625fa..d4f604148a 100644 --- a/Telegram-iOS/SnapshotEnvironment.swift +++ b/Telegram-iOS/SnapshotEnvironment.swift @@ -13,7 +13,8 @@ enum SnapshotEnvironmentTheme { } func snapshotEnvironment(application: UIApplication, mainWindow: UIWindow, statusBarHost: StatusBarHost, theme: SnapshotEnvironmentTheme) -> (AccountContext, AccountManager) { - var randomId: Int64 = 0 + preconditionFailure() + /*var randomId: Int64 = 0 arc4random_buf(&randomId, 8) let path = NSTemporaryDirectory() + "\(randomId)" @@ -106,9 +107,9 @@ func snapshotEnvironment(application: UIApplication, mainWindow: UIWindow, statu semaphore1.wait() precondition(dataAndSettings != nil) - let context = AccountContext(account: result!, applicationBindings: applicationBindings, accountManager: accountManagerValue!, initialPresentationDataAndSettings: dataAndSettings!, postbox: result!.postbox) + let context = AccountContext(sharedContext: SharedAccountContext(applicationBindings: applicationBindings, accountManager: accountManagerValue!), account: result!, initialPresentationDataAndSettings: dataAndSettings!) - return (context, accountManagerValue!) + return (context, accountManagerValue!)*/ } #endif diff --git a/submodules/MtProtoKit b/submodules/MtProtoKit index bead1574a0..89531617a0 160000 --- a/submodules/MtProtoKit +++ b/submodules/MtProtoKit @@ -1 +1 @@ -Subproject commit bead1574a09c72ce8411938124c717180926789a +Subproject commit 89531617a0b77014aa69dd8043b67858218e6a1c diff --git a/submodules/Postbox b/submodules/Postbox index acbebf2342..f811d2a647 160000 --- a/submodules/Postbox +++ b/submodules/Postbox @@ -1 +1 @@ -Subproject commit acbebf2342b6c374d3123ff31e82db1a7f5c9302 +Subproject commit f811d2a6475398acafae51f8b562b65d486b8bfd diff --git a/submodules/SSignalKit b/submodules/SSignalKit index 1a4d2ae680..e05c15c590 160000 --- a/submodules/SSignalKit +++ b/submodules/SSignalKit @@ -1 +1 @@ -Subproject commit 1a4d2ae68067d781baaaa235ec6ac8fcd91ae13b +Subproject commit e05c15c5903fb301aefd2b4952b5361c9dabc2f3 diff --git a/submodules/TelegramCore b/submodules/TelegramCore index 071c1c1f90..668207ab55 160000 --- a/submodules/TelegramCore +++ b/submodules/TelegramCore @@ -1 +1 @@ -Subproject commit 071c1c1f903d30c96a89a4708e5fc519d6310a6f +Subproject commit 668207ab55d2e58843b0614260f7738c3d6116c4 diff --git a/submodules/TelegramUI b/submodules/TelegramUI index 062d0a48d6..c1cc1f23b0 160000 --- a/submodules/TelegramUI +++ b/submodules/TelegramUI @@ -1 +1 @@ -Subproject commit 062d0a48d60f6c8fb732227410bf6fe1d12ca456 +Subproject commit c1cc1f23b092a087d8af3b3a842dba1557568913 diff --git a/submodules/ffmpeg b/submodules/ffmpeg index a9d906d655..cc700e15e8 160000 --- a/submodules/ffmpeg +++ b/submodules/ffmpeg @@ -1 +1 @@ -Subproject commit a9d906d655fd337ab4d8f5e067e92a14c8837b77 +Subproject commit cc700e15e858cfa73ed67b9eaa09735681cc5491