import Foundation import UIKit import Display import AsyncDisplayKit import TelegramCore import TelegramPresentationData import TextFormat import AccountContext import EncryptionKeyVisualization import LocalizedPeerData private func processHexString(_ string: String) -> String { var result = "" var i = 0 for c in string { if i % 2 == 0 && i != 0 { result.append(" ") } if i % 8 == 0 && i != 0 { result.append(" ") } result.append(c) i += 1 } return result } final class SecretChatKeyControllerNode: ViewControllerTracingNode { private let context: AccountContext private var presentationData: PresentationData private let fingerprint: SecretChatKeyFingerprint private let peer: EnginePeer private let getNavigationController: () -> NavigationController? private let scrollNode: ASScrollNode private let imageNode: ASImageNode private let keyTextNode: TextNode private let infoNode: TextNode private var validImageSize: CGSize? init(context: AccountContext, presentationData: PresentationData, fingerprint: SecretChatKeyFingerprint, peer: EnginePeer, getNavigationController: @escaping () -> NavigationController?) { self.context = context self.presentationData = presentationData self.fingerprint = fingerprint self.peer = peer self.getNavigationController = getNavigationController self.scrollNode = ASScrollNode() self.imageNode = ASImageNode() self.imageNode.isLayerBacked = true self.imageNode.displaysAsynchronously = false self.imageNode.displayWithoutProcessing = true self.keyTextNode = TextNode() self.keyTextNode.isUserInteractionEnabled = false self.keyTextNode.displaysAsynchronously = false self.infoNode = TextNode() self.infoNode.displaysAsynchronously = false super.init() self.backgroundColor = presentationData.theme.list.plainBackgroundColor self.addSubnode(self.scrollNode) self.scrollNode.addSubnode(self.imageNode) self.scrollNode.addSubnode(self.keyTextNode) self.scrollNode.addSubnode(self.infoNode) } override func didLoad() { super.didLoad() self.infoNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.infoTap(_:)))) } func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { var insets = layout.insets(options: [.input]) insets.top += navigationBarHeight self.scrollNode.frame = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top)) let sideInset: CGFloat = 10.0 var imageSize = CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.width - sideInset * 2.0) if imageSize.height > layout.size.height - insets.top - sideInset * 2.0 - 100.0 { let side = layout.size.height - insets.top - sideInset * 2.0 - 100.0 imageSize = CGSize(width: side, height: side) } if imageSize.height > 512.0 { imageSize = CGSize(width: 512.0, height: 512.0) } if self.validImageSize != imageSize { self.validImageSize = imageSize self.imageNode.image = secretChatKeyImage(self.fingerprint, size: imageSize) } let makeKeyTextLayout = TextNode.asyncLayout(self.keyTextNode) let makeInfoLayout = TextNode.asyncLayout(self.infoNode) let keySignatureData = self.fingerprint.sha1.data() let additionalSignature = self.fingerprint.sha256.data() var data = Data() data.append(keySignatureData) data.append(additionalSignature) let s1: String = (data.subdata(in: 0 ..< 8) as NSData).stringByEncodingInHex() let s2: String = (data.subdata(in: 8 ..< 16) as NSData).stringByEncodingInHex() let s3: String = (additionalSignature.subdata(in: 0 ..< 8) as NSData).stringByEncodingInHex() let s4: String = (additionalSignature.subdata(in : 8 ..< 16) as NSData).stringByEncodingInHex() let text: String = "\(processHexString(s1))\n\(processHexString(s2))\n\(processHexString(s3))\n\(processHexString(s4))" let (keyTextLayout, keyTextApply) = makeKeyTextLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: text, font: Font.semiboldMonospace(15.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor), backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: layout.size.width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .left, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets())) let infoString = self.presentationData.strings.EncryptionKey_Description(self.peer.compactDisplayTitle, self.peer.compactDisplayTitle) let infoText = NSMutableAttributedString(string: infoString.string, attributes: [.font: Font.regular(14.0), .foregroundColor: self.presentationData.theme.list.itemPrimaryTextColor]) for range in infoString.ranges { infoText.addAttributes([.font: Font.semibold(14.0)], range: range.range) } let linkRange = (infoString.string as NSString).range(of: "telegram.org") if linkRange.location != NSNotFound { infoText.addAttributes([.foregroundColor: self.presentationData.theme.list.itemAccentColor, NSAttributedString.Key(rawValue: TelegramTextAttributes.URL): "https://telegram.org/faq#secret-chats"], range: linkRange) } let (infoLayout, infoApply) = makeInfoLayout(TextNodeLayoutArguments(attributedString: infoText, backgroundColor: nil, maximumNumberOfLines: 0, truncationType: .end, constrainedSize: CGSize(width: layout.size.width - sideInset * 2.0, height: CGFloat.greatestFiniteMagnitude), alignment: .center, lineSpacing: 0.0, cutout: nil, insets: UIEdgeInsets())) let _ = keyTextApply() let _ = infoApply() let imageSpacing: CGFloat = 12.0 let textSpacing: CGFloat = 10.0 let contentHeight = imageSize.height + imageSpacing + keyTextLayout.size.height + textSpacing + infoLayout.size.height let contentOrigin = sideInset + max(0, floor((layout.size.height - insets.top - sideInset * 2.0 - contentHeight) / 2.0)) self.scrollNode.view.contentSize = CGSize(width: layout.size.width, height: contentHeight + sideInset * 2.0) let imageFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - imageSize.width) / 2.0), y: contentOrigin), size: imageSize) transition.updateFrame(node: self.imageNode, frame: imageFrame) let keyTextFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - keyTextLayout.size.width) / 2.0), y: imageFrame.maxY + imageSpacing), size: keyTextLayout.size) transition.updateFrame(node: self.keyTextNode, frame: keyTextFrame) let infoFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - infoLayout.size.width) / 2.0), y: keyTextFrame.maxY + textSpacing), size: infoLayout.size) transition.updateFrame(node: self.infoNode, frame: infoFrame) } @objc func infoTap(_ recognizer: UITapGestureRecognizer) { if case .ended = recognizer.state { let point = recognizer.location(in: recognizer.view) if let attributes = self.infoNode.attributesAtPoint(point)?.1 { if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String { self.context.sharedContext.openExternalUrl(context: self.context, urlContext: .generic, url: url, forceExternal: false, presentationData: self.presentationData, navigationController: self.getNavigationController(), dismissInput: { [weak self] in self?.view.endEditing(true) }) } } } } }