Swiftgram/submodules/TelegramCallsUI/Sources/VoiceChatRecordingSetupController.swift

631 lines
31 KiB
Swift

import Foundation
import UIKit
import Display
import AsyncDisplayKit
import TelegramCore
import SwiftSignalKit
import AccountContext
import TelegramPresentationData
import SolidRoundedButtonNode
import PresentationDataUtils
import VoiceChatActionButton
private let accentColor: UIColor = UIColor(rgb: 0x007aff)
final class VoiceChatRecordingSetupController: ViewController {
private var controllerNode: VoiceChatRecordingSetupControllerNode {
return self.displayNode as! VoiceChatRecordingSetupControllerNode
}
private let context: AccountContext
private let peer: EnginePeer
private let completion: (Bool?) -> Void
private var animatedIn = false
private var presentationDataDisposable: Disposable?
init(context: AccountContext, peer: EnginePeer, completion: @escaping (Bool?) -> Void) {
self.context = context
self.peer = peer
self.completion = completion
super.init(navigationBarPresentationData: nil)
self.statusBar.statusBarStyle = .Ignore
self.blocksBackgroundWhenInOverlay = true
self.presentationDataDisposable = (context.sharedContext.presentationData
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self {
strongSelf.controllerNode.updatePresentationData(presentationData)
}
})
self.statusBar.statusBarStyle = .Ignore
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.presentationDataDisposable?.dispose()
}
override public func loadDisplayNode() {
self.displayNode = VoiceChatRecordingSetupControllerNode(controller: self, context: self.context, peer: self.peer)
self.controllerNode.completion = { [weak self] videoOrientation in
self?.completion(videoOrientation)
}
self.controllerNode.dismiss = { [weak self] in
self?.presentingViewController?.dismiss(animated: false, completion: nil)
}
self.controllerNode.cancel = { [weak self] in
self?.dismiss()
}
}
override public func loadView() {
super.loadView()
}
override public func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !self.animatedIn {
self.animatedIn = true
self.controllerNode.animateIn()
}
}
override public func dismiss(completion: (() -> Void)? = nil) {
self.controllerNode.animateOut(completion: completion)
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
}
}
private class VoiceChatRecordingSetupControllerNode: ViewControllerTracingNode, ASScrollViewDelegate {
enum MediaMode {
case videoAndAudio
case audioOnly
}
enum VideoMode {
case portrait
case landscape
}
private weak var controller: VoiceChatRecordingSetupController?
private let context: AccountContext
private var presentationData: PresentationData
private let dimNode: ASDisplayNode
private let wrappingScrollNode: ASScrollNode
private let contentContainerNode: ASDisplayNode
private let effectNode: ASDisplayNode
private let backgroundNode: ASDisplayNode
private let contentBackgroundNode: ASDisplayNode
private let titleNode: ASTextNode
private let doneButton: VoiceChatActionButton
private let cancelButton: SolidRoundedButtonNode
private let modeContainerNode: ASDisplayNode
private let modeSeparatorNode: ASDisplayNode
private let videoAudioButton: HighlightTrackingButtonNode
private let videoAudioTitleNode: ImmediateTextNode
private let videoAudioCheckNode: ASImageNode
private let audioButton: HighlightTrackingButtonNode
private let audioTitleNode: ImmediateTextNode
private let audioCheckNode: ASImageNode
private let portraitButton: HighlightTrackingButtonNode
private let portraitIconNode: PreviewIconNode
private let portraitTitleNode: ImmediateTextNode
private let landscapeButton: HighlightTrackingButtonNode
private let landscapeIconNode: PreviewIconNode
private let landscapeTitleNode: ImmediateTextNode
private let selectionNode: ASImageNode
private var containerLayout: (ContainerViewLayout, CGFloat)?
private let hapticFeedback = HapticFeedback()
private let readyDisposable = MetaDisposable()
private var mediaMode: MediaMode = .videoAndAudio
private var videoMode: VideoMode = .portrait
var completion: ((Bool?) -> Void)?
var dismiss: (() -> Void)?
var cancel: (() -> Void)?
init(controller: VoiceChatRecordingSetupController, context: AccountContext, peer: EnginePeer) {
self.controller = controller
self.context = context
self.presentationData = context.sharedContext.currentPresentationData.with { $0 }
self.wrappingScrollNode = ASScrollNode()
self.wrappingScrollNode.view.alwaysBounceVertical = true
self.wrappingScrollNode.view.delaysContentTouches = false
self.wrappingScrollNode.view.canCancelContentTouches = true
self.dimNode = ASDisplayNode()
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.5)
self.contentContainerNode = ASDisplayNode()
self.contentContainerNode.isOpaque = false
self.backgroundNode = ASDisplayNode()
self.backgroundNode.clipsToBounds = true
self.backgroundNode.cornerRadius = 16.0
let backgroundColor = UIColor(rgb: 0x1c1c1e)
let textColor: UIColor = .white
let buttonColor: UIColor = UIColor(rgb: 0x2b2b2f)
let buttonTextColor: UIColor = .white
let blurStyle: UIBlurEffect.Style = .dark
self.effectNode = ASDisplayNode(viewBlock: {
return UIVisualEffectView(effect: UIBlurEffect(style: blurStyle))
})
self.contentBackgroundNode = ASDisplayNode()
self.contentBackgroundNode.backgroundColor = backgroundColor
let isLivestream: Bool
if case let .channel(channel) = peer, case .broadcast = channel.info {
isLivestream = true
} else {
isLivestream = false
}
let title = isLivestream ? self.presentationData.strings.LiveStream_RecordTitle : self.presentationData.strings.VoiceChat_RecordTitle
self.titleNode = ASTextNode()
self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(17.0), textColor: textColor)
self.doneButton = VoiceChatActionButton()
self.cancelButton = SolidRoundedButtonNode(theme: SolidRoundedButtonTheme(backgroundColor: buttonColor, foregroundColor: buttonTextColor), font: .regular, height: 52.0, cornerRadius: 11.0, gloss: false)
self.cancelButton.title = self.presentationData.strings.Common_Cancel
self.modeContainerNode = ASDisplayNode()
self.modeContainerNode.clipsToBounds = true
self.modeContainerNode.cornerRadius = 11.0
self.modeContainerNode.backgroundColor = UIColor(rgb: 0x303032)
self.modeSeparatorNode = ASDisplayNode()
self.modeSeparatorNode.backgroundColor = UIColor(rgb: 0x404041)
self.videoAudioButton = HighlightTrackingButtonNode()
self.videoAudioTitleNode = ImmediateTextNode()
self.videoAudioTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.VoiceChat_RecordVideoAndAudio, font: Font.regular(17.0), textColor: .white, paragraphAlignment: .left)
self.videoAudioCheckNode = ASImageNode()
self.videoAudioCheckNode.displaysAsynchronously = false
self.videoAudioCheckNode.image = UIImage(bundleImageName: "Call/Check")
self.audioButton = HighlightTrackingButtonNode()
self.audioTitleNode = ImmediateTextNode()
self.audioTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.VoiceChat_RecordOnlyAudio, font: Font.regular(17.0), textColor: .white, paragraphAlignment: .left)
self.audioCheckNode = ASImageNode()
self.audioCheckNode.displaysAsynchronously = false
self.audioCheckNode.image = UIImage(bundleImageName: "Call/Check")
self.portraitButton = HighlightTrackingButtonNode()
self.portraitButton.backgroundColor = UIColor(rgb: 0x303032)
self.portraitButton.cornerRadius = 11.0
self.portraitIconNode = PreviewIconNode()
self.portraitTitleNode = ImmediateTextNode()
self.portraitTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.VoiceChat_RecordPortrait, font: Font.semibold(15.0), textColor: UIColor(rgb: 0x8e8e93), paragraphAlignment: .left)
self.landscapeButton = HighlightTrackingButtonNode()
self.landscapeButton.backgroundColor = UIColor(rgb: 0x303032)
self.landscapeButton.cornerRadius = 11.0
self.landscapeIconNode = PreviewIconNode()
self.landscapeTitleNode = ImmediateTextNode()
self.landscapeTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.VoiceChat_RecordLandscape, font: Font.semibold(15.0), textColor: UIColor(rgb: 0x8e8e93), paragraphAlignment: .left)
self.selectionNode = ASImageNode()
self.selectionNode.displaysAsynchronously = false
self.selectionNode.image = generateImage(CGSize(width: 174.0, height: 140.0), rotatedContext: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
let lineWidth: CGFloat = 2.0
let path = UIBezierPath(roundedRect: bounds.insetBy(dx: lineWidth / 2.0, dy: lineWidth / 2.0), cornerRadius: 11.0)
let cgPath = path.cgPath.copy(strokingWithWidth: lineWidth, lineCap: .round, lineJoin: .round, miterLimit: 10.0)
context.addPath(cgPath)
context.clip()
let colors: [CGColor] = [UIColor(rgb: 0x5064fd).cgColor, UIColor(rgb: 0xe76598).cgColor]
var locations: [CGFloat] = [0.0, 1.0]
let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colors as CFArray, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: size.width, y: 0.0), options: CGGradientDrawingOptions())
})
self.selectionNode.isUserInteractionEnabled = false
super.init()
self.backgroundColor = nil
self.isOpaque = false
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
self.addSubnode(self.dimNode)
self.wrappingScrollNode.view.delegate = self.wrappedScrollViewDelegate
self.addSubnode(self.wrappingScrollNode)
self.wrappingScrollNode.addSubnode(self.backgroundNode)
self.wrappingScrollNode.addSubnode(self.contentContainerNode)
self.backgroundNode.addSubnode(self.effectNode)
self.backgroundNode.addSubnode(self.contentBackgroundNode)
self.contentContainerNode.addSubnode(self.titleNode)
self.contentContainerNode.addSubnode(self.doneButton)
self.contentContainerNode.addSubnode(self.cancelButton)
self.contentContainerNode.addSubnode(self.modeContainerNode)
self.contentContainerNode.addSubnode(self.videoAudioTitleNode)
self.contentContainerNode.addSubnode(self.videoAudioCheckNode)
self.contentContainerNode.addSubnode(self.videoAudioButton)
self.contentContainerNode.addSubnode(self.modeSeparatorNode)
self.contentContainerNode.addSubnode(self.audioTitleNode)
self.contentContainerNode.addSubnode(self.audioCheckNode)
self.contentContainerNode.addSubnode(self.audioButton)
self.contentContainerNode.addSubnode(self.portraitButton)
self.contentContainerNode.addSubnode(self.portraitIconNode)
self.contentContainerNode.addSubnode(self.portraitTitleNode)
self.contentContainerNode.addSubnode(self.landscapeButton)
self.contentContainerNode.addSubnode(self.landscapeIconNode)
self.contentContainerNode.addSubnode(self.landscapeTitleNode)
self.contentContainerNode.addSubnode(self.selectionNode)
self.videoAudioButton.addTarget(self, action: #selector(self.videoAudioPressed), forControlEvents: .touchUpInside)
self.audioButton.addTarget(self, action: #selector(self.audioPressed), forControlEvents: .touchUpInside)
self.portraitButton.addTarget(self, action: #selector(self.portraitPressed), forControlEvents: .touchUpInside)
self.landscapeButton.addTarget(self, action: #selector(self.landscapePressed), forControlEvents: .touchUpInside)
self.doneButton.addTarget(self, action: #selector(self.donePressed), forControlEvents: .touchUpInside)
self.cancelButton.pressed = { [weak self] in
if let strongSelf = self {
strongSelf.cancel?()
}
}
}
@objc private func donePressed() {
let videoOrientation: Bool?
switch self.mediaMode {
case .audioOnly:
videoOrientation = nil
case .videoAndAudio:
switch self.videoMode {
case .portrait:
videoOrientation = true
case .landscape:
videoOrientation = false
}
}
self.completion?(videoOrientation)
self.dismiss?()
}
@objc private func videoAudioPressed() {
self.mediaMode = .videoAndAudio
if let (layout, navigationHeight) = self.containerLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut))
}
}
@objc private func audioPressed() {
self.mediaMode = .audioOnly
if let (layout, navigationHeight) = self.containerLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut))
}
}
@objc private func portraitPressed() {
self.mediaMode = .videoAndAudio
self.videoMode = .portrait
if let (layout, navigationHeight) = self.containerLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut))
}
}
@objc private func landscapePressed() {
self.mediaMode = .videoAndAudio
self.videoMode = .landscape
if let (layout, navigationHeight) = self.containerLayout {
self.containerLayoutUpdated(layout, navigationBarHeight: navigationHeight, transition: .animated(duration: 0.2, curve: .easeInOut))
}
}
func updatePresentationData(_ presentationData: PresentationData) {
self.presentationData = presentationData
}
override func didLoad() {
super.didLoad()
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
self.wrappingScrollNode.view.contentInsetAdjustmentBehavior = .never
}
}
@objc func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.cancel?()
}
}
func animateIn() {
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.4)
let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY
let dimPosition = self.dimNode.layer.position
let transition = ContainedViewLayoutTransition.animated(duration: 0.4, curve: .spring)
let targetBounds = self.bounds
self.bounds = self.bounds.offsetBy(dx: 0.0, dy: -offset)
self.dimNode.position = CGPoint(x: dimPosition.x, y: dimPosition.y - offset)
transition.animateView({
self.bounds = targetBounds
self.dimNode.position = dimPosition
})
}
func animateOut(completion: (() -> Void)? = nil) {
var dimCompleted = false
var offsetCompleted = false
let internalCompletion: () -> Void = { [weak self] in
if let strongSelf = self, dimCompleted && offsetCompleted {
strongSelf.dismiss?()
}
completion?()
}
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
dimCompleted = true
internalCompletion()
})
let offset = self.bounds.size.height - self.contentBackgroundNode.frame.minY
let dimPosition = self.dimNode.layer.position
self.dimNode.layer.animatePosition(from: dimPosition, to: CGPoint(x: dimPosition.x, y: dimPosition.y - offset), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
self.layer.animateBoundsOriginYAdditive(from: 0.0, to: -offset, duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, completion: { _ in
offsetCompleted = true
internalCompletion()
})
}
override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
if self.bounds.contains(point) {
if !self.contentBackgroundNode.bounds.contains(self.convert(point, to: self.contentBackgroundNode)) {
return self.dimNode.view
}
}
return super.hitTest(point, with: event)
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
let contentOffset = scrollView.contentOffset
let additionalTopHeight = max(0.0, -contentOffset.y)
if additionalTopHeight >= 30.0 {
self.cancel?()
}
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
self.containerLayout = (layout, navigationBarHeight)
let isLandscape: Bool
if layout.size.width > layout.size.height, case .compact = layout.metrics.widthClass {
isLandscape = true
} else {
isLandscape = false
}
var insets = layout.insets(options: [.statusBar, .input])
let cleanInsets = layout.insets(options: [.statusBar])
insets.top = max(10.0, insets.top)
let buttonOffset: CGFloat = 60.0
let bottomInset: CGFloat = 10.0 + cleanInsets.bottom
let titleHeight: CGFloat = 54.0
var contentHeight = titleHeight + bottomInset + 52.0 + 17.0
let innerContentHeight: CGFloat = 287.0
var width = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 0.0)
if isLandscape {
contentHeight = layout.size.height
width = layout.size.width
} else {
contentHeight = titleHeight + bottomInset + 52.0 + 17.0 + innerContentHeight + buttonOffset
}
let inset: CGFloat = 16.0
let sideInset = floor((layout.size.width - width) / 2.0)
let contentContainerFrame = CGRect(origin: CGPoint(x: sideInset, y: layout.size.height - contentHeight), size: CGSize(width: width, height: contentHeight))
let contentFrame = contentContainerFrame
var backgroundFrame = CGRect(origin: CGPoint(x: contentFrame.minX, y: contentFrame.minY), size: CGSize(width: contentFrame.width, height: contentFrame.height + 2000.0))
if backgroundFrame.minY < contentFrame.minY {
backgroundFrame.origin.y = contentFrame.minY
}
transition.updateAlpha(node: self.titleNode, alpha: isLandscape ? 0.0 : 1.0)
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
transition.updateFrame(node: self.effectNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
transition.updateFrame(node: self.contentBackgroundNode, frame: CGRect(origin: CGPoint(), size: backgroundFrame.size))
transition.updateFrame(node: self.wrappingScrollNode, frame: CGRect(origin: CGPoint(), size: layout.size))
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
let titleSize = self.titleNode.measure(CGSize(width: width, height: titleHeight))
let titleFrame = CGRect(origin: CGPoint(x: floor((contentFrame.width - titleSize.width) / 2.0), y: 18.0), size: titleSize)
transition.updateFrame(node: self.titleNode, frame: titleFrame)
let itemHeight: CGFloat = 44.0
transition.updateFrame(node: self.modeContainerNode, frame: CGRect(x: inset, y: 56.0, width: contentFrame.width - inset * 2.0, height: itemHeight * 2.0))
transition.updateFrame(node: self.videoAudioButton, frame: CGRect(x: inset, y: 56.0, width: contentFrame.width - inset * 2.0, height: itemHeight))
transition.updateFrame(node: self.videoAudioCheckNode, frame: CGRect(x: contentFrame.width - inset - 16.0 - 20.0, y: 56.0 + floorToScreenPixels((itemHeight - 16.0) / 2.0), width: 16.0, height: 16.0))
self.videoAudioCheckNode.isHidden = self.mediaMode != .videoAndAudio
let videoAudioSize = self.videoAudioTitleNode.updateLayout(CGSize(width: contentFrame.width - inset * 2.0, height: itemHeight))
transition.updateFrame(node: self.videoAudioTitleNode, frame: CGRect(x: inset + 16.0, y: 56.0 + floorToScreenPixels((itemHeight - videoAudioSize.height) / 2.0), width: videoAudioSize.width, height: videoAudioSize.height))
transition.updateFrame(node: self.audioButton, frame: CGRect(x: inset, y: 56.0 + itemHeight, width: contentFrame.width - inset * 2.0, height: itemHeight))
transition.updateFrame(node: self.audioCheckNode, frame: CGRect(x: contentFrame.width - inset - 16.0 - 20.0, y: 56.0 + itemHeight + floorToScreenPixels((itemHeight - 16.0) / 2.0), width: 16.0, height: 16.0))
self.audioCheckNode.isHidden = self.mediaMode != .audioOnly
let audioSize = self.audioTitleNode.updateLayout(CGSize(width: contentFrame.width - inset * 2.0, height: itemHeight))
transition.updateFrame(node: self.audioTitleNode, frame: CGRect(x: inset + 16.0, y: 56.0 + itemHeight + floorToScreenPixels((itemHeight - audioSize.height) / 2.0), width: audioSize.width, height: audioSize.height))
transition.updateFrame(node: self.modeSeparatorNode, frame: CGRect(x: inset + 16.0, y: 56.0 + itemHeight, width: contentFrame.width - inset * 2.0 - 16.0, height: UIScreenPixel))
var buttonsAlpha: CGFloat = 1.0
if case .audioOnly = self.mediaMode {
buttonsAlpha = 0.3
}
transition.updateAlpha(node: self.portraitButton, alpha: buttonsAlpha)
transition.updateAlpha(node: self.portraitIconNode, alpha: buttonsAlpha)
transition.updateAlpha(node: self.portraitTitleNode, alpha: buttonsAlpha)
transition.updateAlpha(node: self.landscapeButton, alpha: buttonsAlpha)
transition.updateAlpha(node: self.landscapeIconNode, alpha: buttonsAlpha)
transition.updateAlpha(node: self.landscapeTitleNode, alpha: buttonsAlpha)
transition.updateAlpha(node: self.selectionNode, alpha: buttonsAlpha)
self.portraitTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.VoiceChat_RecordPortrait, font: Font.semibold(15.0), textColor: self.videoMode == .portrait ? UIColor(rgb: 0xb56df4) : UIColor(rgb: 0x8e8e93), paragraphAlignment: .left)
self.landscapeTitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.VoiceChat_RecordLandscape, font: Font.semibold(15.0), textColor: self.videoMode == .landscape ? UIColor(rgb: 0xb56df4) : UIColor(rgb: 0x8e8e93), paragraphAlignment: .left)
let buttonWidth = floorToScreenPixels((contentFrame.width - inset * 2.0 - 11.0) / 2.0)
let portraitButtonFrame = CGRect(x: inset, y: 56.0 + itemHeight * 2.0 + 25.0, width: buttonWidth, height: 140.0)
transition.updateFrame(node: self.portraitButton, frame: portraitButtonFrame)
transition.updateFrame(node: self.portraitIconNode, frame: CGRect(x: portraitButtonFrame.minX + floorToScreenPixels((portraitButtonFrame.width - 72.0) / 2.0), y: portraitButtonFrame.minY + floorToScreenPixels((portraitButtonFrame.height - 122.0) / 2.0), width: 76.0, height: 122.0))
self.portraitIconNode.updateLayout(landscape: false)
let portraitSize = self.portraitTitleNode.updateLayout(CGSize(width: buttonWidth, height: 30.0))
transition.updateFrame(node: self.portraitTitleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels(portraitButtonFrame.center.x - portraitSize.width / 2.0), y: portraitButtonFrame.maxY + 7.0), size: portraitSize))
let landscapeButtonFrame = CGRect(x: portraitButtonFrame.maxX + 11.0, y: portraitButtonFrame.minY, width: portraitButtonFrame.width, height: portraitButtonFrame.height)
transition.updateFrame(node: self.landscapeButton, frame: landscapeButtonFrame)
transition.updateFrame(node: self.landscapeIconNode, frame: CGRect(x: landscapeButtonFrame.minX + floorToScreenPixels((landscapeButtonFrame.width - 122.0) / 2.0), y: landscapeButtonFrame.minY + floorToScreenPixels((landscapeButtonFrame.height - 76.0) / 2.0), width: 122.0, height: 76.0))
self.landscapeIconNode.updateLayout(landscape: true)
let landscapeSize = self.landscapeTitleNode.updateLayout(CGSize(width: buttonWidth, height: 30.0))
transition.updateFrame(node: self.landscapeTitleNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels(landscapeButtonFrame.center.x - landscapeSize.width / 2.0), y: landscapeButtonFrame.maxY + 7.0), size: landscapeSize))
let centralButtonSide = min(contentFrame.width, layout.size.height) - 32.0
let centralButtonSize = CGSize(width: centralButtonSide, height: centralButtonSide)
let buttonInset: CGFloat = 16.0
let doneButtonPreFrame = CGRect(x: buttonInset, y: contentHeight - 50.0 - insets.bottom - 16.0 - buttonOffset, width: contentFrame.width - buttonInset * 2.0, height: 50.0)
let doneButtonFrame = CGRect(origin: CGPoint(x: floor(doneButtonPreFrame.midX - centralButtonSize.width / 2.0), y: floor(doneButtonPreFrame.midY - centralButtonSize.height / 2.0)), size: centralButtonSize)
transition.updateFrame(node: self.doneButton, frame: doneButtonFrame)
if self.videoMode == .portrait {
self.selectionNode.frame = portraitButtonFrame.insetBy(dx: -1.0, dy: -1.0)
} else {
self.selectionNode.frame = landscapeButtonFrame.insetBy(dx: -1.0, dy: -1.0)
}
self.doneButton.update(size: centralButtonSize, buttonSize: CGSize(width: 112.0, height: 112.0), state: .button(text: self.presentationData.strings.VoiceChat_RecordStartRecording), title: "", subtitle: "", dark: false, small: false)
let cancelButtonHeight = self.cancelButton.updateLayout(width: contentFrame.width - buttonInset * 2.0, transition: transition)
transition.updateFrame(node: self.cancelButton, frame: CGRect(x: buttonInset, y: contentHeight - cancelButtonHeight - insets.bottom - 16.0, width: contentFrame.width, height: cancelButtonHeight))
transition.updateFrame(node: self.contentContainerNode, frame: contentContainerFrame)
}
}
private class PreviewIconNode: ASDisplayNode {
private let avatar1Node: ASImageNode
private let avatar2Node: ASImageNode
private let avatar3Node: ASImageNode
private let avatar4Node: ASImageNode
override init() {
self.avatar1Node = ASImageNode()
self.avatar1Node.cornerRadius = 4.0
self.avatar1Node.clipsToBounds = true
self.avatar1Node.displaysAsynchronously = false
self.avatar1Node.backgroundColor = UIColor(rgb: 0x834fff)
self.avatar1Node.image = UIImage(bundleImageName: "Call/Avatar1")
self.avatar1Node.contentMode = .bottom
self.avatar2Node = ASImageNode()
self.avatar2Node.cornerRadius = 4.0
self.avatar2Node.clipsToBounds = true
self.avatar2Node.displaysAsynchronously = false
self.avatar2Node.backgroundColor = UIColor(rgb: 0x63d5c9)
self.avatar2Node.image = UIImage(bundleImageName: "Call/Avatar2")
self.avatar2Node.contentMode = .scaleAspectFit
self.avatar3Node = ASImageNode()
self.avatar3Node.cornerRadius = 4.0
self.avatar3Node.clipsToBounds = true
self.avatar3Node.displaysAsynchronously = false
self.avatar3Node.backgroundColor = UIColor(rgb: 0xccff60)
self.avatar3Node.image = UIImage(bundleImageName: "Call/Avatar3")
self.avatar3Node.contentMode = .scaleAspectFit
self.avatar4Node = ASImageNode()
self.avatar4Node.cornerRadius = 4.0
self.avatar4Node.clipsToBounds = true
self.avatar4Node.displaysAsynchronously = false
self.avatar4Node.backgroundColor = UIColor(rgb: 0xf5512a)
self.avatar4Node.image = UIImage(bundleImageName: "Call/Avatar4")
self.avatar4Node.contentMode = .scaleAspectFit
super.init()
self.isUserInteractionEnabled = false
self.addSubnode(self.avatar1Node)
self.addSubnode(self.avatar2Node)
self.addSubnode(self.avatar3Node)
self.addSubnode(self.avatar4Node)
}
func updateLayout(landscape: Bool) {
if landscape {
self.avatar1Node.frame = CGRect(x: 0.0, y: 0.0, width: 96.0, height: 76.0)
self.avatar2Node.frame = CGRect(x: 98.0, y: 0.0, width: 24.0, height: 24.0)
self.avatar3Node.frame = CGRect(x: 98.0, y: 26.0, width: 24.0, height: 24.0)
self.avatar4Node.frame = CGRect(x: 98.0, y: 52.0, width: 24.0, height: 24.0)
} else {
self.avatar1Node.frame = CGRect(x: 0.0, y: 0.0, width: 76.0, height: 96.0)
self.avatar2Node.frame = CGRect(x: 0.0, y: 98.0, width: 24.0, height: 24.0)
self.avatar3Node.frame = CGRect(x: 26.0, y: 98.0, width: 24.0, height: 24.0)
self.avatar4Node.frame = CGRect(x: 52.0, y: 98.0, width: 24.0, height: 24.0)
}
}
}