CallViewController.swift 27.3 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>
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 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
    @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 }
138 139 140 141
        UIView.animate(withDuration: 0.3, animations: { [weak self] in
            if self?.capturedVideoBlurEffect.alpha == 0 {
                self?.isVideoHidden = true
                self?.capturedVideoBlurEffect.alpha = 1
142
            } else {
143 144
                self?.isVideoHidden = false
                self?.capturedVideoBlurEffect.alpha = 0
145
            }
146 147 148
            guard let hidden = self?.infoContainer.isHidden else {return}
            self?.resizeCapturedVideo(withInfoContainer: !hidden)
            self?.view.layoutIfNeeded()
149
        })
150 151 152
    }

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

158 159 160 161 162 163 164 165 166
    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)
167 168 169 170 171 172 173
        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()
174 175
            }
        })
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
176 177
    }

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

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

191 192 193 194 195 196 197 198
        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
199
            .subscribe(onNext: { [weak self] in
200
                self?.viewModel.togglePauseCall()
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
201 202
            }).disposed(by: self.disposeBag)

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

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

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
213
        //Data bindings
214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
        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)

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

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

    func setupBindings() {

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

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

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

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

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

276 277
        self.viewModel.callDuration.asObservable().observeOn(MainScheduler.instance)
            .subscribe(onNext: { [weak self] _ in
278 279 280 281 282 283 284 285 286 287 288
                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
                        })
                    }
289 290 291
                }
            }).disposed(by: self.disposeBag)

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
292
        self.viewModel.callDuration.drive(self.callInfoTimerLabel.rx.text)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
293 294 295 296 297 298
            .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
299 300 301 302

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

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

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

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

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

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

353 354 355 356 357 358 359 360 361 362
        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)

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

371 372 373 374 375 376 377 378
        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
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 407
    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
                }
408 409 410 411 412 413 414 415 416 417
                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
                }
418 419 420 421 422 423 424 425 426 427 428 429 430
            } else {
                if UIDevice.current.hasNotch {
                    self.avatarViewImageTopConstraint.constant = 120
                } else {
                    self.avatarViewImageTopConstraint.constant = 85
                }
                self.buttonsContainerBottomConstraint.constant = 10
            }
            self.avatarView.isHidden = false
        }
    }

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

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
457
    func removeFromScreen() {
458 459 460
        if !self.infoContainer.isHidden {
            task?.cancel()
        }
461
        UIDevice.current.isProximityMonitoringEnabled = false
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
462 463
        self.dismiss(animated: false)
    }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
464

465
    @objc func screenTapped() {
466 467 468
        if self.avatarView.isHidden{
            self.viewModel.respondOnTap()
        }
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
469 470
    }

471 472 473 474 475 476
    func showCancelButton() {
        self.buttonsContainer.isHidden = false
        self.view.layoutIfNeeded()
    }

    func hideCancelButton() {
477 478
        self.buttonsContainerBottomConstraint.constant = -150
        self.infoContainerTopConstraint.constant = 150
479 480 481 482 483 484 485 486 487
        self.buttonsContainer.isHidden = true
        self.view.layoutIfNeeded()
    }

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

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

    func resizeCapturedVideo(withInfoContainer: Bool) {
505
        self.leftArrow.alpha = 0
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 557 558 559 560 561
        //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
            }
        }
562 563 564 565 566
        if self.capturedVideoBlurEffect.alpha == 1 && self.isMenuShowed == false
            && self.avatarView.isHidden == true {
            self.leftArrow.alpha = 1
            self.capturedVideoTrailingConstraint.constant = -200
        }
567 568
    }

Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
569 570 571 572 573 574
    func showContactInfo() {
        if !self.infoContainer.isHidden {
            task?.cancel()
            self.hideContactInfo()
            return
        }
575
        self.isMenuShowed = true
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
576 577 578 579
        self.buttonsContainer.isHidden = false
        self.infoContainer.isHidden = false
        self.view.layoutIfNeeded()

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

594
        task = DispatchWorkItem {[weak self] in self?.hideContactInfo() }
595
        DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 7, execute: task!)
Kateryna Kostiuk's avatar
Kateryna Kostiuk committed
596 597 598
    }

    func hideContactInfo() {
599
        self.isMenuShowed = false
600
        UIView.animate(withDuration: 0.2, animations: { [unowned self] in
601
            if self.isVideoHidden { self.capturedVideoBlurEffect.alpha = 1 }
602 603 604 605 606 607 608 609
            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
610
    }
611 612 613 614

    func showAllInfo() {
        self.buttonsContainer.isHidden = false
        self.infoContainer.isHidden = false
615 616
    }

617 618 619 620
    @objc func canRotate() {
        // empty function to support call screen rotation
    }

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

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