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 = [
|
||||
"//submodules/BuildConfig:BuildConfig",
|
||||
"//submodules/WidgetItems:WidgetItems",
|
||||
"//submodules/AppLockState:AppLockState",
|
||||
],
|
||||
frameworks = [
|
||||
"$SDKROOT/System/Library/Frameworks/UIKit.framework",
|
||||
|
@ -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<Account?, NoError> in
|
||||
if let account = account {
|
||||
switch account {
|
||||
|
@ -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] = []
|
||||
|
@ -384,10 +384,19 @@ public enum CreateGroupMode {
|
||||
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 {
|
||||
var basePath: String { get }
|
||||
var mainWindow: Window1? { get }
|
||||
var accountManager: AccountManager { get }
|
||||
var appLockContext: AppLockContext { get }
|
||||
|
||||
var currentPresentationData: Atomic<PresentationData> { 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()
|
||||
|> 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()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
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
|
||||
}
|
||||
|
||||
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 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<PresentationData, NoError>, 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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -1151,6 +1151,9 @@ final class ChannelMembersSearchContainerNode: SearchDisplayControllerContentNod
|
||||
}
|
||||
})
|
||||
|
||||
self.emptyQueryListNode.beganInteractiveDragging = { [weak self] in
|
||||
self?.dismissInput?()
|
||||
}
|
||||
self.listNode.beganInteractiveDragging = { [weak self] in
|
||||
self?.dismissInput?()
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
||||
|
||||
|
@ -410,14 +410,16 @@ public struct NetworkInitializationArguments {
|
||||
public let appVersion: String
|
||||
public let voipMaxLayer: Int32
|
||||
public let appData: Signal<Data?, NoError>
|
||||
public let autolockDeadine: Signal<Int32?, NoError>
|
||||
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.languagesCategory = languagesCategory
|
||||
self.appVersion = appVersion
|
||||
self.voipMaxLayer = voipMaxLayer
|
||||
self.appData = appData
|
||||
self.autolockDeadine = autolockDeadine
|
||||
self.encryptionProvider = encryptionProvider
|
||||
}
|
||||
}
|
||||
|
@ -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<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)?
|
||||
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 })
|
||||
|
@ -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<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 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
|
||||
|
@ -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<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 SettingsUI
|
||||
import OpenSSLEncryptionProvider
|
||||
import AppLock
|
||||
|
||||
private let inForeground = ValuePromise<Bool>(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<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)
|
||||
globalInternalContext = internalContext
|
||||
}
|
||||
|
@ -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<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())
|
||||
|
||||
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<CachedMediaResourceRepresentationResult, NoError> in
|
||||
return fetchCachedSharedResourceRepresentation(accountManager: accountManager, resource: resource, representation: representation)
|
||||
|
Loading…
x
Reference in New Issue
Block a user