mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Improved locking
This commit is contained in:
parent
7d465b0f93
commit
f75a73d257
1
BUCK
1
BUCK
@ -247,6 +247,7 @@ apple_binary(
|
|||||||
deps = [
|
deps = [
|
||||||
"//submodules/BuildConfig:BuildConfig",
|
"//submodules/BuildConfig:BuildConfig",
|
||||||
"//submodules/WidgetItems:WidgetItems",
|
"//submodules/WidgetItems:WidgetItems",
|
||||||
|
"//submodules/AppLockState:AppLockState",
|
||||||
],
|
],
|
||||||
frameworks = [
|
frameworks = [
|
||||||
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
|
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
|
||||||
|
@ -99,7 +99,7 @@ class IntentHandler: INExtension, INSendMessageIntentHandling, INSearchForMessag
|
|||||||
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
|
let deviceSpecificEncryptionParameters = BuildConfig.deviceSpecificEncryptionParameters(rootPath, baseAppBundleId: baseAppBundleId)
|
||||||
let encryptionParameters = ValueBoxEncryptionParameters(forceEncryptionIfNoSet: false, key: ValueBoxEncryptionParameters.Key(data: deviceSpecificEncryptionParameters.key)!, salt: ValueBoxEncryptionParameters.Salt(data: deviceSpecificEncryptionParameters.salt)!)
|
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<Account?, NoError> in
|
|> mapToSignal { account -> Signal<Account?, NoError> in
|
||||||
if let account = account {
|
if let account = account {
|
||||||
switch account {
|
switch account {
|
||||||
|
@ -2,6 +2,7 @@ import UIKit
|
|||||||
import NotificationCenter
|
import NotificationCenter
|
||||||
import BuildConfig
|
import BuildConfig
|
||||||
import WidgetItems
|
import WidgetItems
|
||||||
|
import AppLockState
|
||||||
|
|
||||||
private func rootPathForBasePath(_ appGroupPath: String) -> String {
|
private func rootPathForBasePath(_ appGroupPath: String) -> String {
|
||||||
return appGroupPath + "/telegram-data"
|
return appGroupPath + "/telegram-data"
|
||||||
@ -13,14 +14,11 @@ class TodayViewController: UIViewController, NCWidgetProviding {
|
|||||||
|
|
||||||
private var buildConfig: BuildConfig?
|
private var buildConfig: BuildConfig?
|
||||||
|
|
||||||
|
private var appLockedLabel: UILabel?
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
if self.initializedInterface {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
self.initializedInterface = true
|
|
||||||
|
|
||||||
let appBundleIdentifier = Bundle.main.bundleIdentifier!
|
let appBundleIdentifier = Bundle.main.bundleIdentifier!
|
||||||
guard let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
|
guard let lastDotRange = appBundleIdentifier.range(of: ".", options: [.backwards]) else {
|
||||||
return
|
return
|
||||||
@ -38,6 +36,23 @@ class TodayViewController: UIViewController, NCWidgetProviding {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let rootPath = rootPathForBasePath(appGroupUrl.path)
|
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"
|
let dataPath = rootPath + "/widget-data"
|
||||||
|
|
||||||
if let data = try? Data(contentsOf: URL(fileURLWithPath: dataPath)), let widgetData = try? JSONDecoder().decode(WidgetData.self, from: 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) {
|
private func updateLayout(size: CGSize) {
|
||||||
self.validLayout = size
|
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)
|
let peerSize = CGSize(width: 70.0, height: 100.0)
|
||||||
|
|
||||||
var peerFrames: [CGRect] = []
|
var peerFrames: [CGRect] = []
|
||||||
|
@ -384,10 +384,19 @@ public enum CreateGroupMode {
|
|||||||
case locatedGroup(latitude: Double, longitude: Double, address: String?)
|
case locatedGroup(latitude: Double, longitude: Double, address: String?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public protocol AppLockContext: class {
|
||||||
|
var invalidAttempts: Signal<AccessChallengeAttempts?, NoError> { get }
|
||||||
|
|
||||||
|
func lock()
|
||||||
|
func unlock()
|
||||||
|
func failedUnlockAttempt()
|
||||||
|
}
|
||||||
|
|
||||||
public protocol SharedAccountContext: class {
|
public protocol SharedAccountContext: class {
|
||||||
var basePath: String { get }
|
var basePath: String { get }
|
||||||
var mainWindow: Window1? { get }
|
var mainWindow: Window1? { get }
|
||||||
var accountManager: AccountManager { get }
|
var accountManager: AccountManager { get }
|
||||||
|
var appLockContext: AppLockContext { get }
|
||||||
|
|
||||||
var currentPresentationData: Atomic<PresentationData> { get }
|
var currentPresentationData: Atomic<PresentationData> { get }
|
||||||
var presentationData: Signal<PresentationData, NoError> { get }
|
var presentationData: Signal<PresentationData, NoError> { get }
|
||||||
|
25
submodules/AppLock/BUCK
Normal file
25
submodules/AppLock/BUCK
Normal file
@ -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",
|
||||||
|
],
|
||||||
|
)
|
247
submodules/AppLock/Sources/AppLock.swift
Normal file
247
submodules/AppLock/Sources/AppLock.swift
Normal file
@ -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<PresentationData, NoError>
|
||||||
|
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<LockState>()
|
||||||
|
|
||||||
|
public init(rootPath: String, window: Window1?, applicationBindings: TelegramApplicationBindings, accountManager: AccountManager, presentationDataSignal: Signal<PresentationData, NoError>, 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<AccessChallengeAttempts?, NoError> {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
35
submodules/AppLock/Sources/LockedWindowCoveringView.swift
Normal file
35
submodules/AppLock/Sources/LockedWindowCoveringView.swift
Normal file
@ -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)
|
||||||
|
}
|
||||||
|
}
|
14
submodules/AppLockState/BUCK
Normal file
14
submodules/AppLockState/BUCK
Normal file
@ -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",
|
||||||
|
],
|
||||||
|
)
|
62
submodules/AppLockState/Sources/AppLockState.swift
Normal file
62
submodules/AppLockState/Sources/AppLockState.swift
Normal file
@ -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
|
||||||
|
}
|
@ -249,7 +249,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
let passcode = context.sharedContext.accountManager.accessChallengeData()
|
let passcode = context.sharedContext.accountManager.accessChallengeData()
|
||||||
|> map { view -> (Bool, Bool) in
|
|> map { view -> (Bool, Bool) in
|
||||||
let data = view.data
|
let data = view.data
|
||||||
return (data.isLockable, data.autolockDeadline == 0)
|
return (data.isLockable, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
if !self.hideNetworkActivityStatus {
|
if !self.hideNetworkActivityStatus {
|
||||||
@ -338,17 +338,7 @@ public class ChatListControllerImpl: TelegramBaseController, ChatListController,
|
|||||||
|
|
||||||
self.titleView.toggleIsLocked = { [weak self] in
|
self.titleView.toggleIsLocked = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let _ = (strongSelf.context.sharedContext.accountManager.transaction({ transaction -> Void in
|
strongSelf.context.sharedContext.appLockContext.lock()
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,9 +149,9 @@ func importLegacyPreferences(accountManager: AccountManager, account: TemporaryA
|
|||||||
}
|
}
|
||||||
|
|
||||||
if mode == 3 {
|
if mode == 3 {
|
||||||
passcodeChallenge = .numericalPassword(value: passwordText, timeout: lockTimeout, attempts: nil)
|
passcodeChallenge = .numericalPassword(value: passwordText)
|
||||||
} else if mode == 4 {
|
} 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
|
var settings: PresentationPasscodeSettings = current as? PresentationPasscodeSettings ?? .defaultSettings
|
||||||
if let passcodeChallenge = passcodeChallenge {
|
if let passcodeChallenge = passcodeChallenge {
|
||||||
transaction.setAccessChallengeData(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
|
settings.enableBiometrics = passcodeEnableBiometrics
|
||||||
}
|
}
|
||||||
return settings
|
return settings
|
||||||
|
19
submodules/MonotonicTime/BUCK
Normal file
19
submodules/MonotonicTime/BUCK
Normal file
@ -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",
|
||||||
|
],
|
||||||
|
)
|
3
submodules/MonotonicTime/Sources/DeviceUptime.h
Normal file
3
submodules/MonotonicTime/Sources/DeviceUptime.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
int32_t getDeviceUptimeSeconds(int32_t *bootTime);
|
22
submodules/MonotonicTime/Sources/DeviceUptime.m
Normal file
22
submodules/MonotonicTime/Sources/DeviceUptime.m
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#import "DeviceUptime.h"
|
||||||
|
|
||||||
|
#include <sys/sysctl.h>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
@ -34,7 +34,11 @@ public final class PasscodeEntryController: ViewController {
|
|||||||
return self.displayNode as! PasscodeEntryControllerNode
|
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<PresentationData, NoError>
|
||||||
|
|
||||||
private var presentationData: PresentationData
|
private var presentationData: PresentationData
|
||||||
private var presentationDataDisposable: Disposable?
|
private var presentationDataDisposable: Disposable?
|
||||||
|
|
||||||
@ -52,9 +56,12 @@ public final class PasscodeEntryController: ViewController {
|
|||||||
private var inBackground: Bool = false
|
private var inBackground: Bool = false
|
||||||
private var inBackgroundDisposable: Disposable?
|
private var inBackgroundDisposable: Disposable?
|
||||||
|
|
||||||
public init(context: AccountContext, challengeData: PostboxAccessChallengeData, biometrics: PasscodeEntryControllerBiometricsMode, arguments: PasscodeEntryControllerPresentationArguments) {
|
public init(applicationBindings: TelegramApplicationBindings, accountManager: AccountManager, appLockContext: AppLockContext, presentationData: PresentationData, presentationDataSignal: Signal<PresentationData, NoError>, challengeData: PostboxAccessChallengeData, biometrics: PasscodeEntryControllerBiometricsMode, arguments: PasscodeEntryControllerPresentationArguments) {
|
||||||
self.context = context
|
self.applicationBindings = applicationBindings
|
||||||
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
self.accountManager = accountManager
|
||||||
|
self.appLockContext = appLockContext
|
||||||
|
self.presentationData = presentationData
|
||||||
|
self.presentationDataSignal = presentationDataSignal
|
||||||
self.challengeData = challengeData
|
self.challengeData = challengeData
|
||||||
self.biometrics = biometrics
|
self.biometrics = biometrics
|
||||||
self.arguments = arguments
|
self.arguments = arguments
|
||||||
@ -64,14 +71,14 @@ public final class PasscodeEntryController: ViewController {
|
|||||||
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
|
||||||
self.statusBar.statusBarStyle = .White
|
self.statusBar.statusBarStyle = .White
|
||||||
|
|
||||||
self.presentationDataDisposable = (context.sharedContext.presentationData
|
self.presentationDataDisposable = (presentationDataSignal
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
|
||||||
if let strongSelf = self, strongSelf.isNodeLoaded {
|
if let strongSelf = self, strongSelf.isNodeLoaded {
|
||||||
strongSelf.controllerNode.updatePresentationData(presentationData)
|
strongSelf.controllerNode.updatePresentationData(presentationData)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
self.inBackgroundDisposable = (context.sharedContext.applicationBindings.applicationInForeground
|
self.inBackgroundDisposable = (applicationBindings.applicationInForeground
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] value in
|
|> deliverOnMainQueue).start(next: { [weak self] value in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -96,7 +103,7 @@ public final class PasscodeEntryController: ViewController {
|
|||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
let passcodeType: PasscodeEntryFieldType
|
let passcodeType: PasscodeEntryFieldType
|
||||||
switch self.challengeData {
|
switch self.challengeData {
|
||||||
case let .numericalPassword(value, _, _):
|
case let .numericalPassword(value):
|
||||||
passcodeType = value.count == 6 ? .digits6 : .digits4
|
passcodeType = value.count == 6 ? .digits6 : .digits4
|
||||||
default:
|
default:
|
||||||
passcodeType = .alphanumeric
|
passcodeType = .alphanumeric
|
||||||
@ -104,7 +111,7 @@ public final class PasscodeEntryController: ViewController {
|
|||||||
let biometricsType: LocalAuthBiometricAuthentication?
|
let biometricsType: LocalAuthBiometricAuthentication?
|
||||||
if case let .enabled(data) = self.biometrics {
|
if case let .enabled(data) = self.biometrics {
|
||||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
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
|
biometricsType = LocalAuth.biometricAuthentication
|
||||||
} else {
|
} else {
|
||||||
biometricsType = nil
|
biometricsType = nil
|
||||||
@ -115,12 +122,11 @@ public final class PasscodeEntryController: ViewController {
|
|||||||
} else {
|
} else {
|
||||||
biometricsType = nil
|
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()
|
self.displayNodeDidLoad()
|
||||||
|
|
||||||
let _ = (self.context.sharedContext.accountManager.transaction({ transaction -> AccessChallengeAttempts? in
|
let _ = (self.appLockContext.invalidAttempts
|
||||||
return transaction.getAccessChallengeData().attempts
|
|> deliverOnMainQueue).start(next: { [weak self] attempts in
|
||||||
}) |> deliverOnMainQueue).start(next: { [weak self] attempts in
|
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -136,9 +142,9 @@ public final class PasscodeEntryController: ViewController {
|
|||||||
switch strongSelf.challengeData {
|
switch strongSelf.challengeData {
|
||||||
case .none:
|
case .none:
|
||||||
succeed = true
|
succeed = true
|
||||||
case let .numericalPassword(code, _, _):
|
case let .numericalPassword(code):
|
||||||
succeed = passcode == normalizeArabicNumeralString(code, type: .western)
|
succeed = passcode == normalizeArabicNumeralString(code, type: .western)
|
||||||
case let .plaintextPassword(code, _, _):
|
case let .plaintextPassword(code):
|
||||||
succeed = passcode == code
|
succeed = passcode == code
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,22 +152,11 @@ public final class PasscodeEntryController: ViewController {
|
|||||||
if let completed = strongSelf.completed {
|
if let completed = strongSelf.completed {
|
||||||
completed()
|
completed()
|
||||||
} else {
|
} else {
|
||||||
let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> Void in
|
strongSelf.appLockContext.unlock()
|
||||||
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()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let isMainApp = strongSelf.context.sharedContext.applicationBindings.isMainApp
|
let isMainApp = strongSelf.applicationBindings.isMainApp
|
||||||
let _ = updatePresentationPasscodeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { settings in
|
let _ = updatePresentationPasscodeSettingsInteractively(accountManager: strongSelf.accountManager, { settings in
|
||||||
if isMainApp {
|
if isMainApp {
|
||||||
return settings.withUpdatedBiometricsDomainState(LocalAuth.evaluatedPolicyDomainState)
|
return settings.withUpdatedBiometricsDomainState(LocalAuth.evaluatedPolicyDomainState)
|
||||||
} else {
|
} else {
|
||||||
@ -169,36 +164,7 @@ public final class PasscodeEntryController: ViewController {
|
|||||||
}
|
}
|
||||||
}).start()
|
}).start()
|
||||||
} else {
|
} else {
|
||||||
let _ = (strongSelf.context.sharedContext.accountManager.transaction({ transaction -> AccessChallengeAttempts in
|
strongSelf.appLockContext.failedUnlockAttempt()
|
||||||
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.controllerNode.animateError()
|
strongSelf.controllerNode.animateError()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -231,7 +197,7 @@ public final class PasscodeEntryController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||||
if data == nil && self.context.sharedContext.applicationBindings.isMainApp {
|
if data == nil && self.applicationBindings.isMainApp {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -258,8 +224,8 @@ public final class PasscodeEntryController: ViewController {
|
|||||||
|
|
||||||
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
if #available(iOSApplicationExtension 9.0, iOS 9.0, *) {
|
||||||
if case let .enabled(storedDomainState) = strongSelf.biometrics, evaluatedPolicyDomainState != nil {
|
if case let .enabled(storedDomainState) = strongSelf.biometrics, evaluatedPolicyDomainState != nil {
|
||||||
if !strongSelf.context.sharedContext.applicationBindings.isMainApp && storedDomainState == nil {
|
if !strongSelf.applicationBindings.isMainApp && storedDomainState == nil {
|
||||||
let _ = updatePresentationPasscodeSettingsInteractively(accountManager: strongSelf.context.sharedContext.accountManager, { settings in
|
let _ = updatePresentationPasscodeSettingsInteractively(accountManager: strongSelf.accountManager, { settings in
|
||||||
return settings.withUpdatedShareBiometricsDomainState(LocalAuth.evaluatedPolicyDomainState)
|
return settings.withUpdatedShareBiometricsDomainState(LocalAuth.evaluatedPolicyDomainState)
|
||||||
}).start()
|
}).start()
|
||||||
} else if storedDomainState != evaluatedPolicyDomainState {
|
} else if storedDomainState != evaluatedPolicyDomainState {
|
||||||
@ -278,15 +244,9 @@ public final class PasscodeEntryController: ViewController {
|
|||||||
}
|
}
|
||||||
strongSelf.hasOngoingBiometricsRequest = false
|
strongSelf.hasOngoingBiometricsRequest = false
|
||||||
} else {
|
} else {
|
||||||
let _ = (strongSelf.context.sharedContext.accountManager.transaction { transaction -> Void in
|
strongSelf.appLockContext.unlock()
|
||||||
let data = transaction.getAccessChallengeData().withUpdatedAutolockDeadline(nil)
|
|
||||||
transaction.setAccessChallengeData(data)
|
|
||||||
}).start(completed: { [weak self] in
|
|
||||||
if let strongSelf = self {
|
|
||||||
strongSelf.hasOngoingBiometricsRequest = false
|
strongSelf.hasOngoingBiometricsRequest = false
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
strongSelf.hasOngoingBiometricsRequest = false
|
strongSelf.hasOngoingBiometricsRequest = false
|
||||||
strongSelf.skipNextBiometricsRequest = true
|
strongSelf.skipNextBiometricsRequest = true
|
||||||
|
@ -17,7 +17,7 @@ private let subtitleFont = Font.regular(15.0)
|
|||||||
private let buttonFont = Font.regular(17.0)
|
private let buttonFont = Font.regular(17.0)
|
||||||
|
|
||||||
final class PasscodeEntryControllerNode: ASDisplayNode {
|
final class PasscodeEntryControllerNode: ASDisplayNode {
|
||||||
private let context: AccountContext
|
private let accountManager: AccountManager
|
||||||
private var theme: PresentationTheme
|
private var theme: PresentationTheme
|
||||||
private var strings: PresentationStrings
|
private var strings: PresentationStrings
|
||||||
private var wallpaper: TelegramWallpaper
|
private var wallpaper: TelegramWallpaper
|
||||||
@ -49,8 +49,8 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
|||||||
var checkPasscode: ((String) -> Void)?
|
var checkPasscode: ((String) -> Void)?
|
||||||
var requestBiometrics: (() -> Void)?
|
var requestBiometrics: (() -> Void)?
|
||||||
|
|
||||||
init(context: AccountContext, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, passcodeType: PasscodeEntryFieldType, biometricsType: LocalAuthBiometricAuthentication?, arguments: PasscodeEntryControllerPresentationArguments, statusBar: StatusBar) {
|
init(accountManager: AccountManager, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, passcodeType: PasscodeEntryFieldType, biometricsType: LocalAuthBiometricAuthentication?, arguments: PasscodeEntryControllerPresentationArguments, statusBar: StatusBar) {
|
||||||
self.context = context
|
self.accountManager = accountManager
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.wallpaper = wallpaper
|
self.wallpaper = wallpaper
|
||||||
@ -174,7 +174,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
switch self.wallpaper {
|
switch self.wallpaper {
|
||||||
case .image, .file:
|
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)
|
self.background = ImageBasedPasscodeBackground(image: image, size: size)
|
||||||
} else {
|
} else {
|
||||||
self.background = GradientPasscodeBackground(size: size, backgroundColors: self.theme.passcode.backgroundColors.colors, buttonColor: self.theme.passcode.buttonColor)
|
self.background = GradientPasscodeBackground(size: size, backgroundColors: self.theme.passcode.backgroundColors.colors, buttonColor: self.theme.passcode.buttonColor)
|
||||||
|
@ -75,7 +75,7 @@ final class PasscodeSetupControllerNode: ASDisplayNode {
|
|||||||
switch self.mode {
|
switch self.mode {
|
||||||
case let .entry(challenge):
|
case let .entry(challenge):
|
||||||
switch challenge {
|
switch challenge {
|
||||||
case let .numericalPassword(value, _, _):
|
case let .numericalPassword(value):
|
||||||
passcodeType = value.count == 6 ? .digits6 : .digits4
|
passcodeType = value.count == 6 ? .digits6 : .digits4
|
||||||
default:
|
default:
|
||||||
passcodeType = .alphanumeric
|
passcodeType = .alphanumeric
|
||||||
|
@ -1151,6 +1151,9 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
self.emptyQueryListNode.beganInteractiveDragging = { [weak self] in
|
||||||
|
self?.dismissInput?()
|
||||||
|
}
|
||||||
self.listNode.beganInteractiveDragging = { [weak self] in
|
self.listNode.beganInteractiveDragging = { [weak self] in
|
||||||
self?.dismissInput?()
|
self?.dismissInput?()
|
||||||
}
|
}
|
||||||
|
@ -22,17 +22,17 @@ public struct AccessChallengeAttempts: PostboxCoding, Equatable {
|
|||||||
|
|
||||||
public enum PostboxAccessChallengeData: PostboxCoding, Equatable {
|
public enum PostboxAccessChallengeData: PostboxCoding, Equatable {
|
||||||
case none
|
case none
|
||||||
case numericalPassword(value: String, timeout: Int32?, attempts: AccessChallengeAttempts?)
|
case numericalPassword(value: String)
|
||||||
case plaintextPassword(value: String, timeout: Int32?, attempts: AccessChallengeAttempts?)
|
case plaintextPassword(value: String)
|
||||||
|
|
||||||
public init(decoder: PostboxDecoder) {
|
public init(decoder: PostboxDecoder) {
|
||||||
switch decoder.decodeInt32ForKey("r", orElse: 0) {
|
switch decoder.decodeInt32ForKey("r", orElse: 0) {
|
||||||
case 0:
|
case 0:
|
||||||
self = .none
|
self = .none
|
||||||
case 1:
|
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:
|
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:
|
default:
|
||||||
assertionFailure()
|
assertionFailure()
|
||||||
self = .none
|
self = .none
|
||||||
@ -43,32 +43,12 @@ public enum PostboxAccessChallengeData: PostboxCoding, Equatable {
|
|||||||
switch self {
|
switch self {
|
||||||
case .none:
|
case .none:
|
||||||
encoder.encodeInt32(0, forKey: "r")
|
encoder.encodeInt32(0, forKey: "r")
|
||||||
case let .numericalPassword(text, timeout, attempts):
|
case let .numericalPassword(text):
|
||||||
encoder.encodeInt32(1, forKey: "r")
|
encoder.encodeInt32(1, forKey: "r")
|
||||||
encoder.encodeString(text, forKey: "t")
|
encoder.encodeString(text, forKey: "t")
|
||||||
if let timeout = timeout {
|
case let .plaintextPassword(text):
|
||||||
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):
|
|
||||||
encoder.encodeInt32(2, forKey: "r")
|
encoder.encodeInt32(2, forKey: "r")
|
||||||
encoder.encodeString(text, forKey: "t")
|
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
|
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 {
|
public struct AuthAccountRecord: PostboxCoding, Codable {
|
||||||
|
@ -254,9 +254,9 @@ func passcodeOptionsController(context: AccountContext) -> ViewController {
|
|||||||
let _ = (context.sharedContext.accountManager.transaction({ transaction -> Void in
|
let _ = (context.sharedContext.accountManager.transaction({ transaction -> Void in
|
||||||
var data = transaction.getAccessChallengeData()
|
var data = transaction.getAccessChallengeData()
|
||||||
if numerical {
|
if numerical {
|
||||||
data = PostboxAccessChallengeData.numericalPassword(value: passcode, timeout: data.autolockDeadline, attempts: nil)
|
data = PostboxAccessChallengeData.numericalPassword(value: passcode)
|
||||||
} else {
|
} else {
|
||||||
data = PostboxAccessChallengeData.plaintextPassword(value: passcode, timeout: data.autolockDeadline, attempts: nil)
|
data = PostboxAccessChallengeData.plaintextPassword(value: passcode)
|
||||||
}
|
}
|
||||||
transaction.setAccessChallengeData(data)
|
transaction.setAccessChallengeData(data)
|
||||||
|
|
||||||
@ -298,9 +298,9 @@ func passcodeOptionsController(context: AccountContext) -> ViewController {
|
|||||||
let _ = (context.sharedContext.accountManager.transaction({ transaction -> Void in
|
let _ = (context.sharedContext.accountManager.transaction({ transaction -> Void in
|
||||||
var data = transaction.getAccessChallengeData()
|
var data = transaction.getAccessChallengeData()
|
||||||
if numerical {
|
if numerical {
|
||||||
data = PostboxAccessChallengeData.numericalPassword(value: passcode, timeout: data.autolockDeadline, attempts: nil)
|
data = PostboxAccessChallengeData.numericalPassword(value: passcode)
|
||||||
} else {
|
} else {
|
||||||
data = PostboxAccessChallengeData.plaintextPassword(value: passcode, timeout: data.autolockDeadline, attempts: nil)
|
data = PostboxAccessChallengeData.plaintextPassword(value: passcode)
|
||||||
}
|
}
|
||||||
transaction.setAccessChallengeData(data)
|
transaction.setAccessChallengeData(data)
|
||||||
}) |> deliverOnMainQueue).start(next: { _ in
|
}) |> deliverOnMainQueue).start(next: { _ in
|
||||||
@ -403,9 +403,9 @@ public func passcodeOptionsAccessController(context: AccountContext, animateIn:
|
|||||||
let _ = (context.sharedContext.accountManager.transaction({ transaction -> Void in
|
let _ = (context.sharedContext.accountManager.transaction({ transaction -> Void in
|
||||||
var data = transaction.getAccessChallengeData()
|
var data = transaction.getAccessChallengeData()
|
||||||
if numerical {
|
if numerical {
|
||||||
data = PostboxAccessChallengeData.numericalPassword(value: passcode, timeout: data.autolockDeadline, attempts: nil)
|
data = PostboxAccessChallengeData.numericalPassword(value: passcode)
|
||||||
} else {
|
} else {
|
||||||
data = PostboxAccessChallengeData.plaintextPassword(value: passcode, timeout: data.autolockDeadline, attempts: nil)
|
data = PostboxAccessChallengeData.plaintextPassword(value: passcode)
|
||||||
}
|
}
|
||||||
transaction.setAccessChallengeData(data)
|
transaction.setAccessChallengeData(data)
|
||||||
|
|
||||||
@ -426,9 +426,9 @@ public func passcodeOptionsAccessController(context: AccountContext, animateIn:
|
|||||||
switch challenge {
|
switch challenge {
|
||||||
case .none:
|
case .none:
|
||||||
succeed = true
|
succeed = true
|
||||||
case let .numericalPassword(code, _, _):
|
case let .numericalPassword(code):
|
||||||
succeed = passcode == normalizeArabicNumeralString(code, type: .western)
|
succeed = passcode == normalizeArabicNumeralString(code, type: .western)
|
||||||
case let .plaintextPassword(code, _, _):
|
case let .plaintextPassword(code):
|
||||||
succeed = passcode == code
|
succeed = passcode == code
|
||||||
}
|
}
|
||||||
if succeed {
|
if succeed {
|
||||||
@ -463,7 +463,7 @@ public func passcodeEntryController(context: AccountContext, animateIn: Bool = t
|
|||||||
} else {
|
} else {
|
||||||
biometrics = .none
|
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)
|
completion(false)
|
||||||
}))
|
}))
|
||||||
controller.presentationCompleted = { [weak controller] in
|
controller.presentationCompleted = { [weak controller] in
|
||||||
|
@ -910,13 +910,7 @@ public class Account {
|
|||||||
}).start()
|
}).start()
|
||||||
self.notificationAutolockReportManager = NotificationAutolockReportManager(deadline: self.autolockReportDeadline.get(), network: network)
|
self.notificationAutolockReportManager = NotificationAutolockReportManager(deadline: self.autolockReportDeadline.get(), network: network)
|
||||||
self.autolockReportDeadline.set(
|
self.autolockReportDeadline.set(
|
||||||
accountManager.accessChallengeData()
|
networkArguments.autolockDeadine
|
||||||
|> map { dataView -> Int32? in
|
|
||||||
guard let autolockDeadline = dataView.data.autolockDeadline else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return autolockDeadline
|
|
||||||
}
|
|
||||||
|> distinctUntilChanged
|
|> distinctUntilChanged
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -410,14 +410,16 @@ public struct NetworkInitializationArguments {
|
|||||||
public let appVersion: String
|
public let appVersion: String
|
||||||
public let voipMaxLayer: Int32
|
public let voipMaxLayer: Int32
|
||||||
public let appData: Signal<Data?, NoError>
|
public let appData: Signal<Data?, NoError>
|
||||||
|
public let autolockDeadine: Signal<Int32?, NoError>
|
||||||
public let encryptionProvider: EncryptionProvider
|
public let encryptionProvider: EncryptionProvider
|
||||||
|
|
||||||
public init(apiId: Int32, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, appData: Signal<Data?, NoError>, encryptionProvider: EncryptionProvider) {
|
public init(apiId: Int32, languagesCategory: String, appVersion: String, voipMaxLayer: Int32, appData: Signal<Data?, NoError>, autolockDeadine: Signal<Int32?, NoError>, encryptionProvider: EncryptionProvider) {
|
||||||
self.apiId = apiId
|
self.apiId = apiId
|
||||||
self.languagesCategory = languagesCategory
|
self.languagesCategory = languagesCategory
|
||||||
self.appVersion = appVersion
|
self.appVersion = appVersion
|
||||||
self.voipMaxLayer = voipMaxLayer
|
self.voipMaxLayer = voipMaxLayer
|
||||||
self.appData = appData
|
self.appData = appData
|
||||||
|
self.autolockDeadine = autolockDeadine
|
||||||
self.encryptionProvider = encryptionProvider
|
self.encryptionProvider = encryptionProvider
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ import UrlHandling
|
|||||||
import WalletUrl
|
import WalletUrl
|
||||||
import WalletCore
|
import WalletCore
|
||||||
import OpenSSLEncryptionProvider
|
import OpenSSLEncryptionProvider
|
||||||
|
import AppLock
|
||||||
|
|
||||||
#if canImport(BackgroundTasks)
|
#if canImport(BackgroundTasks)
|
||||||
import BackgroundTasks
|
import BackgroundTasks
|
||||||
@ -362,7 +363,7 @@ final class SharedApplicationContext {
|
|||||||
Logger.shared.log("data", "can't deserialize")
|
Logger.shared.log("data", "can't deserialize")
|
||||||
}
|
}
|
||||||
return data
|
return data
|
||||||
}, encryptionProvider: OpenSSLEncryptionProvider())
|
}, autolockDeadine: .single(nil), encryptionProvider: OpenSSLEncryptionProvider())
|
||||||
|
|
||||||
guard let appGroupUrl = maybeAppGroupUrl else {
|
guard let appGroupUrl = maybeAppGroupUrl else {
|
||||||
UIAlertView(title: nil, message: "Error 2", delegate: nil, cancelButtonTitle: "OK").show()
|
UIAlertView(title: nil, message: "Error 2", delegate: nil, cancelButtonTitle: "OK").show()
|
||||||
@ -725,8 +726,13 @@ final class SharedApplicationContext {
|
|||||||
let legacyBasePath = appGroupUrl.path
|
let legacyBasePath = appGroupUrl.path
|
||||||
let legacyCache = LegacyCache(path: legacyBasePath + "/Caches")
|
let legacyCache = LegacyCache(path: legacyBasePath + "/Caches")
|
||||||
|
|
||||||
|
let presentationDataPromise = Promise<PresentationData>()
|
||||||
|
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)?
|
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)
|
setPresentationCall?(call)
|
||||||
}, navigateToChat: { accountId, peerId, messageId in
|
}, navigateToChat: { accountId, peerId, messageId in
|
||||||
self.openChatWhenReady(accountId: accountId, peerId: peerId, messageId: messageId)
|
self.openChatWhenReady(accountId: accountId, peerId: peerId, messageId: messageId)
|
||||||
@ -748,6 +754,8 @@ final class SharedApplicationContext {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
presentationDataPromise.set(sharedContext.presentationData)
|
||||||
|
|
||||||
let rawAccounts = sharedContext.activeAccounts
|
let rawAccounts = sharedContext.activeAccounts
|
||||||
|> map { _, accounts, _ -> [Account] in
|
|> map { _, accounts, _ -> [Account] in
|
||||||
return accounts.map({ $0.1 })
|
return accounts.map({ $0.1 })
|
||||||
|
@ -22,14 +22,6 @@ import ImageBlur
|
|||||||
import WatchBridge
|
import WatchBridge
|
||||||
import SettingsUI
|
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 {
|
final class UnauthorizedApplicationContext {
|
||||||
let sharedContext: SharedAccountContextImpl
|
let sharedContext: SharedAccountContextImpl
|
||||||
let account: UnauthorizedAccount
|
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 {
|
final class AuthorizedApplicationContext {
|
||||||
let sharedApplicationContext: SharedApplicationContext
|
let sharedApplicationContext: SharedApplicationContext
|
||||||
let mainWindow: Window1
|
let mainWindow: Window1
|
||||||
@ -166,7 +150,7 @@ final class AuthorizedApplicationContext {
|
|||||||
context.keyShortcutsController = keyShortcutsController
|
context.keyShortcutsController = keyShortcutsController
|
||||||
}
|
}
|
||||||
|
|
||||||
let previousPasscodeState = Atomic<PasscodeState?>(value: nil)
|
/*let previousPasscodeState = Atomic<PasscodeState?>(value: nil)
|
||||||
let passcodeStatusData = combineLatest(queue: Queue.mainQueue(), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]), context.sharedContext.accountManager.accessChallengeData(), context.sharedContext.applicationBindings.applicationIsActive)
|
let passcodeStatusData = combineLatest(queue: Queue.mainQueue(), context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationPasscodeSettings]), context.sharedContext.accountManager.accessChallengeData(), context.sharedContext.applicationBindings.applicationIsActive)
|
||||||
let passcodeState = passcodeStatusData
|
let passcodeState = passcodeStatusData
|
||||||
|> map { sharedData, accessChallengeDataView, isActive -> PasscodeState in
|
|> map { sharedData, accessChallengeDataView, isActive -> PasscodeState in
|
||||||
@ -331,7 +315,21 @@ final class AuthorizedApplicationContext {
|
|||||||
} else {
|
} else {
|
||||||
strongSelf.isReady.set(.single(true))
|
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
|
let accountId = context.account.id
|
||||||
self.loggedOutDisposable.set((context.account.loggedOut
|
self.loggedOutDisposable.set((context.account.loggedOut
|
||||||
|
@ -15,6 +15,7 @@ import PhotoResources
|
|||||||
import AnimatedStickerNode
|
import AnimatedStickerNode
|
||||||
import TelegramAnimatedStickerNode
|
import TelegramAnimatedStickerNode
|
||||||
import OpenSSLEncryptionProvider
|
import OpenSSLEncryptionProvider
|
||||||
|
import AppLock
|
||||||
|
|
||||||
private enum NotificationContentAuthorizationError {
|
private enum NotificationContentAuthorizationError {
|
||||||
case unauthorized
|
case unauthorized
|
||||||
@ -145,7 +146,15 @@ public final class NotificationViewControllerImpl {
|
|||||||
f(false)
|
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<PresentationData>()
|
||||||
|
|
||||||
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ import PeerInfoUI
|
|||||||
import ShareItems
|
import ShareItems
|
||||||
import SettingsUI
|
import SettingsUI
|
||||||
import OpenSSLEncryptionProvider
|
import OpenSSLEncryptionProvider
|
||||||
|
import AppLock
|
||||||
|
|
||||||
private let inForeground = ValuePromise<Bool>(false, ignoreRepeated: true)
|
private let inForeground = ValuePromise<Bool>(false, ignoreRepeated: true)
|
||||||
|
|
||||||
@ -173,7 +174,14 @@ public class ShareRootControllerImpl {
|
|||||||
})
|
})
|
||||||
semaphore.wait()
|
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<PresentationData>()
|
||||||
|
|
||||||
|
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)
|
internalContext = InternalContext(sharedContext: sharedContext)
|
||||||
globalInternalContext = internalContext
|
globalInternalContext = internalContext
|
||||||
}
|
}
|
||||||
|
@ -60,6 +60,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
public let applicationBindings: TelegramApplicationBindings
|
public let applicationBindings: TelegramApplicationBindings
|
||||||
public let basePath: String
|
public let basePath: String
|
||||||
public let accountManager: AccountManager
|
public let accountManager: AccountManager
|
||||||
|
public let appLockContext: AppLockContext
|
||||||
|
|
||||||
private let navigateToChatImpl: (AccountRecordId, PeerId, MessageId?) -> Void
|
private let navigateToChatImpl: (AccountRecordId, PeerId, MessageId?) -> Void
|
||||||
|
|
||||||
@ -146,7 +147,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
|
|
||||||
private var widgetDataContext: WidgetDataContext?
|
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<Data?, NoError>, voipNotificationToken: Signal<Data?, NoError>, 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<Data?, NoError>, voipNotificationToken: Signal<Data?, NoError>, setNotificationCall: @escaping (PresentationCall?) -> Void, navigateToChat: @escaping (AccountRecordId, PeerId, MessageId?) -> Void, displayUpgradeProgress: @escaping (Float?) -> Void = { _ in }) {
|
||||||
assert(Queue.mainQueue().isCurrent())
|
assert(Queue.mainQueue().isCurrent())
|
||||||
|
|
||||||
precondition(!testHasInstance)
|
precondition(!testHasInstance)
|
||||||
@ -158,6 +159,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
self.accountManager = accountManager
|
self.accountManager = accountManager
|
||||||
self.navigateToChatImpl = navigateToChat
|
self.navigateToChatImpl = navigateToChat
|
||||||
self.displayUpgradeProgress = displayUpgradeProgress
|
self.displayUpgradeProgress = displayUpgradeProgress
|
||||||
|
self.appLockContext = appLockContext
|
||||||
|
|
||||||
self.accountManager.mediaBox.fetchCachedResourceRepresentation = { (resource, representation) -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
self.accountManager.mediaBox.fetchCachedResourceRepresentation = { (resource, representation) -> Signal<CachedMediaResourceRepresentationResult, NoError> in
|
||||||
return fetchCachedSharedResourceRepresentation(accountManager: accountManager, resource: resource, representation: representation)
|
return fetchCachedSharedResourceRepresentation(accountManager: accountManager, resource: resource, representation: representation)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user