CallViewController.swift 26.9 KB
Newer Older
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
1
/*
Quentin Muret's avatar
Quentin Muret committed
2
 *  Copyright (C) 2017-2018 Savoir-faire Linux Inc.
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
3 4 5
 *
 *  Author: Silbino Gonçalves Matado <silbino.gmatado@savoirfairelinux.com>
 *  Author: Kateryna Kostiuk <kateryna.kostiuk@savoirfairelinux.com>
Quentin Muret's avatar
Quentin Muret committed
6
 *  Author: Quentin Muret <quentin.muret@gmail.com>
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.
 */

import UIKit
import Chameleon
import RxSwift
import Reusable
27
import SwiftyBeaver
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
28

29
// swiftlint:disable type_body_length
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
30 31
class CallViewController: UIViewController, StoryboardBased, ViewModelBased {

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
32 33 34 35 36
    //preview screen
    @IBOutlet private weak var profileImageView: UIImageView!
    @IBOutlet private weak var nameLabel: UILabel!
    @IBOutlet private weak var durationLabel: UILabel!
    @IBOutlet private weak var infoBottomLabel: UILabel!
37
    @IBOutlet weak var avatarView: UIView!
38 39
    @IBOutlet weak var avatarViewBlurEffect: UIVisualEffectView!
    @IBOutlet private weak var callPulse: UIView!
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
40 41 42 43 44 45

    @IBOutlet private weak var mainView: UIView!

    //video screen
    @IBOutlet private weak var callView: UIView!
    @IBOutlet private weak var incomingVideo: UIImageView!
46
    @IBOutlet weak var beforeIncomingVideo: UIView!
47 48
    @IBOutlet weak var spinner: UIActivityIndicatorView!
    @IBOutlet weak var capturedVideo: UIImageView!
49 50 51 52 53 54 55 56
    @IBOutlet weak var capturedVideoBlurEffect: UIVisualEffectView!
    @IBOutlet weak var viewCapturedVideo: UIView!
    @IBOutlet private weak var infoContainer: UIView!
    @IBOutlet private weak var callProfileImage: UIImageView!
    @IBOutlet private weak var callNameLabel: UILabel!
    @IBOutlet private weak var callInfoTimerLabel: UILabel!
    @IBOutlet private weak var buttonsContainer: ButtonsContainerView!
    @IBOutlet weak var infoBlurEffect: UIVisualEffectView!
57
    @IBOutlet weak var leftArrow: UIImageView!
58 59

    //Constraints
60 61 62 63
    @IBOutlet weak var capturedVideoWidthConstraint: NSLayoutConstraint!
    @IBOutlet weak var capturedVideoTrailingConstraint: NSLayoutConstraint!
    @IBOutlet weak var capturedVideoTopConstraint: NSLayoutConstraint!
    @IBOutlet weak var capturedVideoHeightConstraint: NSLayoutConstraint!
64 65
    @IBOutlet weak var infoContainerTopConstraint: NSLayoutConstraint!
    @IBOutlet weak var buttonsContainerBottomConstraint: NSLayoutConstraint!
66
    @IBOutlet weak var infoContainerHeightConstraint: NSLayoutConstraint!
67 68 69 70
    @IBOutlet weak var buttonContainerHeightConstraint: NSLayoutConstraint!
    @IBOutlet weak var avatarViewImageTopConstraint: NSLayoutConstraint!
    @IBOutlet weak var profileImageViewWidthConstraint: NSLayoutConstraint!
    @IBOutlet weak var profileImageViewHeightConstraint: NSLayoutConstraint!
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
71

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
72 73

    var viewModel: CallViewModel!
74
    var isCallStarted: Bool = false
75 76
    var isMenuShowed = false
    var isVideoHidden = false
77
    var orientation = UIDevice.current.orientation
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
78 79 80

    fileprivate let disposeBag = DisposeBag()

81 82
    private let log = SwiftyBeaver.self

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
83 84
    private var task: DispatchWorkItem?

85 86
    private var shouldRotateScreen = false

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
87 88
    override func viewDidLoad() {
        super.viewDidLoad()
89
        self.setAvatarView(true)
90
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(screenTapped))
91 92 93 94 95 96 97 98
        let tapCapturedVideo = UITapGestureRecognizer(target: self, action: #selector(hideCapturedVideo))
        let swipeLeftCapturedVideo = UISwipeGestureRecognizer(target: self, action: #selector(capturedVideoSwipped(gesture:)))
        swipeLeftCapturedVideo.direction = .left
        let swipeRightCapturedVideo = UISwipeGestureRecognizer(target: self, action: #selector(capturedVideoSwipped(gesture:)))
        swipeRightCapturedVideo.direction = .right
        self.viewCapturedVideo.addGestureRecognizer(tapCapturedVideo)
        self.viewCapturedVideo.addGestureRecognizer(swipeLeftCapturedVideo)
        self.viewCapturedVideo.addGestureRecognizer(swipeRightCapturedVideo)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
99
        self.mainView.addGestureRecognizer(tapGestureRecognizer)
100 101
        self.setUpCallButtons()
        self.setupBindings()
102
        let device = UIDevice.modelName
Quentin Muret's avatar
Quentin Muret committed
103
        self.profileImageView.tintColor = UIColor.jamiDefaultAvatar
104 105 106 107 108 109 110
        switch device {
        case "iPhone X", "iPhone XS", "iPhone XS Max", "iPhone XR" :
            //keep the 4:3 format of the captured video on iPhone X and later when display it in full screen
            if !self.avatarView.isHidden {
                self.capturedVideoWidthConstraint.constant += 200
                self.capturedVideoTrailingConstraint.constant = (self.capturedVideoWidthConstraint.constant - UIScreen.main.bounds.width) / 2
            }
111
        default : break
112
        }
113
        if self.viewModel.isAudioOnly {
114 115 116
            // The durationLabel and buttonsContainer alpha is set here to 0, and to 1 (with a duration) when appear on the screen to have a fade in animation
            self.durationLabel.alpha = 0
            self.buttonsContainer.stackView.alpha = 0
117
            self.showAllInfo()
118 119
            self.setWhiteAvatarView()
        } else {
120
            //The status bar should be white for video calls and black for audio calls
121
            UIApplication.shared.statusBarStyle = .lightContent
122
        }
123

124
        UIDevice.current.isProximityMonitoringEnabled = self.viewModel.isAudioOnly
125 126 127 128

        initCallAnimation()
    }

129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
    @objc func capturedVideoSwipped(gesture: UISwipeGestureRecognizer) {
        if self.avatarView.isHidden == false { return }
        if gesture.direction == UISwipeGestureRecognizerDirection.left && (self.isVideoHidden == false) { return }
        if gesture.direction == UISwipeGestureRecognizerDirection.right && (self.isVideoHidden == true) { return }
        self.hideCapturedVideo()
    }

    @objc func hideCapturedVideo() {
        if self.isMenuShowed { return }
        UIView.animate(withDuration: 0.3, animations: {
            if self.capturedVideoBlurEffect.alpha == 0 {
                self.isVideoHidden = true
                self.capturedVideoBlurEffect.alpha = 1
            } else {
                self.isVideoHidden = false
                self.capturedVideoBlurEffect.alpha = 0
            }
            self.resizeCapturedVideo(withInfoContainer: !self.infoContainer.isHidden)
            self.view.layoutIfNeeded()
        })
149 150 151
    }

    func setWhiteAvatarView() {
152
        UIApplication.shared.statusBarStyle = .default
Quentin Muret's avatar
Quentin Muret committed
153
        self.callPulse.backgroundColor = UIColor.jamiCallPulse
154
        self.avatarView.backgroundColor = UIColor.white
155 156
    }

157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
    func initCallAnimation() {
        self.callPulse.alpha = 0.5
        self.callPulse.layer.cornerRadius = self.callPulse.frame.size.width / 2
        animateCallCircle()
    }

    func animateCallCircle() {
        self.callPulse.alpha = 0.5
        self.callPulse.transform = CGAffineTransform(scaleX: 1.0, y: 1.0)
        UIView.animate(withDuration: 1.5, animations: {
            self.callPulse.alpha = 0.0
            self.callPulse.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
            self.view.layoutIfNeeded()
        }, completion: { [unowned self] _ in
            if self.viewModel.call?.state == .ringing || self.viewModel.call?.state == .connecting {
                self.animateCallCircle()
            }
        })
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
175 176
    }

177 178 179
    func setUpCallButtons() {
        self.buttonsContainer.viewModel = self.viewModel.containerViewModel
        self.buttonsContainer.cancelButton.rx.tap
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
180
            .subscribe(onNext: { [weak self] in
181 182 183
                self?.removeFromScreen()
                self?.viewModel.cancelCall()
            }).disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
184

185
        self.buttonsContainer.muteAudioButton.rx.tap
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
186
            .subscribe(onNext: { [weak self] in
187
                self?.viewModel.toggleMuteAudio()
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
188 189
            }).disposed(by: self.disposeBag)

190 191 192 193 194 195 196 197
        if !(self.viewModel.call?.isAudioOnly ?? false) {
            self.buttonsContainer.muteVideoButton.rx.tap
                .subscribe(onNext: { [weak self] in
                    self?.viewModel.toggleMuteVideo()
                }).disposed(by: self.disposeBag)
        }

        self.buttonsContainer.pauseCallButton.rx.tap
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
198
            .subscribe(onNext: { [weak self] in
199
                self?.viewModel.togglePauseCall()
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
200 201
            }).disposed(by: self.disposeBag)

202
        self.buttonsContainer.switchCameraButton.rx.tap
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
203
            .subscribe(onNext: { [weak self] in
204
                self?.viewModel.switchCamera()
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
205 206
            }).disposed(by: self.disposeBag)

207
        self.buttonsContainer.switchSpeakerButton.rx.tap
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
208
            .subscribe(onNext: { [weak self] in
209
                self?.viewModel.switchSpeaker()
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
210 211
            }).disposed(by: self.disposeBag)

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
212
        //Data bindings
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
        self.viewModel.videoButtonState
            .observeOn(MainScheduler.instance)
            .bind(to: self.buttonsContainer.muteVideoButton.rx.image())
            .disposed(by: self.disposeBag)

        self.buttonsContainer.muteVideoButton.isEnabled = !(self.viewModel.call?.isAudioOnly ?? false)

        self.viewModel.audioButtonState
            .observeOn(MainScheduler.instance)
            .bind(to: self.buttonsContainer.muteAudioButton.rx.image())
            .disposed(by: self.disposeBag)

        self.viewModel.speakerButtonState
            .observeOn(MainScheduler.instance)
            .bind(to: self.buttonsContainer.switchSpeakerButton.rx.image())
            .disposed(by: self.disposeBag)

        self.viewModel.pauseCallButtonState
            .observeOn(MainScheduler.instance)
            .bind(to: self.buttonsContainer.pauseCallButton.rx.image())
            .disposed(by: self.disposeBag)

235 236 237 238 239 240
        self.viewModel.isActiveVideoCall
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] rotate in
                self?.shouldRotateScreen = rotate
            }).disposed(by: self.disposeBag)

241 242 243 244 245 246
        // disable switch camera button for audio only calls
        self.buttonsContainer.switchCameraButton.isEnabled = !(self.viewModel.isAudioOnly)
    }

    func setupBindings() {

247
        self.viewModel.contactImageData?.asObservable()
248 249
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] dataOrNil in
250 251 252 253 254
                if let imageData = dataOrNil {
                    if let image = UIImage(data: imageData) {
                        self?.profileImageView.image = image
                        self?.callProfileImage.image = image
                    }
255
                }
256
            }).disposed(by: self.disposeBag)
257

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
258 259 260
        self.viewModel.dismisVC
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] dismiss in
261 262 263 264
                if dismiss {
                    self?.removeFromScreen()
                }
            }).disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
265

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
266
        self.viewModel.contactName.drive(self.nameLabel.rx.text)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
267 268
            .disposed(by: self.disposeBag)

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
269 270 271 272 273 274
        self.viewModel.contactName.drive(self.callNameLabel.rx.text)
            .disposed(by: self.disposeBag)

        self.viewModel.callDuration.drive(self.durationLabel.rx.text)
            .disposed(by: self.disposeBag)

275 276
        self.viewModel.callDuration.asObservable().observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] _ in
277 278 279 280 281 282 283 284 285 286 287
                if self?.durationLabel.text != "" {
                    if (self?.viewModel.isAudioOnly)! {
                        self?.buttonContainerHeightConstraint.constant = 200
                        self?.buttonsContainer.containerHeightConstraint.constant = 200
                        self?.buttonsContainer.stackViewYConstraint.constant = 110
                        self?.buttonsContainer.stackViewWidthConstraint.priority = UILayoutPriority(rawValue: 999)
                        UIView.animate(withDuration: 0.3, animations: {
                            self?.durationLabel.alpha = 1
                            self?.buttonsContainer.stackView.alpha = 1
                        })
                    }
288 289 290
                }
            }).disposed(by: self.disposeBag)

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
291
        self.viewModel.callDuration.drive(self.callInfoTimerLabel.rx.text)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
292 293 294 295 296 297
            .disposed(by: self.disposeBag)

        self.viewModel.bottomInfo
            .observeOn(MainScheduler.instance)
            .bind(to: self.infoBottomLabel.rx.text)
            .disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
298 299 300 301

        self.viewModel.incomingFrame
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] frame in
302
                if let image = frame {
303
                    self?.spinner.stopAnimating()
304 305 306 307 308 309 310
                    if self?.beforeIncomingVideo.alpha != 0 {
                        UIView.animate(withDuration: 0.4, animations: {
                            self?.beforeIncomingVideo.alpha = 0
                            }, completion: { [weak self] _ in
                                self?.beforeIncomingVideo.isHidden = true
                        })
                    }
311 312 313
                    DispatchQueue.main.async {
                        self?.incomingVideo.image = image
                    }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
314
                }
315
            }).disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
316 317 318 319

        self.viewModel.capturedFrame
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] frame in
320 321 322 323
                if let image = frame {
                    DispatchQueue.main.async {
                        self?.capturedVideo.image = image
                    }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
324
                }
325
            }).disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
326 327

        self.viewModel.showCallOptions
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
328
            .observeOn(MainScheduler.instance)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
329 330 331 332 333
            .subscribe(onNext: { show in
                if show {
                    self.showContactInfo()
                }
            }).disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
334

335
        self.viewModel.showCancelOption
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
336
            .observeOn(MainScheduler.instance)
337 338 339 340 341
            .subscribe(onNext: { show in
                if show {
                    self.showCancelButton()
                }
            }).disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
342

343
        if !self.viewModel.isAudioOnly {
344
            self.resizeCapturedFrame()
345 346
        }

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
347 348 349 350 351
        self.viewModel.videoMuted
            .observeOn(MainScheduler.instance)
            .bind(to: self.capturedVideo.rx.isHidden)
            .disposed(by: self.disposeBag)

352 353 354 355 356 357 358 359 360 361
        self.viewModel.videoMuted
            .observeOn(MainScheduler.instance)
            .bind(to: self.capturedVideoBlurEffect.rx.isHidden)
            .disposed(by: self.disposeBag)

        self.viewModel.videoMuted
            .observeOn(MainScheduler.instance)
            .bind(to: self.leftArrow.rx.isHidden)
            .disposed(by: self.disposeBag)

362 363 364
        if !self.viewModel.isAudioOnly {
            self.viewModel.callPaused
                .observeOn(MainScheduler.instance)
365 366 367
                .subscribe(onNext: { show in
                    self.setAvatarView(show)
                }).disposed(by: self.disposeBag)
368
        }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
369

370 371 372 373 374 375 376 377
        self.viewModel.callPaused
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [unowned self] show in
                if show {
                    self.task?.cancel()
                    self.showCallOptions()
                }
            }).disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
378 379
    }

380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406
    func setAvatarView(_ show: Bool) {
        if !show {
            self.avatarView.isHidden = true
        } else {
            if UIDevice.current.userInterfaceIdiom == .pad {
                self.avatarViewImageTopConstraint.constant = 200
                self.avatarView.isHidden = false
                return
            }
            if self.orientation == .landscapeRight
                || self.orientation == .landscapeLeft {
                let device = UIDevice.modelName
                if device == "iPhone 5" || device ==  "iPhone 5c" || device == "iPhone 5s" || device == "iPhone SE" {
                    self.profileImageViewWidthConstraint.constant = 90
                    self.profileImageViewHeightConstraint.constant = 90
                    self.profileImageView.cornerRadius = 45
                }
                if self.viewModel.call?.state == .ringing || self.viewModel.call?.state == .connecting {
                    self.avatarViewImageTopConstraint.constant = 20
                } else {
                    self.avatarViewImageTopConstraint.constant = 10
                }
                if UIDevice.current.hasNotch {
                    self.buttonsContainerBottomConstraint.constant = 0
                } else {
                    self.buttonsContainerBottomConstraint.constant = 10
                }
407 408 409 410 411 412 413 414 415 416
                if self.viewModel.isAudioOnly {
                    if device == "iPhone 5" || device ==  "iPhone 5c" || device == "iPhone 5s" || device == "iPhone SE" {
                        self.durationLabel.isHidden = true
                        self.buttonsContainerBottomConstraint.constant = -10
                    }
                    self.buttonsContainer.backgroundBlurEffect.alpha = 0
                    self.profileImageViewWidthConstraint.constant = 90
                    self.profileImageViewHeightConstraint.constant = 90
                    self.profileImageView.cornerRadius = 45
                }
417 418 419 420 421 422 423 424 425 426 427 428 429
            } else {
                if UIDevice.current.hasNotch {
                    self.avatarViewImageTopConstraint.constant = 120
                } else {
                    self.avatarViewImageTopConstraint.constant = 85
                }
                self.buttonsContainerBottomConstraint.constant = 10
            }
            self.avatarView.isHidden = false
        }
    }

    func resizeCapturedFrame() {
430 431
        self.viewModel.showCapturedFrame
            .observeOn(MainScheduler.instance)
432 433 434 435
            .subscribe(onNext: { [weak self] dontShow in
                if dontShow && (!(self?.isCallStarted)!) {
                    self?.isCallStarted = true
                    self?.hideCancelButton()
436 437 438 439
                    let device = UIDevice.modelName
                    //Reduce the cancel button for small iPhone
                    switch device {
                    case "iPhone 5", "iPhone 5c", "iPhone 5s", "iPhone SE" :
440 441 442 443
                        self?.buttonsContainer.cancelButtonWidthConstraint.constant = 50
                        self?.buttonsContainer.cancelButtonHeightConstraint.constant = 50
                        self?.buttonsContainer.cancelButton.cornerRadius = 25
                        self?.buttonsContainer.cancelButtonBottomConstraint.constant = 30
444
                    default : break
445
                    }
446 447 448 449 450 451
                    UIView.animate(withDuration: 0.4, animations: {
                        self?.resizeCapturedVideo(withInfoContainer: false)
                        self?.capturedVideoBlurEffect.alpha = 0
                        self?.view.layoutIfNeeded()
                    }, completion: nil)
                    self?.avatarViewBlurEffect.alpha = CGFloat(1)
452 453 454 455
                }
            }).disposed(by: self.disposeBag)
    }

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
456
    func removeFromScreen() {
457
        UIDevice.current.isProximityMonitoringEnabled = false
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
458 459
        self.dismiss(animated: false)
    }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
460

461
    @objc func screenTapped() {
462 463 464
        if self.avatarView.isHidden{
            self.viewModel.respondOnTap()
        }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
465 466
    }

467 468 469 470 471 472
    func showCancelButton() {
        self.buttonsContainer.isHidden = false
        self.view.layoutIfNeeded()
    }

    func hideCancelButton() {
473 474
        self.buttonsContainerBottomConstraint.constant = -150
        self.infoContainerTopConstraint.constant = 150
475 476 477 478 479 480 481 482 483
        self.buttonsContainer.isHidden = true
        self.view.layoutIfNeeded()
    }

    func showCallOptions() {
        self.buttonsContainer.isHidden = false
        self.view.layoutIfNeeded()
    }

484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        // Waiting for screen size change
        DispatchQueue.global(qos: .background).async {
            sleep(UInt32(0.5))
            DispatchQueue.main.async {
                self.resizeCapturedVideo(withInfoContainer: !self.infoContainer.isHidden)
                if UIDevice.current.hasNotch && (UIDevice.current.orientation == .landscapeRight || UIDevice.current.orientation == .landscapeLeft) && self.infoContainer.isHidden == false  {
                    self.buttonsContainerBottomConstraint.constant = 1
                }
            }
        }
        self.viewModel.setCameraOrientation(orientation: UIDevice.current.orientation)
        super.viewWillTransition(to: size, with: coordinator)
    }

    func resizeCapturedVideo(withInfoContainer: Bool) {
500
        self.leftArrow.alpha = 0
501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556
        //Don't change anything if the orientation change to portraitUpsideDown, faceUp or faceDown
        if  UIDevice.current.orientation.rawValue != 5  && UIDevice.current.orientation.rawValue != 6 && UIDevice.current.orientation.rawValue != 2  {
            self.orientation = UIDevice.current.orientation
        }
        switch self.orientation {
        case .landscapeRight, .landscapeLeft:
            if !withInfoContainer {
                self.capturedVideoWidthConstraint.constant = -UIScreen.main.bounds.width + 160
                self.capturedVideoHeightConstraint.constant = -UIScreen.main.bounds.height + 120
                self.viewCapturedVideo.cornerRadius = 15
                if UIDevice.current.userInterfaceIdiom == .pad {
                    self.capturedVideoTrailingConstraint.constant = 35
                    self.capturedVideoTopConstraint.constant = -13
                } else if UIDevice.current.hasNotch && orientation == .landscapeRight {
                    self.capturedVideoTrailingConstraint.constant = 45
                    self.capturedVideoTopConstraint.constant = -15
                } else {
                    self.capturedVideoTrailingConstraint.constant = 15
                    self.capturedVideoTopConstraint.constant = -15
                }
            } else {
                //Keep the 4:3 format of the video
                let widthCapturedVideo = ((self.infoContainerHeightConstraint.constant - 20)/3)*4
                self.capturedVideoHeightConstraint.constant = -UIScreen.main.bounds.height + self.infoContainerHeightConstraint.constant - 20
                self.capturedVideoWidthConstraint.constant = -UIScreen.main.bounds.width + widthCapturedVideo
                let leftPointInfoContainer = self.infoBlurEffect?.convert((self.infoBlurEffect?.frame.origin)!, to: nil).x ?? 0
                self.capturedVideoTrailingConstraint.constant = leftPointInfoContainer + 10
                self.capturedVideoTopConstraint.constant = -20
                self.viewCapturedVideo.cornerRadius = 25
            }
        default:
            if !withInfoContainer {
                self.capturedVideoWidthConstraint.constant = -UIScreen.main.bounds.width + 120
                self.capturedVideoHeightConstraint.constant = -UIScreen.main.bounds.height + 160
                self.viewCapturedVideo.cornerRadius = 15
                if UIDevice.current.userInterfaceIdiom == .pad {
                    self.capturedVideoTrailingConstraint.constant = 35
                    self.capturedVideoTopConstraint.constant = -13
                } else if UIDevice.current.hasNotch {
                    self.capturedVideoTrailingConstraint.constant = 10
                    self.capturedVideoTopConstraint.constant = 0
                } else {
                    self.capturedVideoTrailingConstraint.constant = 10
                    self.capturedVideoTopConstraint.constant = -5
                }
            } else {
                //Keep the 4:3 format of the video
                let widthCapturedVideo = ((self.infoContainerHeightConstraint.constant - 20)/4)*3
                self.capturedVideoHeightConstraint.constant = -UIScreen.main.bounds.height + self.infoContainerHeightConstraint.constant - 20
                self.capturedVideoWidthConstraint.constant = -UIScreen.main.bounds.width + widthCapturedVideo
                let leftPointInfoContainer = self.infoBlurEffect?.convert((self.infoBlurEffect?.frame.origin)!, to: nil).x ?? 0
                self.capturedVideoTrailingConstraint.constant = leftPointInfoContainer + 10
                self.capturedVideoTopConstraint.constant = -20
                self.viewCapturedVideo.cornerRadius = 25
            }
        }
557 558 559 560 561
        if self.capturedVideoBlurEffect.alpha == 1 && self.isMenuShowed == false
            && self.avatarView.isHidden == true {
            self.leftArrow.alpha = 1
            self.capturedVideoTrailingConstraint.constant = -200
        }
562 563
    }

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
564 565 566 567 568 569
    func showContactInfo() {
        if !self.infoContainer.isHidden {
            task?.cancel()
            self.hideContactInfo()
            return
        }
570
        self.isMenuShowed = true
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
571 572 573 574
        self.buttonsContainer.isHidden = false
        self.infoContainer.isHidden = false
        self.view.layoutIfNeeded()

575
        UIView.animate(withDuration: 0.2, animations: {
576
            self.capturedVideoBlurEffect.alpha = 0
577 578 579 580 581 582 583 584 585 586 587
            self.resizeCapturedVideo(withInfoContainer: true)
            self.infoContainerTopConstraint.constant = -10
            if UIDevice.current.hasNotch && (self.orientation == .landscapeRight || self.orientation == .landscapeLeft)  {
                self.buttonsContainerBottomConstraint.constant = 1
            } else if UIDevice.current.userInterfaceIdiom == .pad {
                self.buttonsContainerBottomConstraint.constant = 30
            } else {
                self.buttonsContainerBottomConstraint.constant = 10
            }
            self.view.layoutIfNeeded()
        })
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
588 589

        task = DispatchWorkItem { self.hideContactInfo() }
590
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 7, execute: task!)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
591 592 593
    }

    func hideContactInfo() {
594
        self.isMenuShowed = false
595
        UIView.animate(withDuration: 0.2, animations: { [unowned self] in
596
            if self.isVideoHidden { self.capturedVideoBlurEffect.alpha = 1 }
597 598 599 600 601 602 603 604
            self.resizeCapturedVideo(withInfoContainer: false)
            self.infoContainerTopConstraint.constant = 150
            self.buttonsContainerBottomConstraint.constant = -150
            self.view.layoutIfNeeded()
            }, completion: { [weak self] _ in
                self?.infoContainer.isHidden = true
                self?.buttonsContainer.isHidden = true
        })
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
605
    }
606 607 608 609

    func showAllInfo() {
        self.buttonsContainer.isHidden = false
        self.infoContainer.isHidden = false
610 611
    }

612 613 614 615
    @objc func canRotate() {
        // empty function to support call screen rotation
    }

616 617 618 619 620 621 622
    override func viewWillDisappear(_ animated: Bool) {
        UIDevice.current.setValue(Int(UIInterfaceOrientation.portrait.rawValue), forKey: "orientation")
        super.viewWillDisappear(animated)
    }

    override var shouldAutorotate: Bool {
      return self.shouldRotateScreen
623
    }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
624
}