From 1231b9c20a30e4192778c840f813df46e03ee9e9 Mon Sep 17 00:00:00 2001 From: ryanzhao Date: Thu, 21 Oct 2021 16:28:48 +1100 Subject: [PATCH] add preview before staring video --- Session.xcodeproj/project.pbxproj | 12 ++ Session/Calls/CallVC.swift | 25 ++-- Session/Calls/VideoPreviewVC.swift | 123 ++++++++++++++++++ Session/Calls/Views & Modals/RenderView.swift | 36 +++++ .../ConversationVC+Interaction.swift | 11 +- .../Views & Modals/CallModal.swift | 65 +++++++++ .../Session/Check.imageset/Contents.json | 12 ++ .../Session/Check.imageset/check.pdf | Bin 0 -> 4092 bytes .../Translations/en.lproj/Localizable.strings | 2 + 9 files changed, 276 insertions(+), 10 deletions(-) create mode 100644 Session/Calls/VideoPreviewVC.swift create mode 100644 Session/Calls/Views & Modals/RenderView.swift create mode 100644 Session/Conversations/Views & Modals/CallModal.swift create mode 100644 Session/Meta/Images.xcassets/Session/Check.imageset/Contents.json create mode 100644 Session/Meta/Images.xcassets/Session/Check.imageset/check.pdf diff --git a/Session.xcodeproj/project.pbxproj b/Session.xcodeproj/project.pbxproj index 7299c8883..307b1138d 100644 --- a/Session.xcodeproj/project.pbxproj +++ b/Session.xcodeproj/project.pbxproj @@ -137,6 +137,9 @@ 76C87F19181EFCE600C4ACAB /* MediaPlayer.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 76C87F18181EFCE600C4ACAB /* MediaPlayer.framework */; }; 76EB054018170B33006006FC /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 76EB03C318170B33006006FC /* AppDelegate.m */; }; 7B1581E2271E743B00848B49 /* OWSSounds.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1581E1271E743B00848B49 /* OWSSounds.swift */; }; + 7B1581E4271FC59D00848B49 /* CallModal.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1581E3271FC59C00848B49 /* CallModal.swift */; }; + 7B1581E6271FD2A100848B49 /* VideoPreviewVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1581E5271FD2A100848B49 /* VideoPreviewVC.swift */; }; + 7B1581E827210ECC00848B49 /* RenderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B1581E727210ECC00848B49 /* RenderView.swift */; }; 7B4C75CB26B37E0F0000AC89 /* UnsendRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4C75CA26B37E0F0000AC89 /* UnsendRequest.swift */; }; 7B4C75CD26BB92060000AC89 /* DeletedMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */; }; 7B7CB189270430D20079FF93 /* CallMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7B7CB188270430D20079FF93 /* CallMessageView.swift */; }; @@ -1116,6 +1119,9 @@ 76EB03C218170B33006006FC /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 76EB03C318170B33006006FC /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 7B1581E1271E743B00848B49 /* OWSSounds.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OWSSounds.swift; sourceTree = ""; }; + 7B1581E3271FC59C00848B49 /* CallModal.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallModal.swift; sourceTree = ""; }; + 7B1581E5271FD2A100848B49 /* VideoPreviewVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoPreviewVC.swift; sourceTree = ""; }; + 7B1581E727210ECC00848B49 /* RenderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RenderView.swift; sourceTree = ""; }; 7B2DB2AD26F1B0FF0035B509 /* si */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = si; path = si.lproj/Localizable.strings; sourceTree = ""; }; 7B4C75CA26B37E0F0000AC89 /* UnsendRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UnsendRequest.swift; sourceTree = ""; }; 7B4C75CC26BB92060000AC89 /* DeletedMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedMessageView.swift; sourceTree = ""; }; @@ -2053,6 +2059,7 @@ children = ( 7B7CB18D270D066F0079FF93 /* IncomingCallBanner.swift */, 7B7CB18F270FB2150079FF93 /* MiniCallView.swift */, + 7B1581E727210ECC00848B49 /* RenderView.swift */, ); path = "Views & Modals"; sourceTree = ""; @@ -2169,6 +2176,7 @@ B8214A2A25D63EB9009C0F2A /* MessagesTableView.swift */, C374EEEA25DA3CA70073A857 /* ConversationTitleView.swift */, B848A4C4269EAAA200617031 /* UserDetailsSheet.swift */, + 7B1581E3271FC59C00848B49 /* CallModal.swift */, ); path = "Views & Modals"; sourceTree = ""; @@ -2350,6 +2358,7 @@ isa = PBXGroup; children = ( B877E24126CA12910007970A /* CallVC.swift */, + 7B1581E5271FD2A100848B49 /* VideoPreviewVC.swift */, B877E24526CA13BA0007970A /* CallVC+Camera.swift */, B8B558F026C4BB0600693325 /* CameraManager.swift */, 7B7CB18C270D06350079FF93 /* Views & Modals */, @@ -4837,6 +4846,7 @@ 34386A54207D271D009F5D9C /* NeverClearView.swift in Sources */, 451166C01FD86B98000739BA /* AccountManager.swift in Sources */, C374EEF425DB31D40073A857 /* VoiceMessageRecordingView.swift in Sources */, + 7B1581E6271FD2A100848B49 /* VideoPreviewVC.swift in Sources */, B83F2B88240CB75A000A54AB /* UIImage+Scaling.swift in Sources */, 3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */, 340FC8AA204DAC8D007AEB0F /* NotificationSettingsViewController.m in Sources */, @@ -4917,6 +4927,7 @@ B875885A264503A6000E60D0 /* JoinOpenGroupModal.swift in Sources */, B8CCF6432397711F0091D419 /* SettingsVC.swift in Sources */, C354E75A23FE2A7600CE22E3 /* BaseVC.swift in Sources */, + 7B1581E827210ECC00848B49 /* RenderView.swift in Sources */, 3441FD9F21A3604F00BB9542 /* BackupRestoreViewController.swift in Sources */, 45C0DC1B1E68FE9000E04C47 /* UIApplication+OWS.swift in Sources */, 4539B5861F79348F007141FF /* PushRegistrationManager.swift in Sources */, @@ -4926,6 +4937,7 @@ 4CA46F4C219CCC630038ABDE /* CaptionView.swift in Sources */, C328253025CA55370062D0A7 /* ContextMenuWindow.swift in Sources */, 340FC8B7204DAC8D007AEB0F /* OWSConversationSettingsViewController.m in Sources */, + 7B1581E4271FC59D00848B49 /* CallModal.swift in Sources */, 34BECE2E1F7ABCE000D7438D /* GifPickerViewController.swift in Sources */, B84664F5235022F30083A1CD /* MentionUtilities.swift in Sources */, 34D1F0C01F8EC1760066283D /* MessageRecipientStatusUtils.swift in Sources */, diff --git a/Session/Calls/CallVC.swift b/Session/Calls/CallVC.swift index 482344033..6b57e9ec1 100644 --- a/Session/Calls/CallVC.swift +++ b/Session/Calls/CallVC.swift @@ -4,7 +4,7 @@ import SessionMessagingKit import SessionUtilitiesKit import UIKit -final class CallVC : UIViewController, WebRTCSessionDelegate { +final class CallVC : UIViewController, WebRTCSessionDelegate, VideoPreviewDelegate { let sessionID: String let uuid: String let mode: Mode @@ -225,7 +225,7 @@ final class CallVC : UIViewController, WebRTCSessionDelegate { view.addSubview(fadeView) fadeView.translatesAutoresizingMaskIntoConstraints = false fadeView.pin([ UIView.HorizontalEdge.left, UIView.VerticalEdge.top, UIView.HorizontalEdge.right ], to: view) - // Close button + // Minimize button view.addSubview(minimizeButton) minimizeButton.translatesAutoresizingMaskIntoConstraints = false minimizeButton.pin(.left, to: .left, of: view) @@ -354,15 +354,22 @@ final class CallVC : UIViewController, WebRTCSessionDelegate { cameraManager.stop() videoButton.alpha = 0.5 switchCameraButton.isEnabled = false + isVideoEnabled = false } else { - webRTCSession.turnOnVideo() - localVideoView.isHidden = false - cameraManager.prepare() - cameraManager.start() - videoButton.alpha = 1.0 - switchCameraButton.isEnabled = true + let previewVC = VideoPreviewVC() + previewVC.delegate = self + present(previewVC, animated: true, completion: nil) } - isVideoEnabled = !isVideoEnabled + } + + func cameraDidConfirmTurningOn() { + webRTCSession.turnOnVideo() + localVideoView.isHidden = false + cameraManager.prepare() + cameraManager.start() + videoButton.alpha = 1.0 + switchCameraButton.isEnabled = true + isVideoEnabled = true } @objc private func switchCamera() { diff --git a/Session/Calls/VideoPreviewVC.swift b/Session/Calls/VideoPreviewVC.swift new file mode 100644 index 000000000..df6e98377 --- /dev/null +++ b/Session/Calls/VideoPreviewVC.swift @@ -0,0 +1,123 @@ +import UIKit +import WebRTC + +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: UIView = { + let result = UIView() + let height: CGFloat = 64 + var frame = UIScreen.main.bounds + frame.size.height = height + let layer = CAGradientLayer() + layer.frame = frame + layer.colors = [ UIColor(hex: 0x000000).withAlphaComponent(0.4).cgColor, UIColor(hex: 0x000000).withAlphaComponent(0).cgColor ] + result.layer.insertSublayer(layer, at: 0) + result.set(.height, to: height) + return result + }() + + private lazy var closeButton: UIButton = { + let result = UIButton(type: .custom) + let image = UIImage(named: "X")!.withTint(.white) + result.setImage(image, for: UIControl.State.normal) + result.set(.width, to: 60) + result.set(.height, to: 60) + result.addTarget(self, action: #selector(cancel), for: UIControl.Event.touchUpInside) + return result + }() + + private lazy var confirmButton: UIButton = { + let result = UIButton(type: .custom) + let image = UIImage(named: "Check")!.withTint(.white) + result.setImage(image, for: UIControl.State.normal) + result.set(.width, to: 60) + result.set(.height, to: 60) + result.addTarget(self, action: #selector(confirm), for: UIControl.Event.touchUpInside) + return result + }() + + private lazy var titleLabel: UILabel = { + let result = UILabel() + result.text = "Preview" + result.textColor = .white + result.font = .boldSystemFont(ofSize: Values.veryLargeFontSize) + result.textAlignment = .center + return result + }() + + // MARK: Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + view.backgroundColor = .black + 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) + } +} diff --git a/Session/Calls/Views & Modals/RenderView.swift b/Session/Calls/Views & Modals/RenderView.swift new file mode 100644 index 000000000..da2bd4c56 --- /dev/null +++ b/Session/Calls/Views & Modals/RenderView.swift @@ -0,0 +1,36 @@ +// Copyright © 2021 Rangeproof Pty Ltd. All rights reserved. + +import UIKit +import CoreMedia + +class RenderView: UIView { + + private lazy var displayLayer: AVSampleBufferDisplayLayer = { + let result = AVSampleBufferDisplayLayer() + result.videoGravity = .resizeAspectFill + return result + }() + + init() { + super.init(frame: CGRect.zero) + self.layer.addSublayer(displayLayer) + } + + override init(frame: CGRect) { + preconditionFailure("Use init(message:) instead.") + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(coder:) instead.") + } + + override func layoutSubviews() { + super.layoutSubviews() + displayLayer.frame = self.bounds + } + + public func enqueue(sampleBuffer: CMSampleBuffer) { + displayLayer.enqueue(sampleBuffer) + } + +} diff --git a/Session/Conversations/ConversationVC+Interaction.swift b/Session/Conversations/ConversationVC+Interaction.swift index faad7c3be..8cfee86bb 100644 --- a/Session/Conversations/ConversationVC+Interaction.swift +++ b/Session/Conversations/ConversationVC+Interaction.swift @@ -27,7 +27,7 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc } // MARK: Call - @objc func startCall(_ sender: Any) { + @objc func startCall(_ sender: Any?) { guard let contactSessionID = (thread as? TSContactThread)?.contactSessionID() else { return } let callVC = CallVC(for: contactSessionID, uuid: UUID().uuidString, mode: .offer) callVC.conversationVC = self @@ -38,6 +38,15 @@ extension ConversationVC : InputViewDelegate, MessageCellDelegate, ContextMenuAc present(callVC, animated: true, completion: nil) } + internal func showCallModal() { + let callModal = CallModal() { [weak self] in + self?.startCall(nil) + } + callModal.modalPresentationStyle = .overFullScreen + callModal.modalTransitionStyle = .crossDissolve + present(callModal, animated: true, completion: nil) + } + internal func showCallVCIfNeeded() { guard let contactSessionID = (thread as? TSContactThread)?.contactSessionID(), let incomingCallBanner = IncomingCallBanner.current, incomingCallBanner.sessionID == contactSessionID diff --git a/Session/Conversations/Views & Modals/CallModal.swift b/Session/Conversations/Views & Modals/CallModal.swift new file mode 100644 index 000000000..0620646e7 --- /dev/null +++ b/Session/Conversations/Views & Modals/CallModal.swift @@ -0,0 +1,65 @@ + +final class CallModal : Modal { + private let onCallEnabled: () -> Void + + // MARK: Lifecycle + init(onCallEnabled: @escaping () -> Void) { + self.onCallEnabled = onCallEnabled + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + preconditionFailure("Use init(onCallEnabled:) instead.") + } + + override init(nibName: String?, bundle: Bundle?) { + preconditionFailure("Use init(onCallEnabled:) instead.") + } + + override func populateContentView() { + // Title + let titleLabel = UILabel() + titleLabel.textColor = Colors.text + titleLabel.font = .boldSystemFont(ofSize: Values.largeFontSize) + titleLabel.text = NSLocalizedString("modal_call_title", comment: "") + titleLabel.textAlignment = .center + // Message + let messageLabel = UILabel() + messageLabel.textColor = Colors.text + messageLabel.font = .systemFont(ofSize: Values.smallFontSize) + let message = NSLocalizedString("modal_call_explanation", comment: "") + messageLabel.text = message + messageLabel.numberOfLines = 0 + messageLabel.lineBreakMode = .byWordWrapping + messageLabel.textAlignment = .center + // Enable button + let enableButton = UIButton() + enableButton.set(.height, to: Values.mediumButtonHeight) + enableButton.layer.cornerRadius = Modal.buttonCornerRadius + enableButton.backgroundColor = Colors.buttonBackground + enableButton.titleLabel!.font = .systemFont(ofSize: Values.smallFontSize) + enableButton.setTitleColor(Colors.text, for: UIControl.State.normal) + enableButton.setTitle(NSLocalizedString("modal_link_previews_button_title", comment: ""), for: UIControl.State.normal) + enableButton.addTarget(self, action: #selector(enable), for: UIControl.Event.touchUpInside) + // Button stack view + let buttonStackView = UIStackView(arrangedSubviews: [ cancelButton, enableButton ]) + buttonStackView.axis = .horizontal + buttonStackView.spacing = Values.mediumSpacing + buttonStackView.distribution = .fillEqually + // Main stack view + let mainStackView = UIStackView(arrangedSubviews: [ titleLabel, messageLabel, buttonStackView ]) + mainStackView.axis = .vertical + mainStackView.spacing = Values.largeSpacing + contentView.addSubview(mainStackView) + mainStackView.pin(.leading, to: .leading, of: contentView, withInset: Values.largeSpacing) + mainStackView.pin(.top, to: .top, of: contentView, withInset: Values.largeSpacing) + contentView.pin(.trailing, to: .trailing, of: mainStackView, withInset: Values.largeSpacing) + contentView.pin(.bottom, to: .bottom, of: mainStackView, withInset: Values.largeSpacing) + } + + // MARK: Interaction + @objc private func enable() { + presentingViewController?.dismiss(animated: true, completion: nil) + onCallEnabled() + } +} diff --git a/Session/Meta/Images.xcassets/Session/Check.imageset/Contents.json b/Session/Meta/Images.xcassets/Session/Check.imageset/Contents.json new file mode 100644 index 000000000..37accc538 --- /dev/null +++ b/Session/Meta/Images.xcassets/Session/Check.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "check.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Session/Meta/Images.xcassets/Session/Check.imageset/check.pdf b/Session/Meta/Images.xcassets/Session/Check.imageset/check.pdf new file mode 100644 index 0000000000000000000000000000000000000000..86c5f39ce77bc5ceddc8c0ae7f6f94aadec44420 GIT binary patch literal 4092 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 literal 0 HcmV?d00001 diff --git a/Session/Meta/Translations/en.lproj/Localizable.strings b/Session/Meta/Translations/en.lproj/Localizable.strings index 6861f3c03..6316ecd13 100644 --- a/Session/Meta/Translations/en.lproj/Localizable.strings +++ b/Session/Meta/Translations/en.lproj/Localizable.strings @@ -538,6 +538,8 @@ "modal_link_previews_title" = "Enable Link Previews?"; "modal_link_previews_explanation" = "Enabling link previews will show previews for URLs you send and receive. This can be useful, but Session will need to contact linked websites to generate previews. You can always disable link previews in Session's settings."; "modal_link_previews_button_title" = "Enable"; +"modal_call_title" = "Voice and video calls"; +"modal_call_explanation" = "The current implementation of voice/video calls will expose your IP address to the Oxen Foundation servers and the calling/called user."; "modal_share_logs_title" = "Share Logs"; "modal_share_logs_explanation" = "Would you like to export your application logs to be able to share for troubleshooting?"; "vc_share_title" = "Share to Session";