Merge branch 'mkirk/flip-camera'

pull/1/head
Michael Kirk 7 years ago
commit 56c53cdff2

@ -46,7 +46,10 @@ class CallViewController: OWSViewController, CallObserver, CallServiceObserver,
// MARK: - Ongoing Call Controls // MARK: - Ongoing Call Controls
var ongoingCallView: UIView! var ongoingCallControls: UIStackView!
var ongoingAudioCallControls: UIStackView!
var ongoingVideoCallControls: UIStackView!
var hangUpButton: UIButton! var hangUpButton: UIButton!
var audioSourceButton: UIButton! var audioSourceButton: UIButton!
@ -54,14 +57,11 @@ class CallViewController: OWSViewController, CallObserver, CallServiceObserver,
var audioModeVideoButton: UIButton! var audioModeVideoButton: UIButton!
var videoModeMuteButton: UIButton! var videoModeMuteButton: UIButton!
var videoModeVideoButton: UIButton! var videoModeVideoButton: UIButton!
// TODO: Later, we'll re-enable the text message button var videoModeFlipCameraButton: UIButton!
// so users can send and read messages during a
// call.
// var textMessageButton: UIButton!
// MARK: - Incoming Call Controls // MARK: - Incoming Call Controls
var incomingCallView: UIView! var incomingCallControls: UIStackView!
var acceptIncomingButton: UIButton! var acceptIncomingButton: UIButton!
var declineIncomingButton: UIButton! var declineIncomingButton: UIButton!
@ -182,7 +182,10 @@ class CallViewController: OWSViewController, CallObserver, CallServiceObserver,
override func loadView() { override func loadView() {
self.view = UIView() self.view = UIView()
self.view.layoutMargins = UIEdgeInsets(top: 16, left: 20, bottom: 16, right: 20)
createViews() createViews()
createViewConstraints()
} }
override func viewDidLoad() { override func viewDidLoad() {
@ -348,42 +351,60 @@ class CallViewController: OWSViewController, CallObserver, CallServiceObserver,
// textMessageButton = createButton(imageName:"message-active-wide", // textMessageButton = createButton(imageName:"message-active-wide",
// action:#selector(didPressTextMessage)) // action:#selector(didPressTextMessage))
audioSourceButton = createButton(imageName: "audio-call-speaker-inactive", audioSourceButton = createButton(image: #imageLiteral(resourceName: "audio-call-speaker-inactive"),
action: #selector(didPressAudioSource)) action: #selector(didPressAudioSource))
audioSourceButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_AUDIO_SOURCE_LABEL", audioSourceButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_AUDIO_SOURCE_LABEL",
comment: "Accessibility label for selection the audio source") comment: "Accessibility label for selection the audio source")
hangUpButton = createButton(imageName: "hangup-active-wide", hangUpButton = createButton(image: #imageLiteral(resourceName: "hangup-active-wide"),
action: #selector(didPressHangup)) action: #selector(didPressHangup))
hangUpButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_HANGUP_LABEL", hangUpButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_HANGUP_LABEL",
comment: "Accessibility label for hang up call") comment: "Accessibility label for hang up call")
audioModeMuteButton = createButton(imageName: "audio-call-mute-inactive", audioModeMuteButton = createButton(image: #imageLiteral(resourceName: "audio-call-mute-inactive"),
action: #selector(didPressMute)) action: #selector(didPressMute))
audioModeMuteButton.setImage(#imageLiteral(resourceName: "audio-call-mute-active"), for: .selected)
audioModeMuteButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_MUTE_LABEL", audioModeMuteButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_MUTE_LABEL",
comment: "Accessibility label for muting the microphone") comment: "Accessibility label for muting the microphone")
videoModeMuteButton = createButton(imageName: "video-mute-unselected", audioModeVideoButton = createButton(image: #imageLiteral(resourceName: "audio-call-video-inactive"),
action: #selector(didPressVideo))
audioModeVideoButton.setImage(#imageLiteral(resourceName: "audio-call-video-active"), for: .selected)
audioModeVideoButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_SWITCH_TO_VIDEO_LABEL", comment: "Accessibility label to switch to video call")
videoModeMuteButton = createButton(image: #imageLiteral(resourceName: "video-mute-unselected"),
action: #selector(didPressMute)) action: #selector(didPressMute))
videoModeMuteButton.setImage(#imageLiteral(resourceName: "video-mute-selected"), for: .selected)
videoModeMuteButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_MUTE_LABEL", comment: "Accessibility label for muting the microphone") videoModeMuteButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_MUTE_LABEL", comment: "Accessibility label for muting the microphone")
videoModeMuteButton.alpha = 0.9
audioModeVideoButton = createButton(imageName: "audio-call-video-inactive", // TODO proper asset
action: #selector(didPressVideo)) videoModeFlipCameraButton = createButton(image: #imageLiteral(resourceName: "btnRefresh--white"),
audioModeVideoButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_SWITCH_TO_VIDEO_LABEL", comment: "Accessibility label to switch to video call") action: #selector(didPressFlipCamera))
videoModeFlipCameraButton.setImage(#imageLiteral(resourceName: "btnRefresh--white"),
for: .selected)
videoModeFlipCameraButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_SWITCH_CAMERA_DIRECTION", comment: "Accessibility label to toggle front vs. rear facing camera")
videoModeFlipCameraButton.alpha = 0.9
videoModeVideoButton = createButton(imageName: "video-video-unselected", videoModeVideoButton = createButton(image: #imageLiteral(resourceName: "video-video-unselected"),
action: #selector(didPressVideo)) action: #selector(didPressVideo))
videoModeVideoButton.setImage(#imageLiteral(resourceName: "video-video-selected"), for: .selected)
videoModeVideoButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_SWITCH_TO_AUDIO_LABEL", comment: "Accessibility label to switch to audio only") videoModeVideoButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_SWITCH_TO_AUDIO_LABEL", comment: "Accessibility label to switch to audio only")
videoModeVideoButton.alpha = 0.9
setButtonSelectedImage(button: audioModeMuteButton, imageName: "audio-call-mute-active") ongoingCallControls = UIStackView(arrangedSubviews: [hangUpButton])
setButtonSelectedImage(button: videoModeMuteButton, imageName: "video-mute-selected") ongoingCallControls.axis = .vertical
setButtonSelectedImage(button: audioModeVideoButton, imageName: "audio-call-video-active") ongoingCallControls.alignment = .center
setButtonSelectedImage(button: videoModeVideoButton, imageName: "video-video-selected") view.addSubview(ongoingCallControls)
ongoingCallView = createContainerForCallControls(controlGroups: [ ongoingAudioCallControls = UIStackView(arrangedSubviews: [audioModeMuteButton, audioSourceButton, audioModeVideoButton])
[audioModeMuteButton, audioSourceButton, audioModeVideoButton ], ongoingAudioCallControls.distribution = .equalSpacing
[videoModeMuteButton, hangUpButton, videoModeVideoButton ] ongoingAudioCallControls.axis = .horizontal
])
ongoingVideoCallControls = UIStackView(arrangedSubviews: [videoModeMuteButton, videoModeFlipCameraButton, videoModeVideoButton])
ongoingAudioCallControls.distribution = .equalSpacing
ongoingVideoCallControls.axis = .horizontal
} }
func presentAudioSourcePicker() { func presentAudioSourcePicker() {
@ -416,59 +437,30 @@ class CallViewController: OWSViewController, CallObserver, CallServiceObserver,
self.present(actionSheetController, animated: true) self.present(actionSheetController, animated: true)
} }
func setButtonSelectedImage(button: UIButton, imageName: String) {
let image = UIImage(named: imageName)
assert(image != nil)
button.setImage(image, for: .selected)
}
func updateAvatarImage() { func updateAvatarImage() {
contactAvatarView.image = OWSAvatarBuilder.buildImage(thread: thread, diameter: 400, contactsManager: contactsManager) contactAvatarView.image = OWSAvatarBuilder.buildImage(thread: thread, diameter: 400, contactsManager: contactsManager)
} }
func createIncomingCallControls() { func createIncomingCallControls() {
acceptIncomingButton = createButton(imageName: "call-active-wide", acceptIncomingButton = createButton(image: #imageLiteral(resourceName: "call-active-wide"),
action: #selector(didPressAnswerCall)) action: #selector(didPressAnswerCall))
acceptIncomingButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_ACCEPT_INCOMING_CALL_LABEL", acceptIncomingButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_ACCEPT_INCOMING_CALL_LABEL",
comment: "Accessibility label for accepting incoming calls") comment: "Accessibility label for accepting incoming calls")
declineIncomingButton = createButton(imageName: "hangup-active-wide", declineIncomingButton = createButton(image: #imageLiteral(resourceName: "hangup-active-wide"),
action: #selector(didPressDeclineCall)) action: #selector(didPressDeclineCall))
declineIncomingButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_DECLINE_INCOMING_CALL_LABEL", declineIncomingButton.accessibilityLabel = NSLocalizedString("CALL_VIEW_DECLINE_INCOMING_CALL_LABEL",
comment: "Accessibility label for declining incoming calls") comment: "Accessibility label for declining incoming calls")
incomingCallView = createContainerForCallControls(controlGroups: [ incomingCallControls = UIStackView(arrangedSubviews: [acceptIncomingButton, declineIncomingButton])
[acceptIncomingButton, declineIncomingButton ] incomingCallControls.axis = .horizontal
]) incomingCallControls.alignment = .center
} incomingCallControls.distribution = .equalSpacing
func createContainerForCallControls(controlGroups: [[UIView]]) -> UIView {
let containerView = UIView()
self.view.addSubview(containerView)
var rows: [UIView] = []
for controlGroup in controlGroups {
rows.append(rowWithSubviews(subviews: controlGroup))
}
let rowspacing = ScaleFromIPhone5To7Plus(6, 7)
var prevRow: UIView?
for row in rows {
containerView.addSubview(row)
row.autoHCenterInSuperview()
if prevRow != nil {
row.autoPinEdge(.top, to: .bottom, of: prevRow!, withOffset: rowspacing)
}
prevRow = row
}
containerView.setContentHuggingVerticalHigh() view.addSubview(incomingCallControls)
rows.first!.autoPinEdge(toSuperviewEdge: .top)
rows.last!.autoPinEdge(toSuperviewEdge: .bottom)
return containerView
} }
func createButton(imageName: String, action: Selector) -> UIButton { func createButton(image: UIImage, action: Selector) -> UIButton {
let image = UIImage(named: imageName)
assert(image != nil)
let button = UIButton() let button = UIButton()
button.setImage(image, for: .normal) button.setImage(image, for: .normal)
button.imageEdgeInsets = UIEdgeInsets(top: buttonInset(), button.imageEdgeInsets = UIEdgeInsets(top: buttonInset(),
@ -481,134 +473,81 @@ class CallViewController: OWSViewController, CallObserver, CallServiceObserver,
return button return button
} }
// Creates a row containing a given set of subviews.
func rowWithSubviews(subviews: [UIView]) -> UIView {
let row = UIView()
row.setContentHuggingVerticalHigh()
row.autoSetDimension(.height, toSize: buttonSize())
if subviews.count > 1 {
// If there's more than one subview in the row,
// space them evenly within the row.
var lastSubview: UIView?
for subview in subviews {
row.addSubview(subview)
subview.setContentHuggingHorizontalHigh()
subview.autoVCenterInSuperview()
if lastSubview != nil {
let spacer = UIView()
spacer.isHidden = true
row.addSubview(spacer)
spacer.autoPinEdge(.left, to: .right, of: lastSubview!)
spacer.autoPinEdge(.right, to: .left, of: subview)
spacer.setContentHuggingHorizontalLow()
spacer.autoVCenterInSuperview()
if subviews.count == 2 {
// special case to hardcode the spacer's size when there is only 1 spacer.
spacer.autoSetDimension(.width, toSize: ScaleFromIPhone5To7Plus(46, 60))
} else {
spacer.autoSetDimension(.width, toSize: ScaleFromIPhone5To7Plus(3, 5))
}
}
lastSubview = subview
}
subviews.first!.autoPinEdge(toSuperviewEdge: .left)
subviews.last!.autoPinEdge(toSuperviewEdge: .right)
} else if subviews.count == 1 {
// If there's only one subview in this row, center it.
let subview = subviews.first!
row.addSubview(subview)
subview.autoVCenterInSuperview()
subview.autoPinWidthToSuperview()
}
return row
}
// MARK: - Layout // MARK: - Layout
override func updateViewConstraints() { func createViewConstraints() {
if !hasConstraints { let topMargin = CGFloat(40)
// We only want to create our constraints once. let contactVSpacing = CGFloat(3)
// let settingsNagHMargin = CGFloat(30)
// Note that constraints are also created elsewhere. let ongoingBottomMargin = ScaleFromIPhone5To7Plus(23, 41)
// This only creates the constraints for the top-level contents of the view. let incomingHMargin = ScaleFromIPhone5To7Plus(30, 56)
hasConstraints = true let incomingBottomMargin = CGFloat(41)
let settingsNagBottomMargin = CGFloat(41)
let topMargin = CGFloat(40) let avatarTopSpacing = ScaleFromIPhone5To7Plus(25, 50)
let contactHMargin = CGFloat(5) // The buttons have built-in 10% margins, so to appear centered
let contactVSpacing = CGFloat(3) // the avatar's bottom spacing should be a bit less.
let ongoingHMargin = ScaleFromIPhone5To7Plus(46, 72) let avatarBottomSpacing = ScaleFromIPhone5To7Plus(18, 41)
let incomingHMargin = ScaleFromIPhone5To7Plus(46, 72) // Layout of the local video view is a bit unusual because
let settingsNagHMargin = CGFloat(30) // although the view is square, it will be used
let ongoingBottomMargin = ScaleFromIPhone5To7Plus(23, 41) let videoPreviewHMargin = CGFloat(0)
let incomingBottomMargin = CGFloat(41)
let settingsNagBottomMargin = CGFloat(41)
let avatarTopSpacing = ScaleFromIPhone5To7Plus(25, 50)
// The buttons have built-in 10% margins, so to appear centered
// the avatar's bottom spacing should be a bit less.
let avatarBottomSpacing = ScaleFromIPhone5To7Plus(18, 41)
// Layout of the local video view is a bit unusual because
// although the view is square, it will be used
let videoPreviewHMargin = CGFloat(0)
// Dark blurred background.
blurView.autoPinEdgesToSuperviewEdges()
localVideoView.autoPinTrailingToSuperviewMargin(withInset: videoPreviewHMargin)
localVideoView.autoPinEdge(toSuperviewEdge: .top, withInset: topMargin)
let localVideoSize = ScaleFromIPhone5To7Plus(80, 100)
localVideoView.autoSetDimension(.width, toSize: localVideoSize)
localVideoView.autoSetDimension(.height, toSize: localVideoSize)
remoteVideoView.autoPinEdgesToSuperviewEdges()
contactNameLabel.autoPinEdge(toSuperviewEdge: .top, withInset: topMargin)
contactNameLabel.autoPinLeadingToSuperviewMargin(withInset: contactHMargin)
contactNameLabel.setContentHuggingVerticalHigh()
contactNameLabel.setCompressionResistanceHigh()
callStatusLabel.autoPinEdge(.top, to: .bottom, of: contactNameLabel, withOffset: contactVSpacing)
callStatusLabel.autoPinLeadingToSuperviewMargin(withInset: contactHMargin)
callStatusLabel.setContentHuggingVerticalHigh()
callStatusLabel.setCompressionResistanceHigh()
contactAvatarContainerView.autoPinEdge(.top, to: .bottom, of: callStatusLabel, withOffset: +avatarTopSpacing)
contactAvatarContainerView.autoPinEdge(.bottom, to: .top, of: ongoingCallView, withOffset: -avatarBottomSpacing)
contactAvatarContainerView.autoPinWidthToSuperview(withMargin: avatarTopSpacing)
contactAvatarView.autoCenterInSuperview()
// Ensure ContacAvatarView gets as close as possible to it's superview edges while maintaining
// aspect ratio.
contactAvatarView.autoPinToSquareAspectRatio()
contactAvatarView.autoPinEdge(toSuperviewEdge: .top, withInset: 0, relation: .greaterThanOrEqual)
contactAvatarView.autoPinEdge(toSuperviewEdge: .right, withInset: 0, relation: .greaterThanOrEqual)
contactAvatarView.autoPinEdge(toSuperviewEdge: .bottom, withInset: 0, relation: .greaterThanOrEqual)
contactAvatarView.autoPinEdge(toSuperviewEdge: .left, withInset: 0, relation: .greaterThanOrEqual)
NSLayoutConstraint.autoSetPriority(UILayoutPriorityDefaultLow) {
contactAvatarView.autoPinEdgesToSuperviewMargins()
}
// Ongoing call controls // Dark blurred background.
ongoingCallView.autoPinEdge(toSuperviewEdge: .bottom, withInset: ongoingBottomMargin) blurView.autoPinEdgesToSuperviewEdges()
ongoingCallView.autoPinWidthToSuperview(withMargin: ongoingHMargin)
ongoingCallView.setContentHuggingVerticalHigh() localVideoView.autoPinTrailingToSuperviewMargin(withInset: videoPreviewHMargin)
localVideoView.autoPinEdge(toSuperviewEdge: .top, withInset: topMargin)
let localVideoSize = ScaleFromIPhone5To7Plus(80, 100)
localVideoView.autoSetDimension(.width, toSize: localVideoSize)
localVideoView.autoSetDimension(.height, toSize: localVideoSize)
remoteVideoView.autoPinEdgesToSuperviewEdges()
contactNameLabel.autoPinEdge(toSuperviewEdge: .top, withInset: topMargin)
contactNameLabel.autoPinLeadingToSuperviewMargin()
contactNameLabel.setContentHuggingVerticalHigh()
contactNameLabel.setCompressionResistanceHigh()
callStatusLabel.autoPinEdge(.top, to: .bottom, of: contactNameLabel, withOffset: contactVSpacing)
callStatusLabel.autoPinLeadingToSuperviewMargin()
callStatusLabel.setContentHuggingVerticalHigh()
callStatusLabel.setCompressionResistanceHigh()
contactAvatarContainerView.autoPinEdge(.top, to: .bottom, of: callStatusLabel, withOffset: +avatarTopSpacing)
contactAvatarContainerView.autoPinEdge(.bottom, to: .top, of: ongoingCallControls, withOffset: -avatarBottomSpacing)
contactAvatarContainerView.autoPinWidthToSuperview(withMargin: avatarTopSpacing)
contactAvatarView.autoCenterInSuperview()
// Ensure ContacAvatarView gets as close as possible to it's superview edges while maintaining
// aspect ratio.
contactAvatarView.autoPinToSquareAspectRatio()
contactAvatarView.autoPinEdge(toSuperviewEdge: .top, withInset: 0, relation: .greaterThanOrEqual)
contactAvatarView.autoPinEdge(toSuperviewEdge: .right, withInset: 0, relation: .greaterThanOrEqual)
contactAvatarView.autoPinEdge(toSuperviewEdge: .bottom, withInset: 0, relation: .greaterThanOrEqual)
contactAvatarView.autoPinEdge(toSuperviewEdge: .left, withInset: 0, relation: .greaterThanOrEqual)
NSLayoutConstraint.autoSetPriority(UILayoutPriorityDefaultLow) {
contactAvatarView.autoPinEdgesToSuperviewMargins()
}
// Incoming call controls // Ongoing call controls
incomingCallView.autoPinEdge(toSuperviewEdge: .bottom, withInset: incomingBottomMargin) ongoingCallControls.autoPinEdge(toSuperviewEdge: .bottom, withInset: ongoingBottomMargin)
incomingCallView.autoPinWidthToSuperview(withMargin: incomingHMargin) ongoingCallControls.autoPinLeadingToSuperviewMargin()
incomingCallView.setContentHuggingVerticalHigh() ongoingCallControls.autoPinTrailingToSuperviewMargin()
ongoingCallControls.setContentHuggingVerticalHigh()
// Settings nag views // Incoming call controls
settingsNagView.autoPinEdge(toSuperviewEdge: .bottom, withInset: settingsNagBottomMargin) incomingCallControls.autoPinEdge(toSuperviewEdge: .bottom, withInset: incomingBottomMargin)
settingsNagView.autoPinWidthToSuperview(withMargin: settingsNagHMargin) incomingCallControls.autoPinLeadingToSuperviewMargin(withInset: incomingHMargin)
settingsNagView.autoPinEdge(.top, to: .bottom, of: callStatusLabel) incomingCallControls.autoPinTrailingToSuperviewMargin(withInset: incomingHMargin)
} incomingCallControls.setContentHuggingVerticalHigh()
// Settings nag views
settingsNagView.autoPinEdge(toSuperviewEdge: .bottom, withInset: settingsNagBottomMargin)
settingsNagView.autoPinWidthToSuperview(withMargin: settingsNagHMargin)
settingsNagView.autoPinEdge(.top, to: .bottom, of: callStatusLabel)
}
override func updateViewConstraints() {
updateRemoteVideoLayout() updateRemoteVideoLayout()
updateLocalVideoLayout() updateLocalVideoLayout()
@ -738,7 +677,7 @@ class CallViewController: OWSViewController, CallObserver, CallServiceObserver,
if isShowingSettingsNag { if isShowingSettingsNag {
settingsNagView.isHidden = false settingsNagView.isHidden = false
contactAvatarView.isHidden = true contactAvatarView.isHidden = true
ongoingCallView.isHidden = true ongoingCallControls.isHidden = true
return return
} }
@ -752,10 +691,10 @@ class CallViewController: OWSViewController, CallObserver, CallServiceObserver,
// Show Incoming vs. Ongoing call controls // Show Incoming vs. Ongoing call controls
let isRinging = callState == .localRinging let isRinging = callState == .localRinging
incomingCallView.isHidden = !isRinging incomingCallControls.isHidden = !isRinging
incomingCallView.isUserInteractionEnabled = isRinging incomingCallControls.isUserInteractionEnabled = isRinging
ongoingCallView.isHidden = isRinging ongoingCallControls.isHidden = isRinging
ongoingCallView.isUserInteractionEnabled = !isRinging ongoingCallControls.isUserInteractionEnabled = !isRinging
// Rework control state if remote video is available. // Rework control state if remote video is available.
let hasRemoteVideo = !remoteVideoView.isHidden let hasRemoteVideo = !remoteVideoView.isHidden
@ -764,18 +703,19 @@ class CallViewController: OWSViewController, CallObserver, CallServiceObserver,
// Rework control state if local video is available. // Rework control state if local video is available.
let hasLocalVideo = !localVideoView.isHidden let hasLocalVideo = !localVideoView.isHidden
for subview in [audioModeMuteButton, audioModeVideoButton] { if hasLocalVideo {
subview?.isHidden = hasLocalVideo ongoingAudioCallControls.removeFromSuperview()
} ongoingCallControls.insertArrangedSubview(ongoingVideoCallControls, at: 0)
for subview in [videoModeMuteButton, videoModeVideoButton] { } else {
subview?.isHidden = !hasLocalVideo ongoingVideoCallControls.removeFromSuperview()
ongoingCallControls.insertArrangedSubview(ongoingAudioCallControls, at: 0)
} }
// Also hide other controls if user has tapped to hide them. // Also hide other controls if user has tapped to hide them.
if shouldRemoteVideoControlsBeHidden && !remoteVideoView.isHidden { if shouldRemoteVideoControlsBeHidden && !remoteVideoView.isHidden {
contactNameLabel.isHidden = true contactNameLabel.isHidden = true
callStatusLabel.isHidden = true callStatusLabel.isHidden = true
ongoingCallView.isHidden = true ongoingCallControls.isHidden = true
} else { } else {
contactNameLabel.isHidden = false contactNameLabel.isHidden = false
callStatusLabel.isHidden = false callStatusLabel.isHidden = false
@ -915,6 +855,16 @@ class CallViewController: OWSViewController, CallObserver, CallServiceObserver,
callUIAdapter.setHasLocalVideo(call: call, hasLocalVideo: hasLocalVideo) callUIAdapter.setHasLocalVideo(call: call, hasLocalVideo: hasLocalVideo)
} }
func didPressFlipCamera(sender: UIButton) {
// toggle value
sender.isSelected = !sender.isSelected
let useBackCamera = sender.isSelected
Logger.info("\(TAG) in \(#function) with useBackCamera: \(useBackCamera)")
callUIAdapter.setCameraSource(call: call, useBackCamera: useBackCamera)
}
/** /**
* Denies an incoming not-yet-connected call, Do not confuse with `didPressHangup`. * Denies an incoming not-yet-connected call, Do not confuse with `didPressHangup`.
*/ */

@ -790,6 +790,8 @@ protocol CallServiceObserver: class {
Logger.info("\(self.logTag) in \(#function): \(call.identifiersForLogs).") Logger.info("\(self.logTag) in \(#function): \(call.identifiersForLogs).")
switch call.state { switch call.state {
case .remoteRinging:
Logger.debug("\(self.logTag) in \(#function) disconnect while ringing... we'll keep ringing")
case .connected: case .connected:
call.state = .reconnecting call.state = .reconnecting
default: default:
@ -1164,6 +1166,22 @@ protocol CallServiceObserver: class {
self.setHasLocalVideo(hasLocalVideo: true) self.setHasLocalVideo(hasLocalVideo: true)
} }
func setCameraSource(call: SignalCall, useBackCamera: Bool) {
SwiftAssertIsOnMainThread(#function)
guard call == self.call else {
owsFail("\(logTag) in \(#function) for non-current call.")
return
}
guard let peerConnectionClient = self.peerConnectionClient else {
owsFail("\(logTag) in \(#function) peerConnectionClient was unexpectedly nil")
return
}
peerConnectionClient.setCameraSource(useBackCamera: useBackCamera)
}
/** /**
* Local client received a message on the WebRTC data channel. * Local client received a message on the WebRTC data channel.
* *

@ -120,6 +120,8 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
private var videoCaptureSession: AVCaptureSession? private var videoCaptureSession: AVCaptureSession?
private var videoSender: RTCRtpSender? private var videoSender: RTCRtpSender?
private var localVideoTrack: RTCVideoTrack? private var localVideoTrack: RTCVideoTrack?
private var localVideoSource: RTCAVFoundationVideoSource?
// RTCVideoTrack is fragile and prone to throwing exceptions and/or // RTCVideoTrack is fragile and prone to throwing exceptions and/or
// causing deadlock in its destructor. Therefore we take great care // causing deadlock in its destructor. Therefore we take great care
// with this property. // with this property.
@ -203,6 +205,8 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
// TODO: Revisit the cameraConstraints. // TODO: Revisit the cameraConstraints.
let videoSource = factory.avFoundationVideoSource(with: cameraConstraints) let videoSource = factory.avFoundationVideoSource(with: cameraConstraints)
self.localVideoSource = videoSource
self.videoCaptureSession = videoSource.captureSession self.videoCaptureSession = videoSource.captureSession
videoSource.useBackCamera = false videoSource.useBackCamera = false
@ -220,6 +224,21 @@ class PeerConnectionClient: NSObject, RTCPeerConnectionDelegate, RTCDataChannelD
self.videoSender = videoSender self.videoSender = videoSender
} }
public func setCameraSource(useBackCamera: Bool) {
guard let localVideoSource = self.localVideoSource else {
owsFail("\(logTag) in \(#function) localVideoSource was unexpectedly nil")
return
}
// certain devices, e.g. 16GB iPod touch don't have a back camera
guard localVideoSource.canUseBackCamera else {
owsFail("\(logTag) in \(#function) canUseBackCamera was unexpectedly false")
return
}
localVideoSource.useBackCamera = useBackCamera
}
public func setLocalVideoEnabled(enabled: Bool) { public func setLocalVideoEnabled(enabled: Bool) {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)

@ -87,11 +87,14 @@ extension CallUIAdaptee {
private let adaptee: CallUIAdaptee private let adaptee: CallUIAdaptee
private let contactsManager: OWSContactsManager private let contactsManager: OWSContactsManager
internal let audioService: CallAudioService internal let audioService: CallAudioService
internal let callService: CallService
required init(callService: CallService, contactsManager: OWSContactsManager, notificationsAdapter: CallNotificationsAdapter) { required init(callService: CallService, contactsManager: OWSContactsManager, notificationsAdapter: CallNotificationsAdapter) {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)
self.contactsManager = contactsManager self.contactsManager = contactsManager
self.callService = callService
if Platform.isSimulator { if Platform.isSimulator {
// CallKit doesn't seem entirely supported in simulator. // CallKit doesn't seem entirely supported in simulator.
// e.g. you can't receive calls in the call screen. // e.g. you can't receive calls in the call screen.
@ -247,6 +250,12 @@ extension CallUIAdaptee {
call.audioSource = audioSource call.audioSource = audioSource
} }
internal func setCameraSource(call: SignalCall, useBackCamera: Bool) {
SwiftAssertIsOnMainThread(#function)
callService.setCameraSource(call: call, useBackCamera: useBackCamera)
}
// CallKit handles ringing state on it's own. But for non-call kit we trigger ringing start/stop manually. // CallKit handles ringing state on it's own. But for non-call kit we trigger ringing start/stop manually.
internal var hasManualRinger: Bool { internal var hasManualRinger: Bool {
SwiftAssertIsOnMainThread(#function) SwiftAssertIsOnMainThread(#function)

Loading…
Cancel
Save