|  |  |  | // Copyright © 2022 Rangeproof Pty Ltd. All rights reserved. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import UIKit | 
					
						
							|  |  |  | import SessionUIKit | 
					
						
							|  |  |  | import CoreMedia | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | public protocol VideoPreviewDelegate: AnyObject { | 
					
						
							|  |  |  |     func cameraDidConfirmTurningOn() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class VideoPreviewVC: UIViewController, CameraManagerDelegate { | 
					
						
							|  |  |  |     weak var delegate: VideoPreviewDelegate? | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     lazy var cameraManager: CameraManager = { | 
					
						
							|  |  |  |         let result = CameraManager() | 
					
						
							|  |  |  |         result.delegate = self | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  |     }() | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // MARK: - UI Components | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private lazy var renderView: RenderView = { | 
					
						
							|  |  |  |         let result = RenderView() | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  |     }() | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private lazy var fadeView: GradientView = { | 
					
						
							|  |  |  |         let height: CGFloat = ((UIApplication.shared.keyWindow?.safeAreaInsets.top) | 
					
						
							|  |  |  |             .map { $0 + Values.veryLargeSpacing }) | 
					
						
							|  |  |  |             .defaulting(to: 64) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         let result: GradientView = GradientView() | 
					
						
							|  |  |  |         result.themeBackgroundGradient = [ | 
					
						
							|  |  |  |             .value(.backgroundPrimary, alpha: 0.4), | 
					
						
							|  |  |  |             .value(.backgroundPrimary, alpha: 0) | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         result.set(.height, to: height) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  |     }() | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private lazy var closeButton: UIButton = { | 
					
						
							|  |  |  |         let result = UIButton(type: .custom) | 
					
						
							|  |  |  |         result.setImage( | 
					
						
							|  |  |  |             UIImage(named: "X")? | 
					
						
							|  |  |  |                 .withRenderingMode(.alwaysTemplate), | 
					
						
							|  |  |  |             for: .normal | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         result.themeTintColor = .textPrimary | 
					
						
							|  |  |  |         result.addTarget(self, action: #selector(cancel), for: UIControl.Event.touchUpInside) | 
					
						
							|  |  |  |         result.set(.width, to: 60) | 
					
						
							|  |  |  |         result.set(.height, to: 60) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  |     }() | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private lazy var confirmButton: UIButton = { | 
					
						
							|  |  |  |         let result = UIButton(type: .custom) | 
					
						
							|  |  |  |         result.setImage( | 
					
						
							|  |  |  |             UIImage(named: "Check")? | 
					
						
							|  |  |  |                 .withRenderingMode(.alwaysTemplate), | 
					
						
							|  |  |  |             for: .normal | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  |         result.themeTintColor = .textPrimary | 
					
						
							|  |  |  |         result.addTarget(self, action: #selector(confirm), for: UIControl.Event.touchUpInside) | 
					
						
							|  |  |  |         result.set(.width, to: 60) | 
					
						
							|  |  |  |         result.set(.height, to: 60) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  |     }() | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     private lazy var titleLabel: UILabel = { | 
					
						
							|  |  |  |         let result = UILabel() | 
					
						
							|  |  |  |         result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize) | 
					
						
							|  |  |  |         result.text = "Preview" | 
					
						
							|  |  |  |         result.themeTextColor = .textPrimary | 
					
						
							|  |  |  |         result.textAlignment = .center | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         return result | 
					
						
							|  |  |  |     }() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // MARK: - Lifecycle | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     override func viewDidLoad() { | 
					
						
							|  |  |  |         super.viewDidLoad() | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         view.themeBackgroundColor = .backgroundPrimary | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         setUpViewHierarchy() | 
					
						
							|  |  |  |         cameraManager.prepare() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     func setUpViewHierarchy() { | 
					
						
							|  |  |  |         // Preview video view | 
					
						
							|  |  |  |         view.addSubview(renderView) | 
					
						
							|  |  |  |         renderView.translatesAutoresizingMaskIntoConstraints = false | 
					
						
							|  |  |  |         renderView.pin(to: view) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Fade view | 
					
						
							|  |  |  |         view.addSubview(fadeView) | 
					
						
							|  |  |  |         fadeView.translatesAutoresizingMaskIntoConstraints = false | 
					
						
							|  |  |  |         fadeView.pin([ UIView.HorizontalEdge.left, UIView.VerticalEdge.top, UIView.HorizontalEdge.right ], to: view) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Close button | 
					
						
							|  |  |  |         view.addSubview(closeButton) | 
					
						
							|  |  |  |         closeButton.translatesAutoresizingMaskIntoConstraints = false | 
					
						
							|  |  |  |         closeButton.pin(.left, to: .left, of: view) | 
					
						
							|  |  |  |         closeButton.center(.vertical, in: fadeView) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Confirm button | 
					
						
							|  |  |  |         view.addSubview(confirmButton) | 
					
						
							|  |  |  |         confirmButton.translatesAutoresizingMaskIntoConstraints = false | 
					
						
							|  |  |  |         confirmButton.pin(.right, to: .right, of: view) | 
					
						
							|  |  |  |         confirmButton.center(.vertical, in: fadeView) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         // Title label | 
					
						
							|  |  |  |         view.addSubview(titleLabel) | 
					
						
							|  |  |  |         titleLabel.translatesAutoresizingMaskIntoConstraints = false | 
					
						
							|  |  |  |         titleLabel.center(.vertical, in: closeButton) | 
					
						
							|  |  |  |         titleLabel.center(.horizontal, in: view) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     override func viewDidAppear(_ animated: Bool) { | 
					
						
							|  |  |  |         super.viewDidAppear(animated) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         cameraManager.start() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     override func viewWillDisappear(_ animated: Bool) { | 
					
						
							|  |  |  |         super.viewWillDisappear(animated) | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         cameraManager.stop() | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // MARK: - Interaction | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     @objc func confirm() { | 
					
						
							|  |  |  |         delegate?.cameraDidConfirmTurningOn() | 
					
						
							|  |  |  |         self.dismiss(animated: true, completion: nil) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     @objc func cancel() { | 
					
						
							|  |  |  |         self.dismiss(animated: true, completion: nil) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     // MARK: - CameraManagerDelegate | 
					
						
							|  |  |  |      | 
					
						
							|  |  |  |     func handleVideoOutputCaptured(sampleBuffer: CMSampleBuffer) { | 
					
						
							|  |  |  |         renderView.enqueue(sampleBuffer: sampleBuffer) | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } |