Improved account switching

This commit is contained in:
Peter Iakovlev 2019-01-25 17:56:05 +04:00
parent 4a6a24226e
commit 3e851a0fe8
11 changed files with 317 additions and 286 deletions

View File

@ -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)
}
}

View File

@ -134,7 +134,7 @@ private enum QueuedWakeup: Int32 {
private var isActiveValue = false
let hasActiveAudioSession = Promise<Bool>(false)
private let accountManagerPromise = Promise<AccountManager>()
private let sharedContextPromise = Promise<SharedAccountContext>()
private let watchCommunicationManagerPromise = Promise<WatchCommunicationManager?>()
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<AccountManager, NoError> 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<ImportedLegacyAccountEvent, NoError> 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<AccountManager, NoError> 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<WatchManagerArguments?>()
self.context.set(self.accountManagerPromise.get()
self.sharedContextPromise.set(accountManager(basePath: rootPath + "/accounts-metadata")
|> deliverOnMainQueue
|> mapToSignal { accountManager -> Signal<ApplicationContext?, NoError> 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<SharedAccountContext, NoError> 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<ImportedLegacyAccountEvent, NoError> 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<SharedAccountContext, NoError> 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<WatchManagerArguments?>()
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<ApplicationContext?, NoError> 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<Void, NoError> 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")

View File

@ -7,7 +7,7 @@ import TelegramCore
import Display
import LegacyComponents
func applicationContext(networkArguments: NetworkInitializationArguments, applicationBindings: TelegramApplicationBindings, replyFromNotificationsActive: Signal<Bool, NoError>, backgroundAudioActive: Signal<Bool, NoError>, watchManagerArguments: Signal<WatchManagerArguments?, NoError>, accountManager: AccountManager, rootPath: String, legacyBasePath: String, mainWindow: Window1, reinitializedNotificationSettings: @escaping () -> Void) -> Signal<ApplicationContext?, NoError> {
/*func applicationContext(networkArguments: NetworkInitializationArguments, applicationBindings: TelegramApplicationBindings, replyFromNotificationsActive: Signal<Bool, NoError>, backgroundAudioActive: Signal<Bool, NoError>, watchManagerArguments: Signal<WatchManagerArguments?, NoError>, sharedContext: SharedAccountContext, accountManager: AccountManager, rootPath: String, legacyBasePath: String, mainWindow: Window1, reinitializedNotificationSettings: @escaping () -> Void) -> Signal<ApplicationContext?, NoError> {
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<PasscodeState?>(value: nil)
let preferencesKey: PostboxViewKey = .preferences(keys: Set<ValueBoxKey>([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)
}
}
}))

View File

@ -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<MessageId>()
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<Void, NoError> {
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<Void, NoError> {
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] = [:]

View File

@ -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

@ -1 +1 @@
Subproject commit bead1574a09c72ce8411938124c717180926789a
Subproject commit 89531617a0b77014aa69dd8043b67858218e6a1c

@ -1 +1 @@
Subproject commit acbebf2342b6c374d3123ff31e82db1a7f5c9302
Subproject commit f811d2a6475398acafae51f8b562b65d486b8bfd

@ -1 +1 @@
Subproject commit 1a4d2ae68067d781baaaa235ec6ac8fcd91ae13b
Subproject commit e05c15c5903fb301aefd2b4952b5361c9dabc2f3

@ -1 +1 @@
Subproject commit 071c1c1f903d30c96a89a4708e5fc519d6310a6f
Subproject commit 668207ab55d2e58843b0614260f7738c3d6116c4

@ -1 +1 @@
Subproject commit 062d0a48d60f6c8fb732227410bf6fe1d12ca456
Subproject commit c1cc1f23b092a087d8af3b3a842dba1557568913

@ -1 +1 @@
Subproject commit a9d906d655fd337ab4d8f5e067e92a14c8837b77
Subproject commit cc700e15e858cfa73ed67b9eaa09735681cc5491