Commit 70702cba authored by Kateryna Kostiuk's avatar Kateryna Kostiuk Committed by Andreas Traczyk

call: display video

Change-Id: I5271602778a0d7ee3345619a5b9c41359a742912
Reviewed-by: Andreas Traczyk's avatarAndreas Traczyk <andreas.traczyk@savoirfairelinux.com>
parent ee79fdb6
......@@ -27,13 +27,31 @@ import SwiftyBeaver
class CallViewController: UIViewController, StoryboardBased, ViewModelBased {
@IBOutlet weak var profileImageView: UIImageView!
@IBOutlet weak var nameLabel: UILabel!
@IBOutlet weak var durationLabel: UILabel!
@IBOutlet weak var infoBottomLabel: UILabel!
@IBOutlet weak var cancelButton: UIButton!
@IBOutlet weak var incomingVideo: UIImageView!
@IBOutlet weak var capturedVideo: UIImageView!
//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!
@IBOutlet private weak var cancelButton: UIButton!
@IBOutlet private weak var mainView: UIView!
//video screen
@IBOutlet private weak var callView: UIView!
@IBOutlet private weak var incomingVideo: UIImageView!
@IBOutlet private weak var capturedVideo: UIImageView!
@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 infoLabelConstraint: NSLayoutConstraint!
// call options buttons
@IBOutlet private weak var buttonsContainer: UIView!
@IBOutlet private weak var muteAudioButton: UIButton!
@IBOutlet private weak var muteVideoButton: UIButton!
@IBOutlet private weak var pauseCallButton: UIButton!
@IBOutlet private weak var switchCameraButton: UIButton!
var viewModel: CallViewModel!
......@@ -41,18 +59,23 @@ class CallViewController: UIViewController, StoryboardBased, ViewModelBased {
private let log = SwiftyBeaver.self
private var task: DispatchWorkItem?
override func viewDidLoad() {
super.viewDidLoad()
let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(screenTaped))
self.mainView.addGestureRecognizer(tapGestureRecognizer)
self.setupUI()
self.setupBindings()
}
func setupUI() {
self.cancelButton.backgroundColor = UIColor.red
self.infoContainer.backgroundColor = UIColor.black.withAlphaComponent(0.3)
self.buttonsContainer.backgroundColor = UIColor.black.withAlphaComponent(0.3)
}
func setupBindings() {
//Cancel button action
self.cancelButton.rx.tap
.subscribe(onNext: { [weak self] in
......@@ -61,13 +84,13 @@ class CallViewController: UIViewController, StoryboardBased, ViewModelBased {
}).disposed(by: self.disposeBag)
//Data bindings
self.viewModel.contactImageData.asObservable()
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] dataOrNil in
if let imageData = dataOrNil {
if let image = UIImage(data: imageData) {
self?.profileImageView.image = image
self?.callProfileImage.image = image
}
}
}).disposed(by: self.disposeBag)
......@@ -80,23 +103,93 @@ class CallViewController: UIViewController, StoryboardBased, ViewModelBased {
}
}).disposed(by: self.disposeBag)
self.viewModel.contactName
.observeOn(MainScheduler.instance)
.bind(to: self.nameLabel.rx.text)
self.viewModel.contactName.drive(self.nameLabel.rx.text)
.disposed(by: self.disposeBag)
self.viewModel.callDuration
.observeOn(MainScheduler.instance)
.bind(to: self.durationLabel.rx.text)
self.viewModel.contactName.drive(self.callNameLabel.rx.text)
.disposed(by: self.disposeBag)
self.viewModel.callDuration.drive(self.durationLabel.rx.text)
.disposed(by: self.disposeBag)
self.viewModel.callDuration.drive(self.callInfoTimerLabel.rx.text)
.disposed(by: self.disposeBag)
self.viewModel.bottomInfo
.observeOn(MainScheduler.instance)
.bind(to: self.infoBottomLabel.rx.text)
.disposed(by: self.disposeBag)
self.viewModel.incomingFrame
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] frame in
if let image = frame {
DispatchQueue.main.async {
self?.callView.isHidden = false
self?.incomingVideo.image = image
}
}
}).disposed(by: self.disposeBag)
self.viewModel.capturedFrame
.observeOn(MainScheduler.instance)
.subscribe(onNext: { [weak self] frame in
if let image = frame {
DispatchQueue.main.async {
self?.capturedVideo.image = image
}
}
}).disposed(by: self.disposeBag)
self.viewModel.showCallOptions
.subscribeOn(MainScheduler.instance)
.subscribe(onNext: { show in
if show {
self.showContactInfo()
}
}).disposed(by: self.disposeBag)
}
func removeFromScreen() {
self.dismiss(animated: false)
}
@objc func screenTaped() {
self.viewModel.respondOnTap()
}
func showContactInfo() {
if !self.infoContainer.isHidden {
task?.cancel()
self.hideContactInfo()
return
}
self.infoLabelConstraint.constant = -200.00
self.buttonsContainer.isHidden = false
self.infoContainer.isHidden = false
self.view.layoutIfNeeded()
UIView.animate(withDuration: 0.2, delay: 0.0,
options: .curveEaseOut,
animations: { [weak self] in
self?.infoLabelConstraint.constant = 0.00
self?.view.layoutIfNeeded()
}, completion: nil)
task = DispatchWorkItem { self.hideContactInfo() }
DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + 2, execute: task!)
}
func hideContactInfo() {
UIView.animate(withDuration: 0.2, delay: 0.00,
options: .curveEaseOut,
animations: { [weak self] in
self?.infoLabelConstraint.constant = -200.00
self?.view.layoutIfNeeded()
}, completion: { [weak self] _ in
self?.infoContainer.isHidden = true
self?.buttonsContainer.isHidden = true
})
}
}
......@@ -22,6 +22,7 @@
import RxSwift
import SwiftyBeaver
import Contacts
import RxCocoa
class CallViewModel: Stateable, ViewModel {
......@@ -38,17 +39,6 @@ class CallViewModel: Stateable, ViewModel {
private let disposeBag = DisposeBag()
fileprivate let log = SwiftyBeaver.self
lazy var incomingFrame: Observable<UIImage?> = {
return videoService.incomingVideoFrame.asObservable().map({ frame in
return frame
})
}()
lazy var capturedFrame: Observable<UIImage?> = {
return videoService.capturedVideoFrame.asObservable().map({ frame in
return frame
})
}()
var call: CallModel? {
didSet {
guard let call = self.call else {
......@@ -84,6 +74,17 @@ class CallViewModel: Stateable, ViewModel {
var contactImageData = Variable<Data?>(nil)
lazy var incomingFrame: Observable<UIImage?> = {
return videoService.incomingVideoFrame.asObservable().map({ frame in
return frame
})
}()
lazy var capturedFrame: Observable<UIImage?> = {
return videoService.capturedVideoFrame.asObservable().map({ frame in
return frame
})
}()
lazy var dismisVC: Observable<Bool> = {
return callService.currentCall.map({[weak self] call in
return call.state == .over || call.state == .failure && call.callId == self?.call?.callId
......@@ -92,7 +93,7 @@ class CallViewModel: Stateable, ViewModel {
})
}()
lazy var contactName: Observable<String> = {
lazy var contactName: Driver<String> = {
return callService.currentCall.filter({ [weak self] call in
return call.state != .over && call.state != .inactive && call.callId == self?.call?.callId
}).map({ call in
......@@ -103,10 +104,10 @@ class CallViewModel: Stateable, ViewModel {
} else {
return L10n.Calls.unknown
}
})
}).asDriver(onErrorJustReturn: "")
}()
lazy var callDuration: Observable<String> = {
lazy var callDuration: Driver<String> = {
let timer = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
.takeUntil(self.callService.currentCall
.filter { [weak self] call in
......@@ -120,7 +121,7 @@ class CallViewModel: Stateable, ViewModel {
return call.state == .current
}).flatMap({ _ in
return timer
})
}).asDriver(onErrorJustReturn: "")
}()
lazy var bottomInfo: Observable<String> = {
......@@ -135,6 +136,22 @@ class CallViewModel: Stateable, ViewModel {
})
}()
lazy var showCallOptions: Observable<Bool> = {
return Observable.combineLatest(self.callIsActive, self.screenTapped.asObservable()) {(active, tapped) -> Bool in
return active && tapped
}
}()
lazy var callIsActive: Observable<Bool> = {
self.callService.currentCall.filter({ call in
return call.state == .current && call.callId == self.call?.callId
}).map({_ in
return true
})
}()
var screenTapped = BehaviorSubject(value: false)
required init(with injectionBag: InjectionBag) {
self.callService = injectionBag.callService
self.contactsService = injectionBag.contactsService
......@@ -160,8 +177,6 @@ class CallViewModel: Stateable, ViewModel {
}).disposed(by: self.disposeBag)
}
func answerCall() {
guard let call = self.call else {
return
......@@ -196,4 +211,8 @@ class CallViewModel: Stateable, ViewModel {
}
self.contactImageData.value = data
}
func respondOnTap() {
self.screenTapped.onNext(true)
}
}
......@@ -56,8 +56,14 @@ enum Asset {
static let device = ImageAsset(name: "device")
static let fallbackAvatar = ImageAsset(name: "fallback_avatar")
static let icContactPicture = ImageAsset(name: "ic_contact_picture")
static let moreSettings = ImageAsset(name: "more_settings")
static let muteAudio = ImageAsset(name: "mute_audio")
static let muteVideo = ImageAsset(name: "mute_video")
static let pauseCall = ImageAsset(name: "pause_call")
static let ringLogo = ImageAsset(name: "ring_logo")
static let settingsIcon = ImageAsset(name: "settings_icon")
static let stopCall = ImageAsset(name: "stop_call")
static let switchCamera = ImageAsset(name: "switch_camera")
// swiftlint:disable trailing_comma
static let allColors: [ColorAsset] = [
......@@ -73,8 +79,14 @@ enum Asset {
device,
fallbackAvatar,
icContactPicture,
moreSettings,
muteAudio,
muteVideo,
pauseCall,
ringLogo,
settingsIcon,
stopCall,
switchCamera,
]
// swiftlint:enable trailing_comma
@available(*, deprecated, renamed: "allImages")
......
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_more_horiz_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_more_horiz_white_2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_more_horiz_white_3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_volume_mute_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_volume_mute_white_2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_volume_mute_white_3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_videocam_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_videocam_white_2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_videocam_white_3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_pause_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_pause_white_2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_pause_white_3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_call_end_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_call_end_white_2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_call_end_white_3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
{
"images" : [
{
"idiom" : "universal",
"filename" : "ic_replay_white.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "ic_replay_white_2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "ic_replay_white_3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
\ No newline at end of file
......@@ -135,7 +135,7 @@ class FrameExtractor: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {
}
captureSession.addInput(captureDeviceInput)
let videoOutput = AVCaptureVideoDataOutput()
videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "sample buffer"))
videoOutput.setSampleBufferDelegate(self, queue: sessionQueue)
guard captureSession.canAddOutput(videoOutput) else {
throw VideoError.setupOutputDeviceFailed
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment