From f75a73d257286617f7c69bf7e099da804602eb91 Mon Sep 17 00:00:00 2001 From: Peter <> Date: Fri, 25 Oct 2019 23:06:31 +0400 Subject: [PATCH] Improved locking --- BUCK | 1 + SiriIntents/IntentHandler.swift | 2 +- Widget/TodayViewController.swift | 29 +- .../Sources/AccountContext.swift | 9 + submodules/AppLock/BUCK | 25 ++ submodules/AppLock/Sources/AppLock.swift | 247 ++++++++++++++++++ .../Sources/LockedWindowCoveringView.swift | 35 +++ submodules/AppLockState/BUCK | 14 + .../AppLockState/Sources/AppLockState.swift | 62 +++++ .../Sources/ChatListController.swift | 14 +- .../Sources/LegacyPreferencesImport.swift | 12 +- submodules/MonotonicTime/BUCK | 19 ++ .../MonotonicTime/Sources/DeviceUptime.h | 3 + .../MonotonicTime/Sources/DeviceUptime.m | 22 ++ .../Sources/PasscodeEntryController.swift | 98 ++----- .../Sources/PasscodeEntryControllerNode.swift | 8 +- .../Sources/PasscodeSetupControllerNode.swift | 2 +- .../ChannelMembersSearchContainerNode.swift | 3 + .../Postbox/AccountManagerMetadataTable.swift | 65 +---- .../PasscodeOptionsController.swift | 18 +- .../TelegramCore/TelegramCore/Account.swift | 8 +- .../TelegramCore/TelegramCore/Network.swift | 4 +- .../TelegramUI/TelegramUI/AppDelegate.swift | 12 +- .../TelegramUI/ApplicationContext.swift | 34 ++- .../NotificationContentContext.swift | 11 +- .../TelegramUI/ShareExtensionContext.swift | 10 +- .../TelegramUI/SharedAccountContext.swift | 4 +- 27 files changed, 570 insertions(+), 201 deletions(-) create mode 100644 submodules/AppLock/BUCK create mode 100644 submodules/AppLock/Sources/AppLock.swift create mode 100644 submodules/AppLock/Sources/LockedWindowCoveringView.swift create mode 100644 submodules/AppLockState/BUCK create mode 100644 submodules/AppLockState/Sources/AppLockState.swift create mode 100644 submodules/MonotonicTime/BUCK create mode 100644 submodules/MonotonicTime/Sources/DeviceUptime.h create mode 100644 submodules/MonotonicTime/Sources/DeviceUptime.m diff --git a/BUCK b/BUCK index c9422e5f75..ebe3c2021d 100644 --- a/BUCK +++ b/BUCK @@ -247,6 +247,7 @@ apple_binary( deps = [ "//submodules/BuildConfig:BuildConfig", "//submodules/WidgetItems:WidgetItems", + "//submodules/AppLockState:AppLockState", ], frameworks = [ "$SDKROOT/System/Library/Frameworks/UIKit.framework", diff --git a/SiriIntents/IntentHandler.swift b/SiriIntents/IntentHandler.swift index 071db1ed78..be3027dd34 100644 --- a/SiriIntents/IntentHandler.swift +++ b/SiriIntents/IntentHandler.swift @@ -99,7 +99,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId) let encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!) - account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), encryptionProvider: OpenSSLEncryptionProvider()), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters) + account = currentAccount(allocateIfNotExists: false, networkArguments: NetworkInitializationArguments(apiId: apiId, languagesCategory: languagesCategory, appVersion: appVersion, voipMaxLayer: 0, appData: .single(buildConfig.bundleData(withAppToken: nil, signatureDict: nil)), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider()), supplementary: true, manager: accountManager, rootPath: rootPath, auxiliaryMethods: accountAuxiliaryMethods, encryptionParameters: encryptionParameters) |> mapToSignal { account -> Signal in if let account = account { switch account { diff --git a/Widget/TodayViewController.swift b/Widget/TodayViewController.swift index 28de6e77f2..15e6217188 100644 --- a/Widget/TodayViewController.swift +++ b/Widget/TodayViewController.swift @@ -2,6 +2,7 @@ import UIKit import NotificationCenter import BuildConfig import WidgetItems +import AppLockState private func rootPathForBasePath(_ appGroupPath: String) -> String { return appGroupPath + "/telegram-data" @@ -13,14 +14,11 @@ class TodayViewController: UIViewController, NCWidgetProviding { private var buildConfig: BuildConfig? + private var appLockedLabel: UILabel? + override func viewDidLoad() { super.viewDidLoad() - if self.initializedInterface { - return - } - self.initializedInterface = true - let appBundleIdentifier = Bundle.main.bundleIdentifier! guard let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else { return @@ -38,6 +36,23 @@ class TodayViewController: UIViewController, NCWidgetProviding { } let rootPath = rootPathForBasePath(appGroupUrl.path) + + if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: rootPath))), let state = try? JSONDecoder().decode(LockState.self, from: data), isAppLocked(state: state) { + let appLockedLabel = UILabel() + appLockedLabel.textColor = .black + appLockedLabel.font = UIFont.systemFont(ofSize: 16.0) + appLockedLabel.text = "Unlock the app to use widget" + appLockedLabel.sizeToFit() + self.appLockedLabel = appLockedLabel + self.view.addSubview(appLockedLabel) + return + } + + if self.initializedInterface { + return + } + self.initializedInterface = true + let dataPath = rootPath + "/widget-data" if let data = try? Data(contentsOf: URL(fileURLWithPath: dataPath)), let widgetData = try? JSONDecoder().decode(WidgetData.self, from: data) { @@ -97,6 +112,10 @@ class TodayViewController: UIViewController, NCWidgetProviding { private func updateLayout(size: CGSize) { self.validLayout = size + if let appLockedLabel = self.appLockedLabel { + appLockedLabel.frame = CGRect(origin: CGPoint(x: floor((size.width - appLockedLabel.bounds.width) / 2.0), y: floor((size.height - appLockedLabel.bounds.height) / 2.0)), size: appLockedLabel.bounds.size) + } + let peerSize = CGSize(width: 70.0, height: 100.0) var peerFrames: [CGRect] = [] diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 3b9522695a..4d16677106 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -384,10 +384,19 @@ public enum CreateGroupMode { case locatedGroup(latitude: Double, longitude: Double, address: String?) } +public protocol AppLockContext: class { + var invalidAttempts: Signal { get } + + func lock() + func unlock() + func failedUnlockAttempt() +} + public protocol SharedAccountContext: class { var basePath: String { get } var mainWindow: Window1? { get } var accountManager: AccountManager { get } + var appLockContext: AppLockContext { get } var currentPresentationData: Atomic { get } var presentationData: Signal { get } diff --git a/submodules/AppLock/BUCK b/submodules/AppLock/BUCK new file mode 100644 index 0000000000..65a96d1c58 --- /dev/null +++ b/submodules/AppLock/BUCK @@ -0,0 +1,25 @@ +load("//Config:buck_rule_macros.bzl", "static_library") + +static_library( + name = "AppLock", + srcs = glob([ + "Sources/**/*.swift", + ]), + deps = [ + "//submodules/AsyncDisplayKit:AsyncDisplayKit#shared", + "//submodules/Display:Display#shared", + "//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit#shared", + "//submodules/Postbox:Postbox#shared", + "//submodules/TelegramPresentationData:TelegramPresentationData", + "//submodules/MonotonicTime:MonotonicTime", + "//submodules/PasscodeUI:PasscodeUI", + "//submodules/TelegramUIPreferences:TelegramUIPreferences", + "//submodules/ImageBlur:ImageBlur", + "//submodules/AccountContext:AccountContext", + "//submodules/AppLockState:AppLockState", + ], + frameworks = [ + "$SDKROOT/System/Library/Frameworks/Foundation.framework", + "$SDKROOT/System/Library/Frameworks/UIKit.framework", + ], +) diff --git a/submodules/AppLock/Sources/AppLock.swift b/submodules/AppLock/Sources/AppLock.swift new file mode 100644 index 0000000000..8d8643756d --- /dev/null +++ b/submodules/AppLock/Sources/AppLock.swift @@ -0,0 +1,247 @@ +import Foundation +import UIKit +import Postbox +import Display +import SwiftSignalKit +import MonotonicTime +import AccountContext +import TelegramPresentationData +import PasscodeUI +import TelegramUIPreferences +import ImageBlur +import AppLockState + +private func isLocked(passcodeSettings: PresentationPasscodeSettings, state: LockState, isApplicationActive: Bool) -> Bool { + if state.isManuallyLocked { + return true + } else if let autolockTimeout = passcodeSettings.autolockTimeout { + var bootTimestamp: Int32 = 0 + let uptime = getDeviceUptimeSeconds(&bootTimestamp) + let timestamp = MonotonicTimestamp(bootTimestap: bootTimestamp, uptime: uptime) + + let applicationActivityTimestamp = state.applicationActivityTimestamp + + if let applicationActivityTimestamp = applicationActivityTimestamp { + if timestamp.bootTimestap != applicationActivityTimestamp.bootTimestap { + return true + } + if timestamp.uptime >= applicationActivityTimestamp.uptime + autolockTimeout { + return true + } + } else { + return true + } + } + return false +} + +private func getCoveringViewSnaphot(window: Window1) -> UIImage? { + let scale: CGFloat = 0.5 + let unscaledSize = window.hostView.containerView.frame.size + return generateImage(CGSize(width: floor(unscaledSize.width * scale), height: floor(unscaledSize.height * scale)), rotatedContext: { size, context in + context.clear(CGRect(origin: CGPoint(), size: size)) + context.scaleBy(x: scale, y: scale) + UIGraphicsPushContext(context) + window.hostView.containerView.drawHierarchy(in: CGRect(origin: CGPoint(), size: unscaledSize), afterScreenUpdates: false) + UIGraphicsPopContext() + }).flatMap(applyScreenshotEffectToImage) +} + +public final class AppLockContextImpl: AppLockContext { + private let rootPath: String + private let syncQueue = Queue() + + private let applicationBindings: TelegramApplicationBindings + private let accountManager: AccountManager + private let presentationDataSignal: Signal + private let window: Window1? + + private var coveringView: LockedWindowCoveringView? + private var passcodeController: PasscodeEntryController? + + private var timestampRenewTimer: SwiftSignalKit.Timer? + + private var currentStateValue: LockState + private let currentState = Promise() + + public init(rootPath: String, window: Window1?, applicationBindings: TelegramApplicationBindings, accountManager: AccountManager, presentationDataSignal: Signal, lockIconInitialFrame: @escaping () -> CGRect?) { + assert(Queue.mainQueue().isCurrent()) + + self.applicationBindings = applicationBindings + self.accountManager = accountManager + self.presentationDataSignal = presentationDataSignal + self.rootPath = rootPath + self.window = window + + if let data = try? Data(contentsOf: URL(fileURLWithPath: appLockStatePath(rootPath: self.rootPath))), let current = try? JSONDecoder().decode(LockState.self, from: data) { + self.currentStateValue = current + } else { + self.currentStateValue = LockState() + } + + let _ = (combineLatest(queue: .mainQueue(), + accountManager.accessChallengeData(), + accountManager.sharedData(keys: Set([ApplicationSpecificSharedDataKeys.presentationPasscodeSettings])), + presentationDataSignal, + applicationBindings.applicationIsActive, + self.currentState.get() + ) + |> deliverOnMainQueue).start(next: { [weak self] accessChallengeData, sharedData, presentationData, appInForeground, state in + guard let strongSelf = self else { + return + } + + let passcodeSettings: PresentationPasscodeSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationPasscodeSettings] as? PresentationPasscodeSettings ?? .defaultSettings + + var shouldDisplayCoveringView = false + + if !accessChallengeData.data.isLockable { + if let passcodeController = strongSelf.passcodeController { + strongSelf.passcodeController = nil + passcodeController.dismiss() + } + } else { + if let autolockTimeout = passcodeSettings.autolockTimeout, !appInForeground { + shouldDisplayCoveringView = true + } + + if isLocked(passcodeSettings: passcodeSettings, state: state, isApplicationActive: appInForeground) { + if strongSelf.passcodeController == nil { + let biometrics: PasscodeEntryControllerBiometricsMode + if passcodeSettings.enableBiometrics { + biometrics = .enabled(passcodeSettings.biometricsDomainState) + } else { + biometrics = .none + } + + let passcodeController = PasscodeEntryController(applicationBindings: strongSelf.applicationBindings, accountManager: strongSelf.accountManager, appLockContext: strongSelf, presentationData: presentationData, presentationDataSignal: strongSelf.presentationDataSignal, challengeData: accessChallengeData.data, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: true, lockIconInitialFrame: { [weak self] in + if let lockViewFrame = lockIconInitialFrame() { + return lockViewFrame + } else { + return CGRect() + } + })) + passcodeController.presentedOverCoveringView = true + strongSelf.passcodeController = passcodeController + strongSelf.window?.present(passcodeController, on: .passcode) + } + } else if let passcodeController = strongSelf.passcodeController { + strongSelf.passcodeController = nil + passcodeController.dismiss() + } + } + + strongSelf.updateTimestampRenewTimer(shouldRun: appInForeground && accessChallengeData.data.isLockable) + + if shouldDisplayCoveringView { + if strongSelf.coveringView == nil, let window = strongSelf.window { + let coveringView = LockedWindowCoveringView(theme: presentationData.theme) + coveringView.updateSnapshot(getCoveringViewSnaphot(window: window)) + strongSelf.coveringView = coveringView + window.coveringView = coveringView + } + } else { + if let coveringView = strongSelf.coveringView { + strongSelf.coveringView = nil + strongSelf.window?.coveringView = nil + } + } + }) + + self.currentState.set(.single(self.currentStateValue)) + } + + private func updateTimestampRenewTimer(shouldRun: Bool) { + if shouldRun { + if self.timestampRenewTimer == nil { + let timestampRenewTimer = SwiftSignalKit.Timer(timeout: 5.0, repeat: true, completion: { [weak self] in + guard let strongSelf = self else { + return + } + strongSelf.updateApplicationActivityTimestamp() + }, queue: .mainQueue()) + self.timestampRenewTimer = timestampRenewTimer + timestampRenewTimer.start() + } + } else { + if let timestampRenewTimer = self.timestampRenewTimer { + self.timestampRenewTimer = nil + timestampRenewTimer.invalidate() + } + } + } + + private func updateApplicationActivityTimestamp() { + self.updateLockState { state in + var bootTimestamp: Int32 = 0 + let uptime = getDeviceUptimeSeconds(&bootTimestamp) + + var state = state + state.applicationActivityTimestamp = MonotonicTimestamp(bootTimestap: bootTimestamp, uptime: uptime) + return state + } + } + + private func updateLockState(_ f: @escaping (LockState) -> LockState) { + Queue.mainQueue().async { + let updatedState = f(self.currentStateValue) + if updatedState != self.currentStateValue { + self.currentStateValue = updatedState + self.currentState.set(.single(updatedState)) + + let path = appLockStatePath(rootPath: self.rootPath) + + self.syncQueue.async { + if let data = try? JSONEncoder().encode(updatedState) { + let _ = try? data.write(to: URL(fileURLWithPath: path), options: .atomic) + } + } + } + } + } + + public var invalidAttempts: Signal { + return self.currentState.get() + |> map { state in + return state.unlockAttemts.flatMap { unlockAttemts in + return AccessChallengeAttempts(count: unlockAttemts.count, timestamp: unlockAttemts.wallClockTimestamp) + } + } + } + + public func lock() { + self.updateLockState { state in + var state = state + state.isManuallyLocked = true + return state + } + } + + public func unlock() { + self.updateLockState { state in + var state = state + + state.unlockAttemts = nil + + state.isManuallyLocked = false + + var bootTimestamp: Int32 = 0 + let uptime = getDeviceUptimeSeconds(&bootTimestamp) + let timestamp = MonotonicTimestamp(bootTimestap: bootTimestamp, uptime: uptime) + state.applicationActivityTimestamp = timestamp + + return state + } + } + + public func failedUnlockAttempt() { + self.updateLockState { state in + var state = state + var unlockAttemts = state.unlockAttemts ?? UnlockAttempts(count: 0, wallClockTimestamp: 0) + unlockAttemts.count += 1 + unlockAttemts.wallClockTimestamp = Int32(CFAbsoluteTimeGetCurrent()) + state.unlockAttemts = unlockAttemts + return state + } + } +} diff --git a/submodules/AppLock/Sources/LockedWindowCoveringView.swift b/submodules/AppLock/Sources/LockedWindowCoveringView.swift new file mode 100644 index 0000000000..6da82ea64e --- /dev/null +++ b/submodules/AppLock/Sources/LockedWindowCoveringView.swift @@ -0,0 +1,35 @@ +import Foundation +import Display +import TelegramPresentationData +import AsyncDisplayKit + +final class LockedWindowCoveringView: WindowCoveringView { + private let contentView: UIImageView + + init(theme: PresentationTheme) { + self.contentView = UIImageView() + + super.init(frame: CGRect()) + + self.backgroundColor = theme.chatList.backgroundColor + self.addSubview(self.contentView) + } + + required init?(coder aDecoder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func updateTheme(_ theme: PresentationTheme) { + self.backgroundColor = theme.chatList.backgroundColor + } + + func updateSnapshot(_ image: UIImage?) { + if image != nil { + self.contentView.image = image + } + } + + override func updateLayout(_ size: CGSize) { + self.contentView.frame = CGRect(origin: CGPoint(), size: size) + } +} diff --git a/submodules/AppLockState/BUCK b/submodules/AppLockState/BUCK new file mode 100644 index 0000000000..26e6f4360d --- /dev/null +++ b/submodules/AppLockState/BUCK @@ -0,0 +1,14 @@ +load("//Config:buck_rule_macros.bzl", "static_library") + +static_library( + name = "AppLockState", + srcs = glob([ + "Sources/**/*.swift", + ]), + deps = [ + "//submodules/MonotonicTime:MonotonicTime", + ], + frameworks = [ + "$SDKROOT/System/Library/Frameworks/Foundation.framework", + ], +) diff --git a/submodules/AppLockState/Sources/AppLockState.swift b/submodules/AppLockState/Sources/AppLockState.swift new file mode 100644 index 0000000000..a044c7901c --- /dev/null +++ b/submodules/AppLockState/Sources/AppLockState.swift @@ -0,0 +1,62 @@ +import Foundation +import MonotonicTime + +public struct MonotonicTimestamp: Codable, Equatable { + public var bootTimestap: Int32 + public var uptime: Int32 + + public init(bootTimestap: Int32, uptime: Int32) { + self.bootTimestap = bootTimestap + self.uptime = uptime + } +} + +public struct UnlockAttempts: Codable, Equatable { + public var count: Int32 + public var wallClockTimestamp: Int32 + + public init(count: Int32, wallClockTimestamp: Int32) { + self.count = count + self.wallClockTimestamp = wallClockTimestamp + } +} + +public struct LockState: Codable, Equatable { + public var isManuallyLocked: Bool + public var autolockTimeout: Int32? + public var unlockAttemts: UnlockAttempts? + public var applicationActivityTimestamp: MonotonicTimestamp? + + public init(isManuallyLocked: Bool = false, autolockTimeout: Int32? = nil, unlockAttemts: UnlockAttempts? = nil, applicationActivityTimestamp: MonotonicTimestamp? = nil) { + self.isManuallyLocked = isManuallyLocked + self.autolockTimeout = autolockTimeout + self.unlockAttemts = unlockAttemts + self.applicationActivityTimestamp = applicationActivityTimestamp + } +} + +public func appLockStatePath(rootPath: String) -> String { + return rootPath + "/lockState.json" +} + +public func isAppLocked(state: LockState) -> Bool { + if state.isManuallyLocked { + return true + } else if let autolockTimeout = state.autolockTimeout { + var bootTimestamp: Int32 = 0 + let uptime = getDeviceUptimeSeconds(&bootTimestamp) + let timestamp = MonotonicTimestamp(bootTimestap: bootTimestamp, uptime: uptime) + + if let applicationActivityTimestamp = state.applicationActivityTimestamp { + if timestamp.bootTimestap != applicationActivityTimestamp.bootTimestap { + return true + } + if timestamp.uptime >= applicationActivityTimestamp.uptime + autolockTimeout { + return true + } + } else { + return true + } + } + return false +} diff --git a/submodules/ChatListUI/Sources/ChatListController.swift b/submodules/ChatListUI/Sources/ChatListController.swift index 1d54e5b997..565ee5c4a2 100644 --- a/submodules/ChatListUI/Sources/ChatListController.swift +++ b/submodules/ChatListUI/Sources/ChatListController.swift @@ -249,7 +249,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, let passcode = context.sharedContext.accountManager.accessChallengeData() |> map { view -> (Bool, Bool) in let data = view.data - return (data.isLockable, data.autolockDeadline == 0) + return (data.isLockable, false) } if !self.hideNetworkActivityStatus { @@ -338,17 +338,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController, self.titleView.toggleIsLocked = { [weak self] in if let strongSelf = self { - let _ = (strongSelf.context.sharedContext.accountManager.transaction({ transaction -> Void in - var data = transaction.getAccessChallengeData() - if data.isLockable { - if data.autolockDeadline != 0 { - data = data.withUpdatedAutolockDeadline(0) - } else { - data = data.withUpdatedAutolockDeadline(nil) - } - transaction.setAccessChallengeData(data) - } - }) |> deliverOnMainQueue).start() + strongSelf.context.sharedContext.appLockContext.lock() } } diff --git a/submodules/LegacyDataImport/Sources/LegacyPreferencesImport.swift b/submodules/LegacyDataImport/Sources/LegacyPreferencesImport.swift index f54a75cc71..021ddb6c76 100644 --- a/submodules/LegacyDataImport/Sources/LegacyPreferencesImport.swift +++ b/submodules/LegacyDataImport/Sources/LegacyPreferencesImport.swift @@ -149,9 +149,9 @@ func importLegacyPreferences(accountManager: AccountManager, account: TemporaryA } if mode == 3 { - passcodeChallenge = .numericalPassword(value: passwordText, timeout: lockTimeout, attempts: nil) + passcodeChallenge = .numericalPassword(value: passwordText) } else if mode == 4 { - passcodeChallenge = PostboxAccessChallengeData.plaintextPassword(value: passwordText, timeout: lockTimeout, attempts: nil) + passcodeChallenge = PostboxAccessChallengeData.plaintextPassword(value: passwordText) } } } @@ -288,14 +288,6 @@ func importLegacyPreferences(accountManager: AccountManager, account: TemporaryA var settings: PresentationPasscodeSettings = current as? PresentationPasscodeSettings ?? .defaultSettings if let passcodeChallenge = passcodeChallenge { transaction.setAccessChallengeData(passcodeChallenge) - switch passcodeChallenge { - case .none: - break - case let .numericalPassword(_, timeout, _): - settings.autolockTimeout = timeout - case let .plaintextPassword(_, timeout, _): - settings.autolockTimeout = timeout - } settings.enableBiometrics = passcodeEnableBiometrics } return settings diff --git a/submodules/MonotonicTime/BUCK b/submodules/MonotonicTime/BUCK new file mode 100644 index 0000000000..46904b50fd --- /dev/null +++ b/submodules/MonotonicTime/BUCK @@ -0,0 +1,19 @@ +load("//Config:buck_rule_macros.bzl", "static_library") + +static_library( + name = "MonotonicTime", + srcs = glob([ + "Sources/**/*.m", + ]), + headers = glob([ + "Sources/**/*.h", + ]), + exported_headers = glob([ + "Sources/**/*.h", + ]), + deps = [ + ], + frameworks = [ + "$SDKROOT/System/Library/Frameworks/Foundation.framework", + ], +) diff --git a/submodules/MonotonicTime/Sources/DeviceUptime.h b/submodules/MonotonicTime/Sources/DeviceUptime.h new file mode 100644 index 0000000000..3b40d31b8a --- /dev/null +++ b/submodules/MonotonicTime/Sources/DeviceUptime.h @@ -0,0 +1,3 @@ +#import + +int32_t getDeviceUptimeSeconds(int32_t *bootTime); diff --git a/submodules/MonotonicTime/Sources/DeviceUptime.m b/submodules/MonotonicTime/Sources/DeviceUptime.m new file mode 100644 index 0000000000..87b0720092 --- /dev/null +++ b/submodules/MonotonicTime/Sources/DeviceUptime.m @@ -0,0 +1,22 @@ +#import "DeviceUptime.h" + +#include + +int32_t getDeviceUptimeSeconds(int32_t *bootTime) { + struct timeval boottime; + int mib[2] = {CTL_KERN, KERN_BOOTTIME}; + size_t size = sizeof(boottime); + time_t now; + time_t uptime = -1; + + (void)time(&now); + + if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && boottime.tv_sec != 0) { + uptime = now - boottime.tv_sec; + if (bootTime != NULL) { + *bootTime = boottime.tv_sec; + } + } + + return uptime; +} diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryController.swift b/submodules/PasscodeUI/Sources/PasscodeEntryController.swift index 3bdb3d95f0..7c3f31ecbc 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryController.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryController.swift @@ -34,7 +34,11 @@ public final class PasscodeEntryController: ViewController { return self.displayNode as! PasscodeEntryControllerNode } - private let context: AccountContext + private let applicationBindings: TelegramApplicationBindings + private let accountManager: AccountManager + private let appLockContext: AppLockContext + private let presentationDataSignal: Signal + private var presentationData: PresentationData private var presentationDataDisposable: Disposable? @@ -52,9 +56,12 @@ public final class PasscodeEntryController: ViewController { private var inBackground: Bool = false private var inBackgroundDisposable: Disposable? - public init(context: AccountContext, challengeData: PostboxAccessChallengeData, biometrics: PasscodeEntryControllerBiometricsMode, arguments: PasscodeEntryControllerPresentationArguments) { - self.context = context - self.presentationData = context.sharedContext.currentPresentationData.with { $0 } + public init(applicationBindings: TelegramApplicationBindings, accountManager: AccountManager, appLockContext: AppLockContext, presentationData: PresentationData, presentationDataSignal: Signal, challengeData: PostboxAccessChallengeData, biometrics: PasscodeEntryControllerBiometricsMode, arguments: PasscodeEntryControllerPresentationArguments) { + self.applicationBindings = applicationBindings + self.accountManager = accountManager + self.appLockContext = appLockContext + self.presentationData = presentationData + self.presentationDataSignal = presentationDataSignal self.challengeData = challengeData self.biometrics = biometrics self.arguments = arguments @@ -64,14 +71,14 @@ public final class PasscodeEntryController: ViewController { self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) self.statusBar.statusBarStyle = .White - self.presentationDataDisposable = (context.sharedContext.presentationData + self.presentationDataDisposable = (presentationDataSignal |> deliverOnMainQueue).start(next: { [weak self] presentationData in if let strongSelf = self, strongSelf.isNodeLoaded { strongSelf.controllerNode.updatePresentationData(presentationData) } }) - self.inBackgroundDisposable = (context.sharedContext.applicationBindings.applicationInForeground + self.inBackgroundDisposable = (applicationBindings.applicationInForeground |> deliverOnMainQueue).start(next: { [weak self] value in guard let strongSelf = self else { return @@ -96,7 +103,7 @@ public final class PasscodeEntryController: ViewController { override public func loadDisplayNode() { let passcodeType: PasscodeEntryFieldType switch self.challengeData { - case let .numericalPassword(value, _, _): + case let .numericalPassword(value): passcodeType = value.count == 6 ? .digits6 : .digits4 default: passcodeType = .alphanumeric @@ -104,7 +111,7 @@ public final class PasscodeEntryController: ViewController { let biometricsType: LocalAuthBiometricAuthentication? if case let .enabled(data) = self.biometrics { if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - if data == LocalAuth.evaluatedPolicyDomainState || (data == nil && !self.context.sharedContext.applicationBindings.isMainApp) { + if data == LocalAuth.evaluatedPolicyDomainState || (data == nil && !self.applicationBindings.isMainApp) { biometricsType = LocalAuth.biometricAuthentication } else { biometricsType = nil @@ -115,12 +122,11 @@ public final class PasscodeEntryController: ViewController { } else { biometricsType = nil } - self.displayNode = PasscodeEntryControllerNode(context: self.context, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, passcodeType: passcodeType, biometricsType: biometricsType, arguments: self.arguments, statusBar: self.statusBar) + self.displayNode = PasscodeEntryControllerNode(accountManager: self.accountManager, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, passcodeType: passcodeType, biometricsType: biometricsType, arguments: self.arguments, statusBar: self.statusBar) self.displayNodeDidLoad() - let _ = (self.context.sharedContext.accountManager.transaction({ transaction -> AccessChallengeAttempts? in - return transaction.getAccessChallengeData().attempts - }) |> deliverOnMainQueue).start(next: { [weak self] attempts in + let _ = (self.appLockContext.invalidAttempts + |> deliverOnMainQueue).start(next: { [weak self] attempts in guard let strongSelf = self else { return } @@ -136,9 +142,9 @@ public final class PasscodeEntryController: ViewController { switch strongSelf.challengeData { case .none: succeed = true - case let .numericalPassword(code, _, _): + case let .numericalPassword(code): succeed = passcode == normalizeArabicNumeralString(code, type: .western) - case let .plaintextPassword(code, _, _): + case let .plaintextPassword(code): succeed = passcode == code } @@ -146,22 +152,11 @@ public final class PasscodeEntryController: ViewController { if let completed = strongSelf.completed { completed() } else { - let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> Void in - var data = transaction.getAccessChallengeData().withUpdatedAutolockDeadline(nil) - switch data { - case .none: - break - case let .numericalPassword(value, timeout, _): - data = .numericalPassword(value: value, timeout: timeout, attempts: nil) - case let .plaintextPassword(value, timeout, _): - data = .plaintextPassword(value: value, timeout: timeout, attempts: nil) - } - transaction.setAccessChallengeData(data) - }).start() + strongSelf.appLockContext.unlock() } - let isMainApp = strongSelf.context.sharedContext.applicationBindings.isMainApp - let _ = updatePresentationPasscodeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { settings in + let isMainApp = strongSelf.applicationBindings.isMainApp + let _ = updatePresentationPasscodeSettingsInteractively(accountManager: strongSelf.accountManager, { settings in if isMainApp { return settings.withUpdatedBiometricsDomainState(LocalAuth.evaluatedPolicyDomainState) } else { @@ -169,36 +164,7 @@ public final class PasscodeEntryController: ViewController { } }).start() } else { - let _ = (strongSelf.context.sharedContext.accountManager.transaction({ transaction -> AccessChallengeAttempts in - var data = transaction.getAccessChallengeData() - let updatedAttempts: AccessChallengeAttempts - if let attempts = data.attempts { - var count = attempts.count + 1 - if count > 6 { - count = 1 - } - updatedAttempts = AccessChallengeAttempts(count: count, timestamp: Int32(CFAbsoluteTimeGetCurrent())) - } else { - updatedAttempts = AccessChallengeAttempts(count: 1, timestamp: Int32(CFAbsoluteTimeGetCurrent())) - } - switch data { - case .none: - break - case let .numericalPassword(value, timeout, _): - data = .numericalPassword(value: value, timeout: timeout, attempts: updatedAttempts) - case let .plaintextPassword(value, timeout, _): - data = .plaintextPassword(value: value, timeout: timeout, attempts: updatedAttempts) - } - transaction.setAccessChallengeData(data) - - return updatedAttempts - }) - |> deliverOnMainQueue).start(next: { [weak self] attempts in - if let strongSelf = self { - strongSelf.controllerNode.updateInvalidAttempts(attempts, animated: true) - } - }) - + strongSelf.appLockContext.failedUnlockAttempt() strongSelf.controllerNode.animateError() } } @@ -231,7 +197,7 @@ public final class PasscodeEntryController: ViewController { } if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { - if data == nil && self.context.sharedContext.applicationBindings.isMainApp { + if data == nil && self.applicationBindings.isMainApp { return } } @@ -258,8 +224,8 @@ public final class PasscodeEntryController: ViewController { if #available(iOSApplicationExtension 9.0, iOS 9.0, *) { if case let .enabled(storedDomainState) = strongSelf.biometrics, evaluatedPolicyDomainState != nil { - if !strongSelf.context.sharedContext.applicationBindings.isMainApp && storedDomainState == nil { - let _ = updatePresentationPasscodeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { settings in + if !strongSelf.applicationBindings.isMainApp && storedDomainState == nil { + let _ = updatePresentationPasscodeSettingsInteractively(accountManager: strongSelf.accountManager, { settings in return settings.withUpdatedShareBiometricsDomainState(LocalAuth.evaluatedPolicyDomainState) }).start() } else if storedDomainState != evaluatedPolicyDomainState { @@ -278,14 +244,8 @@ public final class PasscodeEntryController: ViewController { } strongSelf.hasOngoingBiometricsRequest = false } else { - let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> Void in - let data = transaction.getAccessChallengeData().withUpdatedAutolockDeadline(nil) - transaction.setAccessChallengeData(data) - }).start(completed: { [weak self] in - if let strongSelf = self { - strongSelf.hasOngoingBiometricsRequest = false - } - }) + strongSelf.appLockContext.unlock() + strongSelf.hasOngoingBiometricsRequest = false } } else { strongSelf.hasOngoingBiometricsRequest = false diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift index 50432817d9..f5d15761d2 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift @@ -17,7 +17,7 @@ private let subtitleFont = Font.regular(15.0) private let buttonFont = Font.regular(17.0) final class PasscodeEntryControllerNode: ASDisplayNode { - private let context: AccountContext + private let accountManager: AccountManager private var theme: PresentationTheme private var strings: PresentationStrings private var wallpaper: TelegramWallpaper @@ -49,8 +49,8 @@ final class PasscodeEntryControllerNode: ASDisplayNode { var checkPasscode: ((String) -> Void)? var requestBiometrics: (() -> Void)? - init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, passcodeType: PasscodeEntryFieldType, biometricsType: LocalAuthBiometricAuthentication?, arguments: PasscodeEntryControllerPresentationArguments, statusBar: StatusBar) { - self.context = context + init(accountManager: AccountManager, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, passcodeType: PasscodeEntryFieldType, biometricsType: LocalAuthBiometricAuthentication?, arguments: PasscodeEntryControllerPresentationArguments, statusBar: StatusBar) { + self.accountManager = accountManager self.theme = theme self.strings = strings self.wallpaper = wallpaper @@ -174,7 +174,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode { switch self.wallpaper { case .image, .file: - if let image = chatControllerBackgroundImage(theme: self.theme, wallpaper: self.wallpaper, mediaBox: self.context.sharedContext.accountManager.mediaBox, composed: false, knockoutMode: false) { + if let image = chatControllerBackgroundImage(theme: self.theme, wallpaper: self.wallpaper, mediaBox: self.accountManager.mediaBox, composed: false, knockoutMode: false) { self.background = ImageBasedPasscodeBackground(image: image, size: size) } else { self.background = GradientPasscodeBackground(size: size, backgroundColors: self.theme.passcode.backgroundColors.colors, buttonColor: self.theme.passcode.buttonColor) diff --git a/submodules/PasscodeUI/Sources/PasscodeSetupControllerNode.swift b/submodules/PasscodeUI/Sources/PasscodeSetupControllerNode.swift index d428ff9aee..6d2221e05b 100644 --- a/submodules/PasscodeUI/Sources/PasscodeSetupControllerNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeSetupControllerNode.swift @@ -75,7 +75,7 @@ final class PasscodeSetupControllerNode: ASDisplayNode { switch self.mode { case let .entry(challenge): switch challenge { - case let .numericalPassword(value, _, _): + case let .numericalPassword(value): passcodeType = value.count == 6 ? .digits6 : .digits4 default: passcodeType = .alphanumeric diff --git a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift index 2b1c511a72..eae78e8b46 100644 --- a/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift +++ b/submodules/PeerInfoUI/Sources/ChannelMembersSearchContainerNode.swift @@ -1151,6 +1151,9 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod } }) + self.emptyQueryListNode.beganInteractiveDragging = { [weak self] in + self?.dismissInput?() + } self.listNode.beganInteractiveDragging = { [weak self] in self?.dismissInput?() } diff --git a/submodules/Postbox/Postbox/AccountManagerMetadataTable.swift b/submodules/Postbox/Postbox/AccountManagerMetadataTable.swift index 2085661fd6..feffb5f2da 100644 --- a/submodules/Postbox/Postbox/AccountManagerMetadataTable.swift +++ b/submodules/Postbox/Postbox/AccountManagerMetadataTable.swift @@ -22,17 +22,17 @@ public struct AccessChallengeAttempts: PostboxCoding, Equatable { public enum PostboxAccessChallengeData: PostboxCoding, Equatable { case none - case numericalPassword(value: String, timeout: Int32?, attempts: AccessChallengeAttempts?) - case plaintextPassword(value: String, timeout: Int32?, attempts: AccessChallengeAttempts?) + case numericalPassword(value: String) + case plaintextPassword(value: String) public init(decoder: PostboxDecoder) { switch decoder.decodeInt32ForKey("r", orElse: 0) { case 0: self = .none case 1: - self = .numericalPassword(value: decoder.decodeStringForKey("t", orElse: ""), timeout: decoder.decodeOptionalInt32ForKey("a"), attempts: decoder.decodeObjectForKey("att", decoder: { AccessChallengeAttempts(decoder: $0) }) as? AccessChallengeAttempts) + self = .numericalPassword(value: decoder.decodeStringForKey("t", orElse: "")) case 2: - self = .plaintextPassword(value: decoder.decodeStringForKey("t", orElse: ""), timeout: decoder.decodeOptionalInt32ForKey("a"), attempts: decoder.decodeObjectForKey("att", decoder: { AccessChallengeAttempts(decoder: $0) }) as? AccessChallengeAttempts) + self = .plaintextPassword(value: decoder.decodeStringForKey("t", orElse: "")) default: assertionFailure() self = .none @@ -43,32 +43,12 @@ public enum PostboxAccessChallengeData: PostboxCoding, Equatable { switch self { case .none: encoder.encodeInt32(0, forKey: "r") - case let .numericalPassword(text, timeout, attempts): + case let .numericalPassword(text): encoder.encodeInt32(1, forKey: "r") encoder.encodeString(text, forKey: "t") - if let timeout = timeout { - encoder.encodeInt32(timeout, forKey: "a") - } else { - encoder.encodeNil(forKey: "a") - } - if let attempts = attempts { - encoder.encodeObject(attempts, forKey: "att") - } else { - encoder.encodeNil(forKey: "att") - } - case let .plaintextPassword(text, timeout, attempts): + case let .plaintextPassword(text): encoder.encodeInt32(2, forKey: "r") encoder.encodeString(text, forKey: "t") - if let timeout = timeout { - encoder.encodeInt32(timeout, forKey: "a") - } else { - encoder.encodeNil(forKey: "a") - } - if let attempts = attempts { - encoder.encodeObject(attempts, forKey: "att") - } else { - encoder.encodeNil(forKey: "att") - } } } @@ -79,39 +59,6 @@ public enum PostboxAccessChallengeData: PostboxCoding, Equatable { return true } } - - public var autolockDeadline: Int32? { - switch self { - case .none: - return nil - case let .numericalPassword(_, timeout, _): - return timeout - case let .plaintextPassword(_, timeout, _): - return timeout - } - } - - public var attempts: AccessChallengeAttempts? { - switch self { - case .none: - return nil - case let .numericalPassword(_, _, attempts): - return attempts - case let .plaintextPassword(_, _, attempts): - return attempts - } - } - - public func withUpdatedAutolockDeadline(_ autolockDeadline: Int32?) -> PostboxAccessChallengeData { - switch self { - case .none: - return self - case let .numericalPassword(value, _, attempts): - return .numericalPassword(value: value, timeout: autolockDeadline, attempts: attempts) - case let .plaintextPassword(value, _, attempts): - return .plaintextPassword(value: value, timeout: autolockDeadline, attempts: attempts) - } - } } public struct AuthAccountRecord: PostboxCoding, Codable { diff --git a/submodules/SettingsUI/Sources/Privacy and Security/PasscodeOptionsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/PasscodeOptionsController.swift index f0ec9fe268..336599c918 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/PasscodeOptionsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/PasscodeOptionsController.swift @@ -254,9 +254,9 @@ func passcodeOptionsController(context: AccountContext) -> ViewController { let _ = (context.sharedContext.accountManager.transaction({ transaction -> Void in var data = transaction.getAccessChallengeData() if numerical { - data = PostboxAccessChallengeData.numericalPassword(value: passcode, timeout: data.autolockDeadline, attempts: nil) + data = PostboxAccessChallengeData.numericalPassword(value: passcode) } else { - data = PostboxAccessChallengeData.plaintextPassword(value: passcode, timeout: data.autolockDeadline, attempts: nil) + data = PostboxAccessChallengeData.plaintextPassword(value: passcode) } transaction.setAccessChallengeData(data) @@ -298,9 +298,9 @@ func passcodeOptionsController(context: AccountContext) -> ViewController { let _ = (context.sharedContext.accountManager.transaction({ transaction -> Void in var data = transaction.getAccessChallengeData() if numerical { - data = PostboxAccessChallengeData.numericalPassword(value: passcode, timeout: data.autolockDeadline, attempts: nil) + data = PostboxAccessChallengeData.numericalPassword(value: passcode) } else { - data = PostboxAccessChallengeData.plaintextPassword(value: passcode, timeout: data.autolockDeadline, attempts: nil) + data = PostboxAccessChallengeData.plaintextPassword(value: passcode) } transaction.setAccessChallengeData(data) }) |> deliverOnMainQueue).start(next: { _ in @@ -403,9 +403,9 @@ public func passcodeOptionsAccessController(context: AccountContext, animateIn: let _ = (context.sharedContext.accountManager.transaction({ transaction -> Void in var data = transaction.getAccessChallengeData() if numerical { - data = PostboxAccessChallengeData.numericalPassword(value: passcode, timeout: data.autolockDeadline, attempts: nil) + data = PostboxAccessChallengeData.numericalPassword(value: passcode) } else { - data = PostboxAccessChallengeData.plaintextPassword(value: passcode, timeout: data.autolockDeadline, attempts: nil) + data = PostboxAccessChallengeData.plaintextPassword(value: passcode) } transaction.setAccessChallengeData(data) @@ -426,9 +426,9 @@ public func passcodeOptionsAccessController(context: AccountContext, animateIn: switch challenge { case .none: succeed = true - case let .numericalPassword(code, _, _): + case let .numericalPassword(code): succeed = passcode == normalizeArabicNumeralString(code, type: .western) - case let .plaintextPassword(code, _, _): + case let .plaintextPassword(code): succeed = passcode == code } if succeed { @@ -463,7 +463,7 @@ public func passcodeEntryController(context: AccountContext, animateIn: Bool = t } else { biometrics = .none } - let controller = PasscodeEntryController(context: context, challengeData: challenge, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: false, fadeIn: true, cancel: { + let controller = PasscodeEntryController(applicationBindings: context.sharedContext.applicationBindings, accountManager: context.sharedContext.accountManager, appLockContext: context.sharedContext.appLockContext, presentationData: context.sharedContext.currentPresentationData.with { $0 }, presentationDataSignal: context.sharedContext.presentationData, challengeData: challenge, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: false, fadeIn: true, cancel: { completion(false) })) controller.presentationCompleted = { [weak controller] in diff --git a/submodules/TelegramCore/TelegramCore/Account.swift b/submodules/TelegramCore/TelegramCore/Account.swift index 8509fd3300..bb884e5a9d 100644 --- a/submodules/TelegramCore/TelegramCore/Account.swift +++ b/submodules/TelegramCore/TelegramCore/Account.swift @@ -910,13 +910,7 @@ public class Account { }).start() self.notificationAutolockReportManager = NotificationAutolockReportManager(deadline: self.autolockReportDeadline.get(), network: network) self.autolockReportDeadline.set( - accountManager.accessChallengeData() - |> map { dataView -> Int32? in - guard let autolockDeadline = dataView.data.autolockDeadline else { - return nil - } - return autolockDeadline - } + networkArguments.autolockDeadine |> distinctUntilChanged ) diff --git a/submodules/TelegramCore/TelegramCore/Network.swift b/submodules/TelegramCore/TelegramCore/Network.swift index 6c36eb080b..6908c0f947 100644 --- a/submodules/TelegramCore/TelegramCore/Network.swift +++ b/submodules/TelegramCore/TelegramCore/Network.swift @@ -410,14 +410,16 @@ public struct NetworkInitializationArguments { public let appVersion: String public let voipMaxLayer: Int32 public let appData: Signal + public let autolockDeadine: Signal public let encryptionProvider: EncryptionProvider - public init(apiId: Int32, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, appData: Signal, encryptionProvider: EncryptionProvider) { + public init(apiId: Int32, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, appData: Signal, autolockDeadine: Signal, encryptionProvider: EncryptionProvider) { self.apiId = apiId self.languagesCategory = languagesCategory self.appVersion = appVersion self.voipMaxLayer = voipMaxLayer self.appData = appData + self.autolockDeadine = autolockDeadine self.encryptionProvider = encryptionProvider } } diff --git a/submodules/TelegramUI/TelegramUI/AppDelegate.swift b/submodules/TelegramUI/TelegramUI/AppDelegate.swift index 1d93d27080..347721b101 100644 --- a/submodules/TelegramUI/TelegramUI/AppDelegate.swift +++ b/submodules/TelegramUI/TelegramUI/AppDelegate.swift @@ -31,6 +31,7 @@ import UrlHandling import WalletUrl import WalletCore import OpenSSLEncryptionProvider +import AppLock #if canImport(BackgroundTasks) import BackgroundTasks @@ -362,7 +363,7 @@ final class SharedApplicationContext { Logger.shared.log("data", "can't deserialize") } return data - }, encryptionProvider: OpenSSLEncryptionProvider()) + }, autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider()) guard let appGroupUrl = maybeAppGroupUrl else { UIAlertView(title: nil, message: "Error 2", delegate: nil, cancelButtonTitle: "OK").show() @@ -725,8 +726,13 @@ final class SharedApplicationContext { let legacyBasePath = appGroupUrl.path let legacyCache = LegacyCache(path: legacyBasePath + "/Caches") + let presentationDataPromise = Promise() + let appLockContext = AppLockContextImpl(rootPath: rootPath, window: self.mainWindow!, applicationBindings: applicationBindings, accountManager: accountManager, presentationDataSignal: presentationDataPromise.get(), lockIconInitialFrame: { + return (self.mainWindow?.viewController as? TelegramRootController)?.chatListController?.lockViewFrame + }) + var setPresentationCall: ((PresentationCall?) -> Void)? - let sharedContext = SharedAccountContextImpl(mainWindow: self.mainWindow, basePath: rootPath, encryptionParameters: encryptionParameters, accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings, networkArguments: networkArguments, rootPath: rootPath, legacyBasePath: legacyBasePath, legacyCache: legacyCache, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init), setNotificationCall: { call in + let sharedContext = SharedAccountContextImpl(mainWindow: self.mainWindow, basePath: rootPath, encryptionParameters: encryptionParameters, accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings, networkArguments: networkArguments, rootPath: rootPath, legacyBasePath: legacyBasePath, legacyCache: legacyCache, apsNotificationToken: self.notificationTokenPromise.get() |> map(Optional.init), voipNotificationToken: self.voipTokenPromise.get() |> map(Optional.init), setNotificationCall: { call in setPresentationCall?(call) }, navigateToChat: { accountId, peerId, messageId in self.openChatWhenReady(accountId: accountId, peerId: peerId, messageId: messageId) @@ -748,6 +754,8 @@ final class SharedApplicationContext { } }) + presentationDataPromise.set(sharedContext.presentationData) + let rawAccounts = sharedContext.activeAccounts |> map { _, accounts, _ -> [Account] in return accounts.map({ $0.1 }) diff --git a/submodules/TelegramUI/TelegramUI/ApplicationContext.swift b/submodules/TelegramUI/TelegramUI/ApplicationContext.swift index 2438cc6ccf..5230fd32e8 100644 --- a/submodules/TelegramUI/TelegramUI/ApplicationContext.swift +++ b/submodules/TelegramUI/TelegramUI/ApplicationContext.swift @@ -22,14 +22,6 @@ import ImageBlur import WatchBridge import SettingsUI -func isAccessLocked(data: PostboxAccessChallengeData, at timestamp: Int32) -> Bool { - if data.isLockable, let autolockDeadline = data.autolockDeadline, autolockDeadline <= timestamp { - return true - } else { - return false - } -} - final class UnauthorizedApplicationContext { let sharedContext: SharedAccountContextImpl let account: UnauthorizedAccount @@ -56,14 +48,6 @@ final class UnauthorizedApplicationContext { } } -private struct PasscodeState: Equatable { - let isActive: Bool - let challengeData: PostboxAccessChallengeData - let autolockTimeout: Int32? - let enableBiometrics: Bool - let biometricsDomainState: Data? -} - final class AuthorizedApplicationContext { let sharedApplicationContext: SharedApplicationContext let mainWindow: Window1 @@ -166,7 +150,7 @@ final class AuthorizedApplicationContext { context.keyShortcutsController = keyShortcutsController } - let previousPasscodeState = Atomic(value: nil) + /*let previousPasscodeState = Atomic(value: nil) let passcodeStatusData = combineLatest(queue: Queue.mainQueue(), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]), context.sharedContext.accountManager.accessChallengeData(), context.sharedContext.applicationBindings.applicationIsActive) let passcodeState = passcodeStatusData |> map { sharedData, accessChallengeDataView, isActive -> PasscodeState in @@ -331,7 +315,21 @@ final class AuthorizedApplicationContext { } else { strongSelf.isReady.set(.single(true)) } - })) + }))*/ + + if self.rootController.rootTabController == nil { + self.rootController.addRootControllers(showCallsTab: self.showCallsTab) + } + if let tabsController = self.rootController.viewControllers.first as? TabBarController, !tabsController.controllers.isEmpty, tabsController.selectedIndex >= 0 { + let controller = tabsController.controllers[tabsController.selectedIndex] + let combinedReady = combineLatest(tabsController.ready.get(), controller.ready.get()) + |> map { $0 && $1 } + |> filter { $0 } + |> take(1) + self.isReady.set(combinedReady) + } else { + self.isReady.set(.single(true)) + } let accountId = context.account.id self.loggedOutDisposable.set((context.account.loggedOut diff --git a/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift b/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift index 8d3ac76c50..e8fc705370 100644 --- a/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift +++ b/submodules/TelegramUI/TelegramUI/NotificationContentContext.swift @@ -15,6 +15,7 @@ import PhotoResources import AnimatedStickerNode import TelegramAnimatedStickerNode import OpenSSLEncryptionProvider +import AppLock private enum NotificationContentAuthorizationError { case unauthorized @@ -145,7 +146,15 @@ public final class NotificationViewControllerImpl { f(false) }) - sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, appData: .single(self.initializationData.bundleData), encryptionProvider: OpenSSLEncryptionProvider()), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in }) + let presentationDataPromise = Promise() + + let appLockContext = AppLockContextImpl(rootPath: rootPath, window: nil, applicationBindings: applicationBindings, accountManager: accountManager, presentationDataSignal: presentationDataPromise.get(), lockIconInitialFrame: { + return nil + }) + + sharedAccountContext = SharedAccountContextImpl(mainWindow: nil, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider()), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in }) + + presentationDataPromise.set(sharedAccountContext!.presentationData) } } diff --git a/submodules/TelegramUI/TelegramUI/ShareExtensionContext.swift b/submodules/TelegramUI/TelegramUI/ShareExtensionContext.swift index 562cc6e740..6389e7327a 100644 --- a/submodules/TelegramUI/TelegramUI/ShareExtensionContext.swift +++ b/submodules/TelegramUI/TelegramUI/ShareExtensionContext.swift @@ -13,6 +13,7 @@ import PeerInfoUI import ShareItems import SettingsUI import OpenSSLEncryptionProvider +import AppLock private let inForeground = ValuePromise(false, ignoreRepeated: true) @@ -173,7 +174,14 @@ public class ShareRootControllerImpl { }) semaphore.wait() - let sharedContext = SharedAccountContextImpl(mainWindow: nil, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, appData: .single(self.initializationData.bundleData), encryptionProvider: OpenSSLEncryptionProvider()), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in }) + let presentationDataPromise = Promise() + + let appLockContext = AppLockContextImpl(rootPath: rootPath, window: nil, applicationBindings: applicationBindings, accountManager: accountManager, presentationDataSignal: presentationDataPromise.get(), lockIconInitialFrame: { + return nil + }) + + let sharedContext = SharedAccountContextImpl(mainWindow: nil, basePath: rootPath, encryptionParameters: ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: self.initializationData.encryptionParameters.0)!, salt: ValueBoxEncryptionParameters.Salt(data: self.initializationData.encryptionParameters.1)!), accountManager: accountManager, appLockContext: appLockContext, applicationBindings: applicationBindings, initialPresentationDataAndSettings: initialPresentationDataAndSettings!, networkArguments: NetworkInitializationArguments(apiId: self.initializationData.apiId, languagesCategory: self.initializationData.languagesCategory, appVersion: self.initializationData.appVersion, voipMaxLayer: 0, appData: .single(self.initializationData.bundleData), autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider()), rootPath: rootPath, legacyBasePath: nil, legacyCache: nil, apsNotificationToken: .never(), voipNotificationToken: .never(), setNotificationCall: { _ in }, navigateToChat: { _, _, _ in }) + presentationDataPromise.set(sharedContext.presentationData) internalContext = InternalContext(sharedContext: sharedContext) globalInternalContext = internalContext } diff --git a/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift b/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift index 5b4c0ecbe9..63db234c02 100644 --- a/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift +++ b/submodules/TelegramUI/TelegramUI/SharedAccountContext.swift @@ -60,6 +60,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { public let applicationBindings: TelegramApplicationBindings public let basePath: String public let accountManager: AccountManager + public let appLockContext: AppLockContext private let navigateToChatImpl: (AccountRecordId, PeerId, MessageId?) -> Void @@ -146,7 +147,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { private var widgetDataContext: WidgetDataContext? - public init(mainWindow: Window1?, basePath: String, encryptionParameters: ValueBoxEncryptionParameters, accountManager: AccountManager, applicationBindings: TelegramApplicationBindings, initialPresentationDataAndSettings: InitialPresentationDataAndSettings, networkArguments: NetworkInitializationArguments, rootPath: String, legacyBasePath: String?, legacyCache: LegacyCache?, apsNotificationToken: Signal, voipNotificationToken: Signal, setNotificationCall: @escaping (PresentationCall?) -> Void, navigateToChat: @escaping (AccountRecordId, PeerId, MessageId?) -> Void, displayUpgradeProgress: @escaping (Float?) -> Void = { _ in }) { + public init(mainWindow: Window1?, basePath: String, encryptionParameters: ValueBoxEncryptionParameters, accountManager: AccountManager, appLockContext: AppLockContext, applicationBindings: TelegramApplicationBindings, initialPresentationDataAndSettings: InitialPresentationDataAndSettings, networkArguments: NetworkInitializationArguments, rootPath: String, legacyBasePath: String?, legacyCache: LegacyCache?, apsNotificationToken: Signal, voipNotificationToken: Signal, setNotificationCall: @escaping (PresentationCall?) -> Void, navigateToChat: @escaping (AccountRecordId, PeerId, MessageId?) -> Void, displayUpgradeProgress: @escaping (Float?) -> Void = { _ in }) { assert(Queue.mainQueue().isCurrent()) precondition(!testHasInstance) @@ -158,6 +159,7 @@ public final class SharedAccountContextImpl: SharedAccountContext { self.accountManager = accountManager self.navigateToChatImpl = navigateToChat self.displayUpgradeProgress = displayUpgradeProgress + self.appLockContext = appLockContext self.accountManager.mediaBox.fetchCachedResourceRepresentation = { (resource, representation) -> Signal in return fetchCachedSharedResourceRepresentation(accountManager: accountManager, resource: resource, representation: representation)