220 lines
7.3 KiB
Swift
220 lines
7.3 KiB
Swift
//
|
|
// Copyright (c) 2021 Open Whisper Systems. All rights reserved.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
@objc
|
|
public protocol CallViewControllerWindowReference: AnyObject {
|
|
var localVideoViewReference: UIView { get }
|
|
var remoteVideoViewReference: UIView { get }
|
|
var remoteVideoAddress: String { get }
|
|
var view: UIView! { get }
|
|
|
|
func returnFromPip(pipWindow: UIWindow)
|
|
}
|
|
|
|
@objc
|
|
public class ReturnToCallViewController: UIViewController {
|
|
|
|
@objc
|
|
public static var pipSize: CGSize {
|
|
let nineBySixteen = CGSize(width: 90, height: 160)
|
|
let fourByThree = CGSize(width: 272, height: 204)
|
|
let threeByFour = CGSize(width: 204, height: 272)
|
|
|
|
if UIDevice.current.isIPad && UIDevice.current.isFullScreen {
|
|
if CurrentAppContext().frame.size.width > CurrentAppContext().frame.size.height {
|
|
return fourByThree
|
|
} else {
|
|
return threeByFour
|
|
}
|
|
} else {
|
|
return nineBySixteen
|
|
}
|
|
}
|
|
|
|
/*
|
|
private weak var callViewController: CallViewControllerWindowReference?
|
|
|
|
@objc
|
|
public func displayForCallViewController(_ callViewController: CallViewControllerWindowReference) {
|
|
guard callViewController !== self.callViewController else { return }
|
|
|
|
guard let callViewSnapshot = callViewController.view.snapshotView(afterScreenUpdates: false) else {
|
|
return owsFailDebug("failed to snapshot call view")
|
|
}
|
|
|
|
self.callViewController = callViewController
|
|
|
|
view.addSubview(callViewController.remoteVideoViewReference)
|
|
callViewController.remoteVideoViewReference.autoPinEdgesToSuperviewEdges()
|
|
|
|
view.addSubview(callViewController.localVideoViewReference)
|
|
callViewController.localVideoViewReference.layer.cornerRadius = 6
|
|
updateLocalVideoFrame()
|
|
|
|
let profileImage = databaseStorage.read { transaction -> UIImage? in
|
|
avatarView.configure(address: callViewController.remoteVideoAddress, transaction: transaction)
|
|
|
|
return self.profileManagerImpl.profileAvatar(for: callViewController.remoteVideoAddress,
|
|
transaction: transaction)
|
|
}
|
|
|
|
backgroundAvatarView.image = profileImage
|
|
|
|
animatePipPresentation(snapshot: callViewSnapshot)
|
|
}
|
|
|
|
@objc
|
|
public func resignCall() {
|
|
callViewController?.localVideoViewReference.removeFromSuperview()
|
|
callViewController?.remoteVideoViewReference.removeFromSuperview()
|
|
callViewController = nil
|
|
}
|
|
|
|
private lazy var avatarView = ConversationAvatarView(diameterPoints: 60,
|
|
localUserDisplayMode: .asUser)
|
|
private lazy var backgroundAvatarView = UIImageView()
|
|
private lazy var blurView = UIVisualEffectView(effect: UIBlurEffect(style: .dark))
|
|
|
|
override public func loadView() {
|
|
view = UIView()
|
|
|
|
view.backgroundColor = .black
|
|
view.clipsToBounds = true
|
|
view.layer.cornerRadius = 8
|
|
|
|
backgroundAvatarView.contentMode = .scaleAspectFill
|
|
view.addSubview(backgroundAvatarView)
|
|
backgroundAvatarView.autoPinEdgesToSuperviewEdges()
|
|
|
|
view.addSubview(blurView)
|
|
blurView.autoPinEdgesToSuperviewEdges()
|
|
|
|
view.addSubview(avatarView)
|
|
avatarView.autoCenterInSuperview()
|
|
|
|
view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(handleTap)))
|
|
view.addGestureRecognizer(UIPanGestureRecognizer(target: self, action: #selector(handlePan)))
|
|
}
|
|
|
|
public override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
|
|
super.viewWillTransition(to: size, with: coordinator)
|
|
coordinator.animate(alongsideTransition: { [weak self] _ in
|
|
self?.updatePipLayout()
|
|
}, completion: nil)
|
|
}
|
|
|
|
public override func viewWillLayoutSubviews() {
|
|
super.viewWillLayoutSubviews()
|
|
updatePipLayout()
|
|
}
|
|
|
|
// MARK: -
|
|
|
|
private func updateLocalVideoFrame() {
|
|
let localVideoSize = CGSizeScale(Self.pipSize, 0.3)
|
|
callViewController?.localVideoViewReference.frame = CGRect(
|
|
origin: CGPoint(
|
|
x: Self.pipSize.width - 6 - localVideoSize.width,
|
|
y: Self.pipSize.height - 6 - localVideoSize.height
|
|
),
|
|
size: localVideoSize
|
|
)
|
|
}
|
|
|
|
private var isAnimating = false
|
|
private func animatePipPresentation(snapshot: UIView) {
|
|
guard let window = view.window else { return owsFailDebug("missing window") }
|
|
|
|
isAnimating = true
|
|
|
|
let previousOrigin = window.frame.origin
|
|
window.frame = OWSWindowManager.shared.rootWindow.bounds
|
|
|
|
view.addSubview(snapshot)
|
|
snapshot.autoPinEdgesToSuperviewEdges()
|
|
|
|
window.layoutIfNeeded()
|
|
|
|
UIView.animate(withDuration: 0.2, animations: {
|
|
snapshot.alpha = 0
|
|
window.frame = CGRect(
|
|
origin: previousOrigin,
|
|
size: Self.pipSize
|
|
).pinnedToVerticalEdge(of: self.pipBoundingRect)
|
|
window.layoutIfNeeded()
|
|
}) { _ in
|
|
snapshot.removeFromSuperview()
|
|
self.isAnimating = false
|
|
}
|
|
}
|
|
|
|
private var pipBoundingRect: CGRect {
|
|
let padding: CGFloat = 4
|
|
var rect = CurrentAppContext().frame
|
|
|
|
let safeAreaInsets = OWSWindowManager.shared.rootWindow.safeAreaInsets
|
|
|
|
let leftInset = safeAreaInsets.left + padding
|
|
let rightInset = safeAreaInsets.right + padding
|
|
rect.origin.x += leftInset
|
|
rect.size.width -= leftInset + rightInset
|
|
|
|
let topInset = safeAreaInsets.top + padding
|
|
let bottomInset = safeAreaInsets.bottom + padding
|
|
rect.origin.y += topInset
|
|
rect.size.height -= topInset + bottomInset
|
|
|
|
return rect
|
|
}
|
|
|
|
private func updatePipLayout() {
|
|
guard !isAnimating else { return }
|
|
guard let window = view.window else { return owsFailDebug("missing window") }
|
|
let newFrame = CGRect(
|
|
origin: window.frame.origin,
|
|
size: Self.pipSize
|
|
).pinnedToVerticalEdge(of: pipBoundingRect)
|
|
UIView.animate(withDuration: 0.25) {
|
|
self.updateLocalVideoFrame()
|
|
window.frame = newFrame
|
|
}
|
|
}
|
|
|
|
@objc func handlePan(sender: UIPanGestureRecognizer) {
|
|
guard let window = view.window else { return owsFailDebug("missing window") }
|
|
|
|
switch sender.state {
|
|
case .began, .changed:
|
|
let translation = sender.translation(in: window)
|
|
sender.setTranslation(.zero, in: window)
|
|
|
|
window.frame.origin.y += translation.y
|
|
window.frame.origin.x += translation.x
|
|
case .ended, .cancelled, .failed:
|
|
window.animateDecelerationToVerticalEdge(
|
|
withDuration: 0.35,
|
|
velocity: sender.velocity(in: window),
|
|
boundingRect: pipBoundingRect
|
|
)
|
|
default:
|
|
break
|
|
}
|
|
}
|
|
|
|
@objc
|
|
private func handleTap(sender: UITapGestureRecognizer) {
|
|
OWSWindowManager.shared.returnToCallView()
|
|
}
|
|
|
|
// MARK: Orientation
|
|
|
|
public override var supportedInterfaceOrientations: UIInterfaceOrientationMask {
|
|
return UIDevice.current.isIPad ? .all : .portrait
|
|
}
|
|
|
|
*/
|
|
}
|