diff --git a/submodules/AppLock/Sources/AppLock.swift b/submodules/AppLock/Sources/AppLock.swift index b771fc82de..993663bce7 100644 --- a/submodules/AppLock/Sources/AppLock.swift +++ b/submodules/AppLock/Sources/AppLock.swift @@ -184,7 +184,7 @@ public final class AppLockContextImpl: AppLockContext { } passcodeController.ensureInputFocused() } else { - let passcodeController = PasscodeEntryController(applicationBindings: strongSelf.applicationBindings, accountManager: strongSelf.accountManager, appLockContext: strongSelf, presentationData: presentationData, presentationDataSignal: strongSelf.presentationDataSignal, challengeData: accessChallengeData.data, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: !becameActiveRecently, lockIconInitialFrame: { [weak self] in + let passcodeController = PasscodeEntryController(applicationBindings: strongSelf.applicationBindings, accountManager: strongSelf.accountManager, appLockContext: strongSelf, presentationData: presentationData, presentationDataSignal: strongSelf.presentationDataSignal, statusBarHost: window?.statusBarHost, challengeData: accessChallengeData.data, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: !becameActiveRecently, lockIconInitialFrame: { [weak self] in if let lockViewFrame = lockIconInitialFrame() { return lockViewFrame } else { diff --git a/submodules/Display/Source/WindowContent.swift b/submodules/Display/Source/WindowContent.swift index 23b2b84cb5..5694a5d375 100644 --- a/submodules/Display/Source/WindowContent.swift +++ b/submodules/Display/Source/WindowContent.swift @@ -237,7 +237,7 @@ public class Window1 { private var deviceMetrics: DeviceMetrics - private let statusBarHost: StatusBarHost? + public let statusBarHost: StatusBarHost? private let keyboardManager: KeyboardManager? private let keyboardViewManager: KeyboardViewManager? private var statusBarChangeObserver: AnyObject? diff --git a/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift b/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift index df02af0f17..5635a76076 100644 --- a/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift +++ b/submodules/GradientBackground/Sources/SoftwareGradientBackground.swift @@ -219,16 +219,6 @@ public final class GradientBackgroundNode: ASDisplayNode { } private var validLayout: CGSize? - - private struct PhaseTransitionKey: Hashable { - var width: Int - var height: Int - var fromPhase: Int - var toPhase: Int - var numberOfFrames: Int - var curve: ContainedViewLayoutTransitionCurve - } - private let cloneNodes = SparseBag>() private let useSharedAnimationPhase: Bool @@ -259,7 +249,7 @@ public final class GradientBackgroundNode: ASDisplayNode { deinit { } - public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, extendAnimation: Bool = false) { + public func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition, extendAnimation: Bool = false, backwards: Bool = false) { let sizeUpdated = self.validLayout != size self.validLayout = size @@ -273,7 +263,20 @@ public final class GradientBackgroundNode: ASDisplayNode { self.invalidated = false var steps: [[CGPoint]] = [] - if extendAnimation { + if backwards { + let phaseCount = extendAnimation ? 4 : 1 + self.phase = (self.phase + phaseCount) % 8 + self.validPhase = self.phase + + var stepPhase = self.phase - phaseCount + if stepPhase < 0 { + stepPhase = 7 + } + for _ in 0 ... phaseCount { + steps.append(gatherPositions(shiftArray(array: GradientBackgroundNode.basePositions, offset: stepPhase))) + stepPhase = (stepPhase + 1) % 8 + } + } else if extendAnimation { let phaseCount = 4 var stepPhase = (self.phase + phaseCount) % 8 for _ in 0 ... phaseCount { @@ -332,13 +335,13 @@ public final class GradientBackgroundNode: ASDisplayNode { let animation = CAKeyframeAnimation(keyPath: "contents") animation.values = images.map { $0.cgImage! } animation.duration = duration * UIView.animationDurationFactor() - if extendAnimation { + if backwards || extendAnimation { animation.calculationMode = .discrete } else { animation.calculationMode = .linear } animation.isRemovedOnCompletion = true - if extendAnimation { + if extendAnimation && !backwards { animation.fillMode = .backwards animation.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25 } @@ -347,19 +350,13 @@ public final class GradientBackgroundNode: ASDisplayNode { self.contentView.layer.add(animation, forKey: "contents") if !self.cloneNodes.isEmpty { - let animation = CAKeyframeAnimation(keyPath: "contents") - animation.values = dimmedImages.map { $0.cgImage! } - animation.duration = duration * UIView.animationDurationFactor() - if extendAnimation { - animation.calculationMode = .discrete - } else { - animation.calculationMode = .linear - } - animation.isRemovedOnCompletion = true - if extendAnimation { - animation.fillMode = .backwards - animation.beginTime = self.contentView.layer.convertTime(CACurrentMediaTime(), from: nil) + 0.25 - } + let cloneAnimation = CAKeyframeAnimation(keyPath: "contents") + cloneAnimation.values = dimmedImages.map { $0.cgImage! } + cloneAnimation.duration = animation.duration + cloneAnimation.calculationMode = animation.calculationMode + cloneAnimation.isRemovedOnCompletion = animation.isRemovedOnCompletion + cloneAnimation.fillMode = animation.fillMode + cloneAnimation.beginTime = animation.beginTime self._dimmedImage = dimmedImages.last @@ -367,7 +364,7 @@ public final class GradientBackgroundNode: ASDisplayNode { if let value = cloneNode.value { value.image = dimmedImages.last value.layer.removeAnimation(forKey: "contents") - value.layer.add(animation, forKey: "contents") + value.layer.add(cloneAnimation, forKey: "contents") } } } @@ -422,12 +419,12 @@ public final class GradientBackgroundNode: ASDisplayNode { } } - public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool = false) { + public func animateEvent(transition: ContainedViewLayoutTransition, extendAnimation: Bool = false, backwards: Bool = false) { guard case let .animated(duration, _) = transition, duration > 0.001 else { return } - if extendAnimation { + if extendAnimation || backwards { self.invalidated = true } else { if self.phase == 0 { @@ -440,7 +437,7 @@ public final class GradientBackgroundNode: ASDisplayNode { GradientBackgroundNode.sharedPhase = self.phase } if let size = self.validLayout { - self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation) + self.updateLayout(size: size, transition: transition, extendAnimation: extendAnimation, backwards: backwards) } } } diff --git a/submodules/PasscodeUI/BUILD b/submodules/PasscodeUI/BUILD index 60825d4832..cadf3b48ea 100644 --- a/submodules/PasscodeUI/BUILD +++ b/submodules/PasscodeUI/BUILD @@ -23,6 +23,7 @@ swift_library( "//submodules/AppBundle:AppBundle", "//submodules/PasscodeInputFieldNode:PasscodeInputFieldNode", "//submodules/MonotonicTime:MonotonicTime", + "//submodules/GradientBackground:GradientBackground", ], visibility = [ "//visibility:public", diff --git a/submodules/PasscodeUI/Sources/PasscodeBackground.swift b/submodules/PasscodeUI/Sources/PasscodeBackground.swift index 1845af212d..fbe8da29c6 100644 --- a/submodules/PasscodeUI/Sources/PasscodeBackground.swift +++ b/submodules/PasscodeUI/Sources/PasscodeBackground.swift @@ -1,19 +1,40 @@ import Foundation import UIKit +import AsyncDisplayKit import Display import ImageBlur import FastBlur +import GradientBackground protocol PasscodeBackground { var size: CGSize { get } - var backgroundImage: UIImage { get } - var foregroundImage: UIImage { get } + var backgroundImage: UIImage? { get } + var foregroundImage: UIImage? { get } + + func makeBackgroundNode() -> ASDisplayNode? +} + +final class CustomPasscodeBackground: PasscodeBackground { + private let colors: [UIColor] + + public private(set) var size: CGSize + public private(set) var backgroundImage: UIImage? = nil + public private(set) var foregroundImage: UIImage? = nil + + init(size: CGSize, colors: [UIColor]) { + self.size = size + self.colors = colors + } + + func makeBackgroundNode() -> ASDisplayNode? { + return createGradientBackgroundNode(colors: self.colors) + } } final class GradientPasscodeBackground: PasscodeBackground { public private(set) var size: CGSize - public private(set) var backgroundImage: UIImage - public private(set) var foregroundImage: UIImage + public private(set) var backgroundImage: UIImage? + public private(set) var foregroundImage: UIImage? init(size: CGSize, backgroundColors: (UIColor, UIColor), buttonColor: UIColor) { self.size = size @@ -35,12 +56,16 @@ final class GradientPasscodeBackground: PasscodeBackground { context.fill(bounds) })! } + + func makeBackgroundNode() -> ASDisplayNode? { + return nil + } } final class ImageBasedPasscodeBackground: PasscodeBackground { public private(set) var size: CGSize - public private(set) var backgroundImage: UIImage - public private(set) var foregroundImage: UIImage + public private(set) var backgroundImage: UIImage? + public private(set) var foregroundImage: UIImage? init(image: UIImage, size: CGSize) { self.size = size @@ -82,4 +107,8 @@ final class ImageBasedPasscodeBackground: PasscodeBackground { } self.backgroundImage = backgroundContext.generateImage()! } + + func makeBackgroundNode() -> ASDisplayNode? { + return nil + } } diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryController.swift b/submodules/PasscodeUI/Sources/PasscodeEntryController.swift index 7c61f2a950..78a2f2c8b6 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryController.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryController.swift @@ -58,7 +58,10 @@ public final class PasscodeEntryController: ViewController { private var inBackground: Bool = false private var inBackgroundDisposable: Disposable? - public init(applicationBindings: TelegramApplicationBindings, accountManager: AccountManager, appLockContext: AppLockContext, presentationData: PresentationData, presentationDataSignal: Signal, challengeData: PostboxAccessChallengeData, biometrics: PasscodeEntryControllerBiometricsMode, arguments: PasscodeEntryControllerPresentationArguments) { + private let statusBarHost: StatusBarHost? + private var previousStatusBarStyle: UIStatusBarStyle? + + public init(applicationBindings: TelegramApplicationBindings, accountManager: AccountManager, appLockContext: AppLockContext, presentationData: PresentationData, presentationDataSignal: Signal, statusBarHost: StatusBarHost?, challengeData: PostboxAccessChallengeData, biometrics: PasscodeEntryControllerBiometricsMode, arguments: PasscodeEntryControllerPresentationArguments) { self.applicationBindings = applicationBindings self.accountManager = accountManager self.appLockContext = appLockContext @@ -68,10 +71,12 @@ public final class PasscodeEntryController: ViewController { self.biometrics = biometrics self.arguments = arguments + self.statusBarHost = statusBarHost + self.previousStatusBarStyle = statusBarHost?.statusBarStyle super.init(navigationBarPresentationData: nil) self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) - self.statusBar.statusBarStyle = .White + statusBarHost?.setStatusBarStyle(.lightContent, animated: true) self.presentationDataDisposable = (presentationDataSignal |> deliverOnMainQueue).start(next: { [weak self] presentationData in @@ -128,7 +133,7 @@ public final class PasscodeEntryController: ViewController { } else { biometricsType = nil } - 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, modalPresentation: self.arguments.modalPresentation) + self.displayNode = PasscodeEntryControllerNode(accountManager: self.accountManager, presentationData: self.presentationData, theme: self.presentationData.theme, strings: self.presentationData.strings, wallpaper: self.presentationData.chatWallpaper, passcodeType: passcodeType, biometricsType: biometricsType, arguments: self.arguments, modalPresentation: self.arguments.modalPresentation) self.displayNodeDidLoad() let _ = (self.appLockContext.invalidAttempts @@ -271,6 +276,9 @@ public final class PasscodeEntryController: ViewController { } public override func dismiss(completion: (() -> Void)? = nil) { + if let statusBarHost = self.statusBarHost, let previousStatusBarStyle = self.previousStatusBarStyle { + statusBarHost.setStatusBarStyle(previousStatusBarStyle, animated: true) + } self.view.endEditing(true) self.controllerNode.animateOut { [weak self] in guard let strongSelf = self else { diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift index 6272a4c17b..851a02b094 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryControllerNode.swift @@ -12,6 +12,7 @@ import LocalAuth import AppBundle import PasscodeInputFieldNode import MonotonicTime +import GradientBackground private let titleFont = Font.regular(20.0) private let subtitleFont = Font.regular(15.0) @@ -19,6 +20,7 @@ private let buttonFont = Font.regular(17.0) final class PasscodeEntryControllerNode: ASDisplayNode { private let accountManager: AccountManager + private var presentationData: PresentationData private var theme: PresentationTheme private var strings: PresentationStrings private var wallpaper: TelegramWallpaper @@ -27,11 +29,11 @@ final class PasscodeEntryControllerNode: ASDisplayNode { private let arguments: PasscodeEntryControllerPresentationArguments private var background: PasscodeBackground? - private let statusBar: StatusBar - private let modalPresentation: Bool - private let backgroundNode: ASImageNode + private var backgroundCustomNode: ASDisplayNode? + private let backgroundDimNode: ASDisplayNode + private let backgroundImageNode: ASImageNode private let iconNode: PasscodeLockIconNode private let titleNode: PasscodeEntryLabelNode private let inputFieldNode: PasscodeInputFieldNode @@ -52,20 +54,24 @@ final class PasscodeEntryControllerNode: ASDisplayNode { var checkPasscode: ((String) -> Void)? var requestBiometrics: (() -> Void)? - init(accountManager: AccountManager, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, passcodeType: PasscodeEntryFieldType, biometricsType: LocalAuthBiometricAuthentication?, arguments: PasscodeEntryControllerPresentationArguments, statusBar: StatusBar, modalPresentation: Bool) { + init(accountManager: AccountManager, presentationData: PresentationData, theme: PresentationTheme, strings: PresentationStrings, wallpaper: TelegramWallpaper, passcodeType: PasscodeEntryFieldType, biometricsType: LocalAuthBiometricAuthentication?, arguments: PasscodeEntryControllerPresentationArguments, modalPresentation: Bool) { self.accountManager = accountManager + self.presentationData = presentationData self.theme = theme self.strings = strings self.wallpaper = wallpaper self.passcodeType = passcodeType self.biometricsType = biometricsType self.arguments = arguments - self.statusBar = statusBar self.modalPresentation = modalPresentation - self.backgroundNode = ASImageNode() - self.backgroundNode.contentMode = .scaleToFill + self.backgroundImageNode = ASImageNode() + self.backgroundImageNode.contentMode = .scaleToFill + self.backgroundDimNode = ASDisplayNode() + self.backgroundDimNode.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.15) + self.backgroundDimNode.isHidden = true + self.iconNode = PasscodeLockIconNode() self.titleNode = PasscodeEntryLabelNode() self.inputFieldNode = PasscodeInputFieldNode(color: .white, accentColor: .white, fieldType: passcodeType, keyboardAppearance: .dark, useCustomNumpad: true) @@ -86,7 +92,12 @@ final class PasscodeEntryControllerNode: ASDisplayNode { self.iconNode.unlockedColor = theme.rootController.navigationBar.primaryTextColor self.keyboardNode.charactedEntered = { [weak self] character in - self?.inputFieldNode.append(character) + if let strongSelf = self { + strongSelf.inputFieldNode.append(character) + if let gradientNode = strongSelf.backgroundCustomNode as? GradientBackgroundNode { + gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring)) + } + } } self.inputFieldNode.complete = { [weak self] passcode in guard let strongSelf = self else { @@ -111,7 +122,8 @@ final class PasscodeEntryControllerNode: ASDisplayNode { } } - self.addSubnode(self.backgroundNode) + self.addSubnode(self.backgroundImageNode) + self.addSubnode(self.backgroundDimNode) self.addSubnode(self.iconNode) self.addSubnode(self.titleNode) self.addSubnode(self.inputFieldNode) @@ -146,7 +158,10 @@ final class PasscodeEntryControllerNode: ASDisplayNode { @objc private func deletePressed() { self.hapticFeedback.tap() - self.inputFieldNode.delete() + let result = self.inputFieldNode.delete() + if result, let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { + gradientNode.animateEvent(transition: .animated(duration: 0.55, curve: .spring), backwards: true) + } } @objc private func biometricsPressed() { @@ -158,6 +173,7 @@ final class PasscodeEntryControllerNode: ASDisplayNode { } func updatePresentationData(_ presentationData: PresentationData) { + self.presentationData = presentationData self.theme = presentationData.theme self.strings = presentationData.strings self.wallpaper = presentationData.chatWallpaper @@ -173,26 +189,42 @@ final class PasscodeEntryControllerNode: ASDisplayNode { return } - var size = validLayout.size + let size = validLayout.size if let background = self.background, background.size == size { return } switch self.wallpaper { + case let .gradient(_, colors, _): + self.background = CustomPasscodeBackground(size: size, colors: colors.compactMap { UIColor(rgb: $0) }) case .image, .file: 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) + if case let .file(file) = self.wallpaper, !file.settings.colors.isEmpty { + self.background = CustomPasscodeBackground(size: size, colors: file.settings.colors.compactMap { UIColor(rgb: $0) }) + } else { + self.background = GradientPasscodeBackground(size: size, backgroundColors: self.theme.passcode.backgroundColors.colors, buttonColor: self.theme.passcode.buttonColor) + } } default: self.background = GradientPasscodeBackground(size: size, backgroundColors: self.theme.passcode.backgroundColors.colors, buttonColor: self.theme.passcode.buttonColor) } if let background = self.background { - self.backgroundNode.image = background.backgroundImage - self.keyboardNode.updateBackground(background) - self.inputFieldNode.updateBackground(background.foregroundImage, size: background.size) + self.backgroundCustomNode?.removeFromSupernode() + self.backgroundCustomNode = nil + + if let backgroundImage = background.backgroundImage { + self.backgroundImageNode.image = backgroundImage + self.backgroundDimNode.isHidden = true + } else if let customBackgroundNode = background.makeBackgroundNode() { + self.backgroundCustomNode = customBackgroundNode + self.insertSubnode(customBackgroundNode, aboveSubnode: self.backgroundImageNode) + self.backgroundDimNode.isHidden = false + } + self.keyboardNode.updateBackground(self.presentationData, background) + self.inputFieldNode.updateBackground(background) } } @@ -263,7 +295,12 @@ final class PasscodeEntryControllerNode: ASDisplayNode { self.effectView.alpha = 1.0 } }) - self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + self.backgroundImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { + gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring), extendAnimation: true) + } } self.titleNode.setAttributedText(NSAttributedString(string: self.strings.EnterPasscode_EnterPasscode, font: titleFont, textColor: .white), animation: .none) } @@ -277,15 +314,17 @@ final class PasscodeEntryControllerNode: ASDisplayNode { self.effectView.alpha = 1.0 } }) - self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + self.backgroundImageNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { + gradientNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + gradientNode.animateEvent(transition: .animated(duration: 0.35, curve: .spring)) + self.backgroundDimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) + } if !iconFrame.isEmpty { self.iconNode.animateIn(fromScale: 0.416) self.iconNode.layer.animatePosition(from: iconFrame.center.offsetBy(dx: 6.0, dy: 6.0), to: self.iconNode.layer.position, duration: 0.45) } - self.statusBar.layer.removeAnimation(forKey: "opacity") - self.statusBar.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3) - self.subtitleNode.isHidden = true self.inputFieldNode.isHidden = true self.keyboardNode.isHidden = true @@ -303,6 +342,9 @@ final class PasscodeEntryControllerNode: ASDisplayNode { self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { + gradientNode.animateEvent(transition: .animated(duration: 1.0, curve: .spring)) + } self.inputFieldNode.animateIn() self.keyboardNode.animateIn() var biometricDelay = 0.3 @@ -323,7 +365,6 @@ final class PasscodeEntryControllerNode: ASDisplayNode { } func animateOut(down: Bool = false, completion: @escaping () -> Void = {}) { - self.statusBar.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false) self.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: down ? self.bounds.size.height : -self.bounds.size.height), duration: 0.2, removeOnCompletion: false, additive: true, completion: { _ in completion() }) @@ -340,6 +381,10 @@ final class PasscodeEntryControllerNode: ASDisplayNode { self.iconNode.layer.addShakeAnimation(amplitude: -8.0, duration: 0.5, count: 6, decay: true) self.hapticFeedback.error() + + if let gradientNode = self.backgroundCustomNode as? GradientBackgroundNode { + gradientNode.animateEvent(transition: .animated(duration: 1.5, curve: .spring), extendAnimation: true, backwards: true) + } } func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { @@ -348,7 +393,14 @@ final class PasscodeEntryControllerNode: ASDisplayNode { self.updateBackground() let bounds = CGRect(origin: CGPoint(), size: layout.size) - transition.updateFrame(node: self.backgroundNode, frame: bounds) + transition.updateFrame(node: self.backgroundImageNode, frame: bounds) + transition.updateFrame(node: self.backgroundDimNode, frame: bounds) + if let backgroundCustomNode = self.backgroundCustomNode { + transition.updateFrame(node: backgroundCustomNode, frame: bounds) + if let gradientBackgroundNode = backgroundCustomNode as? GradientBackgroundNode { + gradientBackgroundNode.updateLayout(size: bounds.size, transition: transition) + } + } transition.updateFrame(view: self.effectView, frame: bounds) switch self.passcodeType { diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift b/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift index 0154e44451..2cd4b4f8fc 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift @@ -3,6 +3,7 @@ import UIKit import Display import AsyncDisplayKit import SwiftSignalKit +import TelegramPresentationData private let regularTitleFont = Font.regular(36.0) private let regularSubtitleFont: UIFont = { @@ -35,8 +36,9 @@ private func generateButtonImage(background: PasscodeBackground, frame: CGRect, context.clip() context.setAlpha(0.8) - context.draw(background.foregroundImage.cgImage!, in: relativeFrame) - + if let foregroundImage = background.foregroundImage { + context.draw(foregroundImage.cgImage!, in: relativeFrame) + } if highlighted { context.setFillColor(UIColor(white: 1.0, alpha: 0.65).cgColor) context.fillEllipse(in: bounds) @@ -98,6 +100,7 @@ private func generateButtonImage(background: PasscodeBackground, frame: CGRect, } final class PasscodeEntryButtonNode: HighlightTrackingButtonNode { + private var presentationData: PresentationData private var background: PasscodeBackground let title: String private let subtitle: String @@ -106,15 +109,24 @@ final class PasscodeEntryButtonNode: HighlightTrackingButtonNode { private var regularImage: UIImage? private var highlightedImage: UIImage? + private var blurredBackgroundNode: NavigationBackgroundNode? private let backgroundNode: ASImageNode var action: (() -> Void)? - init(background: PasscodeBackground, title: String, subtitle: String) { + init(presentationData: PresentationData, background: PasscodeBackground, title: String, subtitle: String) { + self.presentationData = presentationData self.background = background self.title = title self.subtitle = subtitle + if background is CustomPasscodeBackground { + let blurredBackgroundColor = (selectDateFillStaticColor(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper), dateFillNeedsBlur(theme: presentationData.theme, wallpaper: presentationData.chatWallpaper)) + + let blurredBackgroundNode = NavigationBackgroundNode(color: blurredBackgroundColor.0, enableBlur: blurredBackgroundColor.1) + self.blurredBackgroundNode = blurredBackgroundNode + } + self.backgroundNode = ASImageNode() self.backgroundNode.displaysAsynchronously = false self.backgroundNode.displayWithoutProcessing = true @@ -122,6 +134,9 @@ final class PasscodeEntryButtonNode: HighlightTrackingButtonNode { super.init() + if let blurredBackgroundNode = self.blurredBackgroundNode { + self.addSubnode(blurredBackgroundNode) + } self.addSubnode(self.backgroundNode) self.highligthedChanged = { [weak self] highlighted in @@ -146,7 +161,8 @@ final class PasscodeEntryButtonNode: HighlightTrackingButtonNode { } } - func updateBackground(_ background: PasscodeBackground) { + func updateBackground(_ presentationData: PresentationData, _ background: PasscodeBackground) { + self.presentationData = presentationData self.background = background self.updateGraphics() } @@ -175,6 +191,10 @@ final class PasscodeEntryButtonNode: HighlightTrackingButtonNode { override func layout() { super.layout() + if let blurredBackgroundNode = self.blurredBackgroundNode { + blurredBackgroundNode.frame = self.bounds + blurredBackgroundNode.update(size: blurredBackgroundNode.bounds.size, cornerRadius: blurredBackgroundNode.bounds.height / 2.0, transition: .immediate) + } self.backgroundNode.frame = self.bounds } @@ -199,22 +219,23 @@ private let buttonsData = [ ] final class PasscodeEntryKeyboardNode: ASDisplayNode { + private var presentationData: PresentationData? private var background: PasscodeBackground? var charactedEntered: ((String) -> Void)? private func updateButtons() { - guard let background = self.background else { + guard let presentationData = self.presentationData, let background = self.background else { return } if let subnodes = self.subnodes, !subnodes.isEmpty { for case let button as PasscodeEntryButtonNode in subnodes { - button.updateBackground(background) + button.updateBackground(presentationData, background) } } else { for (title, subtitle) in buttonsData { - let buttonNode = PasscodeEntryButtonNode(background: background, title: title, subtitle: subtitle) + let buttonNode = PasscodeEntryButtonNode(presentationData: presentationData, background: background, title: title, subtitle: subtitle) buttonNode.action = { [weak self] in self?.charactedEntered?(title) } @@ -223,7 +244,8 @@ final class PasscodeEntryKeyboardNode: ASDisplayNode { } } - func updateBackground(_ background: PasscodeBackground) { + func updateBackground(_ presentationData: PresentationData, _ background: PasscodeBackground) { + self.presentationData = presentationData self.background = background self.updateButtons() } diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryLabelNode.swift b/submodules/PasscodeUI/Sources/PasscodeEntryLabelNode.swift index 51db01bf6e..d5b1783263 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryLabelNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryLabelNode.swift @@ -22,6 +22,7 @@ final class PasscodeEntryLabelNode: ASDisplayNode { self.textNode = ASTextNode() self.textNode.isLayerBacked = false self.textNode.textAlignment = .center + self.textNode.displaysAsynchronously = false super.init() diff --git a/submodules/PasscodeUI/Sources/PasscodeInputFieldNode.swift b/submodules/PasscodeUI/Sources/PasscodeInputFieldNode.swift index dd2ce400ac..f6ea4d47e5 100644 --- a/submodules/PasscodeUI/Sources/PasscodeInputFieldNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeInputFieldNode.swift @@ -24,20 +24,23 @@ private func generateDotImage(color: UIColor, filled: Bool) -> UIImage? { }) } -private func generateFieldBackgroundImage(backgroundImage: UIImage, backgroundSize: CGSize, frame: CGRect) -> UIImage? { +private func generateFieldBackgroundImage(backgroundImage: UIImage?, backgroundSize: CGSize?, frame: CGRect) -> UIImage? { return generateImage(frame.size, contextGenerator: { size, context in let bounds = CGRect(origin: CGPoint(), size: size) context.clear(bounds) - let relativeFrame = CGRect(x: -frame.minX, y: frame.minY - backgroundSize.height + frame.size.height - , width: backgroundSize.width, height: backgroundSize.height) - let path = UIBezierPath(roundedRect: CGRect(x: 0.0, y: 0.0, width: size.width, height: size.height), cornerRadius: 6.0) context.addPath(path.cgPath) context.clip() - context.draw(backgroundImage.cgImage!, in: relativeFrame) - + if let backgroundImage = backgroundImage, let backgroundSize = backgroundSize { + let relativeFrame = CGRect(x: -frame.minX, y: frame.minY - backgroundSize.height + frame.size.height + , width: backgroundSize.width, height: backgroundSize.height) + context.draw(backgroundImage.cgImage!, in: relativeFrame) + } else { + context.setFillColor(UIColor(rgb: 0xffffff, alpha: 1.0).cgColor) + context.fill(bounds) + } context.setBlendMode(.clear) context.setFillColor(UIColor.clear.cgColor) @@ -129,7 +132,7 @@ private class PasscodeEntryDotNode: ASImageNode { } public final class PasscodeInputFieldNode: ASDisplayNode, UITextFieldDelegate { - private var background: (UIImage, CGSize)? + private var background: PasscodeBackground? private var color: UIColor private var accentColor: UIColor private var fieldType: PasscodeEntryFieldType @@ -207,8 +210,8 @@ public final class PasscodeInputFieldNode: ASDisplayNode, UITextFieldDelegate { } } - func updateBackground(_ image: UIImage, size: CGSize) { - self.background = (image, size) + func updateBackground(_ background: PasscodeBackground) { + self.background = background if let (size, topOffset) = self.validLayout { let _ = self.updateLayout(size: size, topOffset: topOffset, transition: .immediate) } @@ -276,14 +279,15 @@ public final class PasscodeInputFieldNode: ASDisplayNode, UITextFieldDelegate { } } - func delete() { + func delete() -> Bool { var text = self.textFieldNode.textField.text ?? "" guard !text.isEmpty else { - return + return false } text = String(text[text.startIndex ..< text.index(text.endIndex, offsetBy: -1)]) self.textFieldNode.textField.text = text self.updateDots(count: text.count, animated: true) + return true } func updateDots(count: Int, animated: Bool) { @@ -346,9 +350,8 @@ public final class PasscodeInputFieldNode: ASDisplayNode, UITextFieldDelegate { let fieldFrame = CGRect(x: inset, y: origin.y, width: size.width - inset * 2.0, height: fieldHeight) transition.updateFrame(node: self.borderNode, frame: fieldFrame) transition.updateFrame(node: self.textFieldNode, frame: fieldFrame.insetBy(dx: 13.0, dy: 0.0)) - if let (backgroundImage, backgroundSize) = self.background { - self.borderNode.image = generateFieldBackgroundImage(backgroundImage: backgroundImage, backgroundSize: backgroundSize, frame: fieldFrame) - } + + self.borderNode.image = generateFieldBackgroundImage(backgroundImage: self.background?.foregroundImage, backgroundSize: self.background?.size, frame: fieldFrame) return fieldFrame } diff --git a/submodules/SettingsUI/Sources/Privacy and Security/PasscodeOptionsController.swift b/submodules/SettingsUI/Sources/Privacy and Security/PasscodeOptionsController.swift index 750686026c..7b4c6980d7 100644 --- a/submodules/SettingsUI/Sources/Privacy and Security/PasscodeOptionsController.swift +++ b/submodules/SettingsUI/Sources/Privacy and Security/PasscodeOptionsController.swift @@ -469,7 +469,7 @@ public func passcodeEntryController(context: AccountContext, animateIn: Bool = t biometrics = .none } #endif - 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: { + 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, statusBarHost: context.sharedContext.mainWindow?.statusBarHost, challengeData: challenge, biometrics: biometrics, arguments: PasscodeEntryControllerPresentationArguments(animated: false, fadeIn: true, cancel: { completion(false) }, modalPresentation: modalPresentation)) controller.presentationCompleted = { [weak controller] in