|
|
@ -8,6 +8,8 @@ import MediaPlayer
|
|
|
|
final class CallVC : UIViewController, VideoPreviewDelegate {
|
|
|
|
final class CallVC : UIViewController, VideoPreviewDelegate {
|
|
|
|
let call: SessionCall
|
|
|
|
let call: SessionCall
|
|
|
|
var latestKnownAudioOutputDeviceName: String?
|
|
|
|
var latestKnownAudioOutputDeviceName: String?
|
|
|
|
|
|
|
|
var durationTimer: Timer?
|
|
|
|
|
|
|
|
var duration: Int = -1
|
|
|
|
var shouldRestartCamera = true
|
|
|
|
var shouldRestartCamera = true
|
|
|
|
weak var conversationVC: ConversationVC? = nil
|
|
|
|
weak var conversationVC: ConversationVC? = nil
|
|
|
|
|
|
|
|
|
|
|
@ -48,6 +50,17 @@ final class CallVC : UIViewController, VideoPreviewDelegate {
|
|
|
|
return result
|
|
|
|
return result
|
|
|
|
}()
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private lazy var profilePictureView: UIImageView = {
|
|
|
|
|
|
|
|
let result = UIImageView()
|
|
|
|
|
|
|
|
result.image = self.call.profilePicture
|
|
|
|
|
|
|
|
result.set(.width, to: 240)
|
|
|
|
|
|
|
|
result.set(.height, to: 240)
|
|
|
|
|
|
|
|
result.layer.cornerRadius = 120
|
|
|
|
|
|
|
|
result.layer.masksToBounds = true
|
|
|
|
|
|
|
|
result.contentMode = .scaleAspectFill
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
private lazy var minimizeButton: UIButton = {
|
|
|
|
private lazy var minimizeButton: UIButton = {
|
|
|
|
let result = UIButton(type: .custom)
|
|
|
|
let result = UIButton(type: .custom)
|
|
|
|
result.isHidden = !call.hasConnected
|
|
|
|
result.isHidden = !call.hasConnected
|
|
|
@ -199,16 +212,15 @@ final class CallVC : UIViewController, VideoPreviewDelegate {
|
|
|
|
CallRingTonePlayer.shared.stopPlayingRingTone()
|
|
|
|
CallRingTonePlayer.shared.stopPlayingRingTone()
|
|
|
|
self.callInfoLabel.text = "Connected"
|
|
|
|
self.callInfoLabel.text = "Connected"
|
|
|
|
self.minimizeButton.isHidden = false
|
|
|
|
self.minimizeButton.isHidden = false
|
|
|
|
UIView.animate(withDuration: 0.5, delay: 1, options: [], animations: {
|
|
|
|
self.durationTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { _ in
|
|
|
|
self.callInfoLabel.alpha = 0
|
|
|
|
self.updateDuration()
|
|
|
|
}, completion: { _ in
|
|
|
|
}
|
|
|
|
self.callInfoLabel.isHidden = true
|
|
|
|
|
|
|
|
self.callInfoLabel.alpha = 1
|
|
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.call.hasEndedDidChange = {
|
|
|
|
self.call.hasEndedDidChange = {
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
DispatchQueue.main.async {
|
|
|
|
|
|
|
|
self.durationTimer?.invalidate()
|
|
|
|
|
|
|
|
self.durationTimer = nil
|
|
|
|
self.handleEndCallMessage()
|
|
|
|
self.handleEndCallMessage()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -242,14 +254,9 @@ final class CallVC : UIViewController, VideoPreviewDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func setUpViewHierarchy() {
|
|
|
|
func setUpViewHierarchy() {
|
|
|
|
// Background
|
|
|
|
// Profile picture container
|
|
|
|
let background = getBackgroudView()
|
|
|
|
let profilePictureContainer = UIView()
|
|
|
|
view.addSubview(background)
|
|
|
|
view.addSubview(profilePictureContainer)
|
|
|
|
background.pin(to: view)
|
|
|
|
|
|
|
|
// Call info label
|
|
|
|
|
|
|
|
view.addSubview(callInfoLabel)
|
|
|
|
|
|
|
|
callInfoLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
|
|
|
|
callInfoLabel.center(in: view)
|
|
|
|
|
|
|
|
// Remote video view
|
|
|
|
// Remote video view
|
|
|
|
call.attachRemoteVideoRenderer(remoteVideoView)
|
|
|
|
call.attachRemoteVideoRenderer(remoteVideoView)
|
|
|
|
view.addSubview(remoteVideoView)
|
|
|
|
view.addSubview(remoteVideoView)
|
|
|
@ -283,25 +290,21 @@ final class CallVC : UIViewController, VideoPreviewDelegate {
|
|
|
|
view.addSubview(operationPanel)
|
|
|
|
view.addSubview(operationPanel)
|
|
|
|
operationPanel.center(.horizontal, in: view)
|
|
|
|
operationPanel.center(.horizontal, in: view)
|
|
|
|
operationPanel.pin(.bottom, to: .top, of: responsePanel, withInset: -Values.veryLargeSpacing)
|
|
|
|
operationPanel.pin(.bottom, to: .top, of: responsePanel, withInset: -Values.veryLargeSpacing)
|
|
|
|
}
|
|
|
|
// Profile picture view
|
|
|
|
|
|
|
|
profilePictureContainer.pin(.top, to: .bottom, of: fadeView)
|
|
|
|
private func getBackgroudView() -> UIView {
|
|
|
|
profilePictureContainer.pin(.bottom, to: .top, of: operationPanel)
|
|
|
|
let background = UIView()
|
|
|
|
profilePictureContainer.pin([ UIView.HorizontalEdge.left, UIView.HorizontalEdge.right ], to: view)
|
|
|
|
let imageView = UIImageView()
|
|
|
|
profilePictureContainer.addSubview(profilePictureView)
|
|
|
|
imageView.layer.cornerRadius = 150
|
|
|
|
profilePictureView.center(in: profilePictureContainer)
|
|
|
|
imageView.layer.masksToBounds = true
|
|
|
|
// Call info label
|
|
|
|
imageView.contentMode = .scaleAspectFill
|
|
|
|
let callInfoLabelContainer = UIView()
|
|
|
|
imageView.image = self.call.profilePicture
|
|
|
|
view.addSubview(callInfoLabelContainer)
|
|
|
|
background.addSubview(imageView)
|
|
|
|
callInfoLabelContainer.pin(.top, to: .bottom, of: profilePictureView)
|
|
|
|
imageView.set(.width, to: 300)
|
|
|
|
callInfoLabelContainer.pin(.bottom, to: .bottom, of: profilePictureContainer)
|
|
|
|
imageView.set(.height, to: 300)
|
|
|
|
callInfoLabelContainer.pin([ UIView.HorizontalEdge.left, UIView.HorizontalEdge.right ], to: view)
|
|
|
|
imageView.center(in: background)
|
|
|
|
callInfoLabelContainer.addSubview(callInfoLabel)
|
|
|
|
let blurView = UIView()
|
|
|
|
callInfoLabel.translatesAutoresizingMaskIntoConstraints = false
|
|
|
|
blurView.alpha = 0.5
|
|
|
|
callInfoLabel.center(in: callInfoLabelContainer)
|
|
|
|
blurView.backgroundColor = .black
|
|
|
|
|
|
|
|
background.addSubview(blurView)
|
|
|
|
|
|
|
|
blurView.autoPinEdgesToSuperviewEdges()
|
|
|
|
|
|
|
|
return background
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
|
|
override func viewDidAppear(_ animated: Bool) {
|
|
|
@ -322,7 +325,6 @@ final class CallVC : UIViewController, VideoPreviewDelegate {
|
|
|
|
|
|
|
|
|
|
|
|
func handleEndCallMessage() {
|
|
|
|
func handleEndCallMessage() {
|
|
|
|
print("[Calls] Ending call.")
|
|
|
|
print("[Calls] Ending call.")
|
|
|
|
callInfoLabel.isHidden = false
|
|
|
|
|
|
|
|
callInfoLabel.text = "Call Ended"
|
|
|
|
callInfoLabel.text = "Call Ended"
|
|
|
|
UIView.animate(withDuration: 0.25) {
|
|
|
|
UIView.animate(withDuration: 0.25) {
|
|
|
|
self.remoteVideoView.alpha = 0
|
|
|
|
self.remoteVideoView.alpha = 0
|
|
|
@ -357,6 +359,11 @@ final class CallVC : UIViewController, VideoPreviewDelegate {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@objc private func updateDuration() {
|
|
|
|
|
|
|
|
duration += 1
|
|
|
|
|
|
|
|
callInfoLabel.text = String(format: "%.2d:%.2d", duration/60, duration%60)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: Minimize to a floating view
|
|
|
|
// MARK: Minimize to a floating view
|
|
|
|
@objc private func minimize() {
|
|
|
|
@objc private func minimize() {
|
|
|
|
self.shouldRestartCamera = false
|
|
|
|
self.shouldRestartCamera = false
|
|
|
@ -414,11 +421,6 @@ final class CallVC : UIViewController, VideoPreviewDelegate {
|
|
|
|
if let latestKnownAudioOutputDeviceName = latestKnownAudioOutputDeviceName, currentOutput.portName == latestKnownAudioOutputDeviceName { return }
|
|
|
|
if let latestKnownAudioOutputDeviceName = latestKnownAudioOutputDeviceName, currentOutput.portName == latestKnownAudioOutputDeviceName { return }
|
|
|
|
latestKnownAudioOutputDeviceName = currentOutput.portName
|
|
|
|
latestKnownAudioOutputDeviceName = currentOutput.portName
|
|
|
|
switch currentOutput.portType {
|
|
|
|
switch currentOutput.portType {
|
|
|
|
case .builtInReceiver:
|
|
|
|
|
|
|
|
let image = UIImage(named: "Speaker")?.withRenderingMode(.alwaysTemplate)
|
|
|
|
|
|
|
|
volumeView.setRouteButtonImage(image, for: .normal)
|
|
|
|
|
|
|
|
volumeView.tintColor = .white
|
|
|
|
|
|
|
|
volumeView.backgroundColor = UIColor(hex: 0x1F1F1F)
|
|
|
|
|
|
|
|
case .builtInSpeaker:
|
|
|
|
case .builtInSpeaker:
|
|
|
|
let image = UIImage(named: "Speaker")?.withRenderingMode(.alwaysTemplate)
|
|
|
|
let image = UIImage(named: "Speaker")?.withRenderingMode(.alwaysTemplate)
|
|
|
|
volumeView.setRouteButtonImage(image, for: .normal)
|
|
|
|
volumeView.setRouteButtonImage(image, for: .normal)
|
|
|
@ -440,11 +442,14 @@ final class CallVC : UIViewController, VideoPreviewDelegate {
|
|
|
|
volumeView.setRouteButtonImage(image, for: .normal)
|
|
|
|
volumeView.setRouteButtonImage(image, for: .normal)
|
|
|
|
volumeView.tintColor = UIColor(hex: 0x1F1F1F)
|
|
|
|
volumeView.tintColor = UIColor(hex: 0x1F1F1F)
|
|
|
|
volumeView.backgroundColor = .white
|
|
|
|
volumeView.backgroundColor = .white
|
|
|
|
|
|
|
|
case .builtInReceiver: fallthrough
|
|
|
|
default:
|
|
|
|
default:
|
|
|
|
break
|
|
|
|
let image = UIImage(named: "Speaker")?.withRenderingMode(.alwaysTemplate)
|
|
|
|
|
|
|
|
volumeView.setRouteButtonImage(image, for: .normal)
|
|
|
|
|
|
|
|
volumeView.tintColor = .white
|
|
|
|
|
|
|
|
volumeView.backgroundColor = UIColor(hex: 0x1F1F1F)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
print(currentRoute.outputs)
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// MARK: Pan gesture handling
|
|
|
|
// MARK: Pan gesture handling
|
|
|
|