CallViewController.swift 27.2 KB
Newer Older
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
1
/*
2
 *  Copyright (C) 2017-2019 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>
6
 *  Author: Quentin Muret <quentin.muret@savoirfairelinux.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
 *
 *  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 RxSwift
import Reusable
26
import SwiftyBeaver
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
27

28
// swiftlint:disable type_body_length
29
// swiftlint:disable file_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
    var viewModel: CallViewModel!
73
    var isCallStarted: Bool = false
74 75
    var isMenuShowed = false
    var isVideoHidden = false
76
    var orientation = UIDevice.current.orientation
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
77 78 79

    fileprivate let disposeBag = DisposeBag()

80
    private let log = SwiftyBeaver.self
81 82
    private var shouldRotateScreen = false

83 84 85 86
    override var preferredStatusBarStyle: UIStatusBarStyle {
        return self.viewModel.isAudioOnly ? .lightContent : .default
    }

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
            self.setWhiteAvatarView()
119
        }
120

121
        UIDevice.current.isProximityMonitoringEnabled = self.viewModel.isAudioOnly
122 123

        initCallAnimation()
124
        UIApplication.shared.isIdleTimerDisabled = true
125 126
    }

127 128
    @objc func capturedVideoSwipped(gesture: UISwipeGestureRecognizer) {
        if self.avatarView.isHidden == false { return }
129 130
        if gesture.direction == UISwipeGestureRecognizer.Direction.left && (self.isVideoHidden == false) { return }
        if gesture.direction == UISwipeGestureRecognizer.Direction.right && (self.isVideoHidden == true) { return }
131 132 133 134 135
        self.hideCapturedVideo()
    }

    @objc func hideCapturedVideo() {
        if self.isMenuShowed { return }
136 137 138 139
        UIView.animate(withDuration: 0.3, animations: { [weak self] in
            if self?.capturedVideoBlurEffect.alpha == 0 {
                self?.isVideoHidden = true
                self?.capturedVideoBlurEffect.alpha = 1
140
            } else {
141 142
                self?.isVideoHidden = false
                self?.capturedVideoBlurEffect.alpha = 0
143
            }
144 145 146
            guard let hidden = self?.infoContainer.isHidden else {return}
            self?.resizeCapturedVideo(withInfoContainer: !hidden)
            self?.view.layoutIfNeeded()
147
        })
148 149 150
    }

    func setWhiteAvatarView() {
Quentin Muret's avatar
Quentin Muret committed
151
        self.callPulse.backgroundColor = UIColor.jamiCallPulse
152
        self.avatarView.backgroundColor = UIColor.white
153 154
    }

155 156 157 158 159 160 161 162 163
    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)
164 165 166 167 168 169 170
        UIView.animate(withDuration: 1.5, animations: { [weak self] in
            self?.callPulse.alpha = 0.0
            self?.callPulse.transform = CGAffineTransform(scaleX: 2.0, y: 2.0)
            self?.view.layoutIfNeeded()
        }, completion: { [weak self] _ in
            if self?.viewModel.call?.state == .ringing || self?.viewModel.call?.state == .connecting {
                self?.animateCallCircle()
171 172
            }
        })
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
173 174
    }

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

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
184
        self.buttonsContainer.acceptCallButton.rx.tap
185 186
            .subscribe(onNext: { [unowned self] in
                self.viewModel.answerCall()
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
187
                    .subscribe()
188
                    .disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
189 190
            }).disposed(by: self.disposeBag)

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
191 192 193 194 195
        self.buttonsContainer.dialpadButton.rx.tap
            .subscribe(onNext: { [weak self] in
                self?.viewModel.showDialpad()
            }).disposed(by: self.disposeBag)

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

201 202 203 204 205 206 207 208
        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
209
            .subscribe(onNext: { [weak self] in
210
                self?.viewModel.togglePauseCall()
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
211 212
            }).disposed(by: self.disposeBag)

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
213
        self.buttonsContainer.switchCameraButton.rx.tap
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
214
            .subscribe(onNext: { [weak self] in
215
                self?.viewModel.switchCamera()
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
216 217
            }).disposed(by: self.disposeBag)

218
        self.buttonsContainer.switchSpeakerButton.rx.tap
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
219
            .subscribe(onNext: { [weak self] in
220
                self?.viewModel.switchSpeaker()
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
221 222
            }).disposed(by: self.disposeBag)

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
223
        //Data bindings
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245
        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)

246 247 248 249 250
        self.viewModel.isActiveVideoCall
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] rotate in
                self?.shouldRotateScreen = rotate
            }).disposed(by: self.disposeBag)
251 252
    }

253 254
    // swiftlint:disable function_body_length
    // swiftlint:disable cyclomatic_complexity
255 256
    func setupBindings() {

257
        self.viewModel.contactImageData?.asObservable()
258 259
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] dataOrNil in
260 261 262 263 264
                if let imageData = dataOrNil {
                    if let image = UIImage(data: imageData) {
                        self?.profileImageView.image = image
                        self?.callProfileImage.image = image
                    }
265
                }
266
            }).disposed(by: self.disposeBag)
267

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
268 269 270
        self.viewModel.dismisVC
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] dismiss in
271 272 273 274
                if dismiss {
                    self?.removeFromScreen()
                }
            }).disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
275

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
276
        self.viewModel.contactName.drive(self.nameLabel.rx.text)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
277 278
            .disposed(by: self.disposeBag)

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
279 280 281 282 283 284
        self.viewModel.contactName.drive(self.callNameLabel.rx.text)
            .disposed(by: self.disposeBag)

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

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
285 286 287
        self.viewModel.callDuration
            .asObservable()
            .observeOn(MainScheduler.instance)
288
            .subscribe(onNext: { [weak self] _ in
289
                if self?.durationLabel.text != "" {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
290
                    if self?.viewModel.isAudioOnly ?? true {
291 292 293 294 295 296 297 298 299
                        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
                        })
                    }
300 301 302
                }
            }).disposed(by: self.disposeBag)

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
303
        self.viewModel.callDuration.drive(self.callInfoTimerLabel.rx.text)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
304 305 306 307 308 309
            .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
310 311 312 313

        self.viewModel.incomingFrame
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] frame in
314
                if let image = frame {
315
                    self?.spinner.stopAnimating()
316 317 318 319 320 321 322
                    if self?.beforeIncomingVideo.alpha != 0 {
                        UIView.animate(withDuration: 0.4, animations: {
                            self?.beforeIncomingVideo.alpha = 0
                            }, completion: { [weak self] _ in
                                self?.beforeIncomingVideo.isHidden = true
                        })
                    }
323 324 325
                    DispatchQueue.main.async {
                        self?.incomingVideo.image = image
                    }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
326
                }
327
            }).disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
328 329 330 331

        self.viewModel.capturedFrame
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] frame in
332 333 334 335
                if let image = frame {
                    DispatchQueue.main.async {
                        self?.capturedVideo.image = image
                    }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
336
                }
337
            }).disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
338 339

        self.viewModel.showCallOptions
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
340
            .observeOn(MainScheduler.instance)
341
            .subscribe(onNext: { [weak self] show in
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
342
                if show {
343
                    self?.showContactInfo()
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
344 345
                }
            }).disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
346

347
        self.viewModel.showCancelOption
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
348
            .observeOn(MainScheduler.instance)
349
            .subscribe(onNext: { [weak self] show in
350
                if show {
351
                    self?.showCancelButton()
352 353
                }
            }).disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
354

355
        if !self.viewModel.isAudioOnly {
356
            self.resizeCapturedFrame()
357 358
        }

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
359 360 361 362 363
        self.viewModel.videoMuted
            .observeOn(MainScheduler.instance)
            .bind(to: self.capturedVideo.rx.isHidden)
            .disposed(by: self.disposeBag)

364 365 366 367 368 369 370 371 372 373
        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)

374 375 376
        if !self.viewModel.isAudioOnly {
            self.viewModel.callPaused
                .observeOn(MainScheduler.instance)
377 378
                .subscribe(onNext: { [weak self] show in
                    self?.setAvatarView(show)
379
                }).disposed(by: self.disposeBag)
380
        }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
381

382 383 384 385 386 387 388
        self.viewModel.callPaused
            .observeOn(MainScheduler.instance)
            .subscribe(onNext: { [unowned self] show in
                if show {
                    self.showCallOptions()
                }
            }).disposed(by: self.disposeBag)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
389 390
    }

391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417
    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
                }
418 419 420 421 422 423 424 425 426 427
                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
                }
428 429 430 431 432 433 434 435 436 437 438 439 440
            } else {
                if UIDevice.current.hasNotch {
                    self.avatarViewImageTopConstraint.constant = 120
                } else {
                    self.avatarViewImageTopConstraint.constant = 85
                }
                self.buttonsContainerBottomConstraint.constant = 10
            }
            self.avatarView.isHidden = false
        }
    }

    func resizeCapturedFrame() {
441 442
        self.viewModel.showCapturedFrame
            .observeOn(MainScheduler.instance)
443
            .subscribe(onNext: { [weak self] dontShow in
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
444
                if dontShow && (!(self?.isCallStarted ?? false)) {
445
                    self?.isCallStarted = true
446 447 448 449
                    let device = UIDevice.modelName
                    //Reduce the cancel button for small iPhone
                    switch device {
                    case "iPhone 5", "iPhone 5c", "iPhone 5s", "iPhone SE" :
450 451 452
                        self?.buttonsContainer.cancelButtonWidthConstraint.constant = 50
                        self?.buttonsContainer.cancelButtonHeightConstraint.constant = 50
                        self?.buttonsContainer.cancelButton.cornerRadius = 25
453
                    default : break
454
                    }
455 456 457 458 459 460
                    UIView.animate(withDuration: 0.4, animations: {
                        self?.resizeCapturedVideo(withInfoContainer: false)
                        self?.capturedVideoBlurEffect.alpha = 0
                        self?.view.layoutIfNeeded()
                    }, completion: nil)
                    self?.avatarViewBlurEffect.alpha = CGFloat(1)
461 462 463 464
                }
            }).disposed(by: self.disposeBag)
    }

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
465
    func removeFromScreen() {
466
        UIDevice.current.isProximityMonitoringEnabled = false
467
        UIApplication.shared.isIdleTimerDisabled = false
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
468 469
        self.dismiss(animated: false)
    }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
470

471
    @objc func screenTapped() {
472
        if self.avatarView.isHidden {
473 474
            self.viewModel.respondOnTap()
        }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
475 476
    }

477 478 479 480 481 482
    func showCancelButton() {
        self.buttonsContainer.isHidden = false
        self.view.layoutIfNeeded()
    }

    func hideCancelButton() {
483 484
        self.buttonsContainerBottomConstraint.constant = -150
        self.infoContainerTopConstraint.constant = 150
485 486 487 488 489 490 491 492 493
        self.buttonsContainer.isHidden = true
        self.view.layoutIfNeeded()
    }

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

494 495 496 497
    override func viewWillTransition(to size: CGSize, with coordinator: UIViewControllerTransitionCoordinator) {
        // Waiting for screen size change
        DispatchQueue.global(qos: .background).async {
            sleep(UInt32(0.5))
498 499 500
            DispatchQueue.main.async { [weak self] in
                guard let hidden = self?.infoContainer.isHidden else {return}
                self?.resizeCapturedVideo(withInfoContainer: !hidden)
501
                if UIDevice.current.hasNotch && (UIDevice.current.orientation == .landscapeRight || UIDevice.current.orientation == .landscapeLeft) && self?.infoContainer.isHidden == false {
502
                    self?.buttonsContainerBottomConstraint.constant = 1
503 504 505 506 507 508 509 510
                }
            }
        }
        self.viewModel.setCameraOrientation(orientation: UIDevice.current.orientation)
        super.viewWillTransition(to: size, with: coordinator)
    }

    func resizeCapturedVideo(withInfoContainer: Bool) {
511
        self.leftArrow.alpha = 0
512
        //Don't change anything if the orientation change to portraitUpsideDown, faceUp or faceDown
513
        if  UIDevice.current.orientation.rawValue != 5  && UIDevice.current.orientation.rawValue != 6 && UIDevice.current.orientation.rawValue != 2 {
514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536
            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
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
537 538
                let leftPointInfoContainer = self.infoBlurEffect?
                    .convert((self.infoBlurEffect?.frame.origin)!, to: nil).x ?? 0
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562
                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
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
563 564
                let leftPointInfoContainer = self.infoBlurEffect?.convert((self.infoBlurEffect?
                    .frame.origin)!, to: nil).x ?? 0
565 566 567 568 569
                self.capturedVideoTrailingConstraint.constant = leftPointInfoContainer + 10
                self.capturedVideoTopConstraint.constant = -20
                self.viewCapturedVideo.cornerRadius = 25
            }
        }
570 571 572 573 574
        if self.capturedVideoBlurEffect.alpha == 1 && self.isMenuShowed == false
            && self.avatarView.isHidden == true {
            self.leftArrow.alpha = 1
            self.capturedVideoTrailingConstraint.constant = -200
        }
575 576
    }

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
577
    func showContactInfo() {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
578
        if !self.buttonsContainer.isHidden {
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
579 580 581
            self.hideContactInfo()
            return
        }
582
        self.isMenuShowed = true
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
583
        self.buttonsContainer.isHidden = false
584

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
585 586 587
        self.infoContainer.isHidden = false
        self.view.layoutIfNeeded()

588 589 590
        UIView.animate(withDuration: 0.2, animations: { [weak self] in
            self?.resizeCapturedVideo(withInfoContainer: true)
            self?.infoContainerTopConstraint.constant = -10
591
            if UIDevice.current.hasNotch && (self?.orientation == .landscapeRight || self?.orientation == .landscapeLeft) {
592
                self?.buttonsContainerBottomConstraint.constant = 1
593
            } else if UIDevice.current.userInterfaceIdiom == .pad {
594
                self?.buttonsContainerBottomConstraint.constant = 30
595
            } else {
596
                self?.buttonsContainerBottomConstraint.constant = 10
597
            }
598
            self?.view.layoutIfNeeded()
599
        })
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
600 601 602
    }

    func hideContactInfo() {
603
        self.isMenuShowed = false
604 605 606
        UIView.animate(withDuration: 0.2, animations: { [unowned self] in
            self.resizeCapturedVideo(withInfoContainer: false)
            self.infoContainerTopConstraint.constant = 150
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
607
            self.buttonsContainerBottomConstraint.constant = -200
608 609 610 611 612
            self.view.layoutIfNeeded()
            }, completion: { [weak self] _ in
                self?.infoContainer.isHidden = true
                self?.buttonsContainer.isHidden = true
        })
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
613
    }
614 615 616 617

    func showAllInfo() {
        self.buttonsContainer.isHidden = false
        self.infoContainer.isHidden = false
618 619
    }

620 621 622 623
    @objc func canRotate() {
        // empty function to support call screen rotation
    }

624 625 626 627 628 629 630
    override func viewWillDisappear(_ animated: Bool) {
        UIDevice.current.setValue(Int(UIInterfaceOrientation.portrait.rawValue), forKey: "orientation")
        super.viewWillDisappear(animated)
    }

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