forked from mirrors/linphone-iphone
- Waiting room preferences
- Asymetric video - Refactorisation - Conference layout picker in waiting room
This commit is contained in:
parent
b4d6b8c351
commit
5adf3d8a3f
29 changed files with 302 additions and 90 deletions
|
|
@ -274,7 +274,7 @@ import AVFoundation
|
|||
}
|
||||
if (isConference) {
|
||||
lcallParams.videoEnabled = true
|
||||
lcallParams.videoDirection = isVideo ? .SendRecv : .RecvOnly
|
||||
lcallParams.videoDirection = ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.value == true ? .SendRecv : .RecvOnly
|
||||
} else {
|
||||
lcallParams.videoEnabled = isVideo
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2021 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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
|
||||
* aDouble with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
class ConferenceWaitingRoomViewModel: ControlsViewModel {
|
||||
|
||||
|
||||
static let sharedModel = ConferenceWaitingRoomViewModel()
|
||||
|
||||
|
||||
let joinLayout = MutableLiveData<ConferenceLayout>()
|
||||
let joinInProgress = MutableLiveData<Bool>(false)
|
||||
let showLayoutPicker = MutableLiveData<Bool>()
|
||||
|
||||
|
||||
override init() {
|
||||
super.init()
|
||||
self.reset()
|
||||
}
|
||||
|
||||
func reset() {
|
||||
joinLayout.value = .Grid
|
||||
joinInProgress.value = false
|
||||
isMicrophoneMuted.value = !micAuthorized()
|
||||
isMuteMicrophoneEnabled.value = true
|
||||
isSpeakerSelected.value = true
|
||||
isVideoEnabled.value = true
|
||||
isVideoAvailable.value = core.videoCaptureEnabled
|
||||
showLayoutPicker.value = false
|
||||
}
|
||||
|
||||
override func toggleMuteMicrophone() {
|
||||
if (!micAuthorized()) {
|
||||
AVAudioSession.sharedInstance().requestRecordPermission { granted in
|
||||
if granted {
|
||||
self.isMicrophoneMuted.value = self.isMicrophoneMuted.value != true
|
||||
}
|
||||
}
|
||||
}
|
||||
self.isMicrophoneMuted.value = self.isMicrophoneMuted.value != true
|
||||
}
|
||||
|
||||
override func toggleSpeaker() {
|
||||
isSpeakerSelected.value = isSpeakerSelected.value != true
|
||||
}
|
||||
|
||||
override func toggleVideo() {
|
||||
isVideoEnabled.value = isVideoEnabled.value != true
|
||||
}
|
||||
|
||||
override func updateUI() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -38,6 +38,8 @@ import linphonesw
|
|||
let subject = StyledLabel(VoipTheme.conference_preview_subject_font)
|
||||
let localVideo = UIView()
|
||||
let switchCamera = UIImageView(image: UIImage(named:"voip_change_camera")?.tinted(with:.white))
|
||||
let noVideoLabel = StyledLabel(VoipTheme.conference_waiting_room_no_video_font, VoipTexts.conference_waiting_room_video_disabled)
|
||||
|
||||
let buttonsView = UIStackView()
|
||||
let cancel = FormButton(title: VoipTexts.cancel.uppercased(), backgroundStateColors: VoipTheme.primary_colors_background_gray, bold:false)
|
||||
let start = FormButton(title: VoipTexts.conference_waiting_room_start_call.uppercased(), backgroundStateColors: VoipTheme.primary_colors_background)
|
||||
|
|
@ -46,7 +48,6 @@ import linphonesw
|
|||
var conferenceUrl : String? = nil
|
||||
let conferenceSubject = MutableLiveData<String>()
|
||||
|
||||
|
||||
static let compositeDescription = UICompositeViewDescription(ConferenceWaitingRoomFragment.self, statusBar: StatusBarView.self, tabBar: nil, sideMenu: nil, fullscreen: false, isLeftFragment: false,fragmentWith: nil)
|
||||
static func compositeViewDescription() -> UICompositeViewDescription! { return compositeDescription }
|
||||
func compositeViewDescription() -> UICompositeViewDescription! { return type(of: self).compositeDescription }
|
||||
|
|
@ -63,10 +64,38 @@ import linphonesw
|
|||
}
|
||||
|
||||
// Controls
|
||||
let controlsView = ControlsView(showVideo: true)
|
||||
let controlsView = ControlsView(showVideo: true, controlsViewModel: ConferenceWaitingRoomViewModel.sharedModel)
|
||||
view.addSubview(controlsView)
|
||||
controlsView.alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).centerX().done()
|
||||
|
||||
// Layoout picker
|
||||
let layoutPicker = CallControlButton(imageInset : UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8),buttonTheme: VoipTheme.conf_waiting_room_layout_picker, onClickAction: {
|
||||
ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.value = ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.value != true
|
||||
})
|
||||
view.addSubview(layoutPicker)
|
||||
layoutPicker.alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).alignParentRight(withMargin:SharedLayoutConstants.buttons_bottom_margin).done()
|
||||
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinLayout.readCurrentAndObserve { layout in
|
||||
var icon = ""
|
||||
switch (layout!) {
|
||||
case .Grid: icon = "voip_conference_mosaic"; break
|
||||
case .ActiveSpeaker: icon = "voip_conference_active_speaker"; break
|
||||
case .Legacy: icon = "voip_conference_audio_only"; break
|
||||
}
|
||||
layoutPicker.applyTintedIcons(tintedIcons: [UIButton.State.normal.rawValue : TintableIcon(name: icon ,tintColor: LightDarkColor(.white,.white))])
|
||||
}
|
||||
|
||||
let layoutPickerView = ConferenceLayoutPickerView()
|
||||
view.addSubview(layoutPickerView)
|
||||
layoutPickerView.alignAbove(view:layoutPicker,withMargin:button_spacing).alignVerticalCenterWith(layoutPicker).done()
|
||||
|
||||
ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.readCurrentAndObserve { show in
|
||||
layoutPicker.isSelected = show == true
|
||||
layoutPickerView.isHidden = show != true
|
||||
if (show == true) {
|
||||
self.view.bringSubviewToFront(layoutPickerView)
|
||||
}
|
||||
}
|
||||
|
||||
// Form buttons
|
||||
buttonsView.axis = .horizontal
|
||||
|
|
@ -86,25 +115,29 @@ import linphonesw
|
|||
CallManager.instance().terminateCall(call: call.getCobject)
|
||||
}
|
||||
}
|
||||
ConferenceWaitingRoomViewModel.shared.joinInProgress.value = false
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinInProgress.value = false
|
||||
PhoneMainView.instance().popView(self.compositeViewDescription())
|
||||
}
|
||||
|
||||
start.onClick {
|
||||
ConferenceWaitingRoomViewModel.shared.joinInProgress.value = true
|
||||
self.conferenceUrl.map{ CallManager.instance().startCall(addr: $0, isSas: false, isVideo: true, isConference: true) }
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinInProgress.value = true
|
||||
self.conferenceUrl.map{ CallManager.instance().startCall(addr: $0, isSas: false, isVideo: ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.value!, isConference: true) }
|
||||
}
|
||||
|
||||
ConferenceWaitingRoomViewModel.shared.joinInProgress.readCurrentAndObserve { joining in
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinInProgress.readCurrentAndObserve { joining in
|
||||
self.start.isEnabled = joining != true
|
||||
self.localVideo.isHidden = joining == true
|
||||
//self.localVideo.isHidden = joining == true (UX question as video window goes black by the core, better black or hidden ?)
|
||||
self.noVideoLabel.isHidden = joining == true
|
||||
layoutPicker.isHidden = joining == true
|
||||
if (joining == true) {
|
||||
self.view.addSubview(self.conferenceJoinSpinner)
|
||||
self.conferenceJoinSpinner.square(IncomingOutgoingCommonView.spinner_size).center().done()
|
||||
self.conferenceJoinSpinner.startRotation()
|
||||
controlsView.isHidden = true
|
||||
} else {
|
||||
self.conferenceJoinSpinner.stopRotation()
|
||||
self.conferenceJoinSpinner.removeFromSuperview()
|
||||
controlsView.isHidden = false
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -126,11 +159,22 @@ import linphonesw
|
|||
Core.get().videoPreviewEnabled = true
|
||||
}
|
||||
|
||||
self.view.addSubview(noVideoLabel)
|
||||
noVideoLabel.center().done()
|
||||
|
||||
ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.readCurrentAndObserve { videoEnabled in
|
||||
Core.get().videoPreviewEnabled = videoEnabled == true
|
||||
self.localVideo.isHidden = videoEnabled != true
|
||||
self.switchCamera.isHidden = videoEnabled != true
|
||||
self.noVideoLabel.isHidden = videoEnabled == true
|
||||
}
|
||||
|
||||
|
||||
// Audio Routes
|
||||
audioRoutesView = AudioRoutesView()
|
||||
view.addSubview(audioRoutesView!)
|
||||
audioRoutesView!.alignBottomWith(otherView: controlsView).done()
|
||||
ControlsViewModel.shared.audioRoutesSelected.readCurrentAndObserve { (audioRoutesSelected) in
|
||||
ConferenceWaitingRoomViewModel.sharedModel.audioRoutesSelected.readCurrentAndObserve { (audioRoutesSelected) in
|
||||
self.audioRoutesView!.isHidden = audioRoutesSelected != true
|
||||
}
|
||||
audioRoutesView!.alignAbove(view:controlsView,withMargin:SharedLayoutConstants.buttons_bottom_margin).centerX().done()
|
||||
|
|
@ -140,15 +184,17 @@ import linphonesw
|
|||
|
||||
override func viewWillAppear(_ animated: Bool) {
|
||||
super.viewWillAppear(true)
|
||||
ControlsViewModel.shared.audioRoutesSelected.value = false
|
||||
ConferenceWaitingRoomViewModel.sharedModel.audioRoutesSelected.value = false
|
||||
ConferenceWaitingRoomViewModel.sharedModel.reset()
|
||||
Core.get().nativePreviewWindow = localVideo
|
||||
Core.get().videoPreviewEnabled = true
|
||||
Core.get().videoPreviewEnabled = ConferenceWaitingRoomViewModel.sharedModel.isVideoEnabled.value == true
|
||||
}
|
||||
|
||||
override func viewWillDisappear(_ animated: Bool) {
|
||||
ControlsViewModel.shared.fullScreenMode.value = false
|
||||
Core.get().nativePreviewWindow = nil
|
||||
Core.get().videoPreviewEnabled = false
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinInProgress.value = false
|
||||
super.viewWillDisappear(animated)
|
||||
}
|
||||
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2021 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-android
|
||||
* (see https://www.linphone.org).
|
||||
*
|
||||
* 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
|
||||
* aDouble with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
import Foundation
|
||||
import linphonesw
|
||||
|
||||
class ConferenceWaitingRoomViewModel {
|
||||
|
||||
var core : Core { get { Core.get() } }
|
||||
static let shared = ConferenceWaitingRoomViewModel()
|
||||
|
||||
let joinWithVideo = MutableLiveData<Bool>()
|
||||
let layout = MutableLiveData<ConferenceLayout>()
|
||||
let joinInProgress = MutableLiveData<Bool>(false)
|
||||
|
||||
init () {
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
@ -285,6 +285,13 @@ extension UIView {
|
|||
return self
|
||||
}
|
||||
|
||||
func alignVerticalCenterWith(_ view:UIView) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
make.centerX.equalTo(view)
|
||||
}
|
||||
return self
|
||||
}
|
||||
|
||||
|
||||
func toLeftOf(_ view:UIView) -> UIView {
|
||||
snp.makeConstraints { (make) in
|
||||
|
|
|
|||
|
|
@ -29,5 +29,6 @@ extension Conference : CustomStringConvertible {
|
|||
}
|
||||
return "<pointer:\(Unmanaged.passUnretained(self).toOpaque())>"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -95,6 +95,8 @@ import UIKit
|
|||
static let conference_display_no_active_speaker = NSLocalizedString("No active speaker",comment:"")
|
||||
static let conference_waiting_room_start_call = NSLocalizedString("Start",comment:"")
|
||||
static let conference_waiting_room_cancel_call = NSLocalizedString("Cancel",comment:"")
|
||||
static let conference_waiting_room_video_disabled = NSLocalizedString("Video is currently disabled",comment:"")
|
||||
|
||||
static let conference_scheduled = NSLocalizedString("Conferences",comment:"")
|
||||
static let conference_too_many_participants_for_mosaic_layout = NSLocalizedString("You can't change conference layout as there is too many participants",comment:"")
|
||||
static let conference_participant_paused = NSLocalizedString("(paused)",comment:"")
|
||||
|
|
|
|||
|
|
@ -123,7 +123,8 @@ class VoipTheme { // Names & values replicated from Android
|
|||
static let conference_invite_subject_font = TextStyle(fgColor: LightDarkColor(voip_dark_gray,voip_dark_gray), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .left, font: fontName+"-Bold", size: 14.0)
|
||||
static let conference_invite_title_font = TextStyle(fgColor: LightDarkColor(dark_grey_color,dark_grey_color), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .left, font: fontName+"-Bold", size: 16.0)
|
||||
static let conference_preview_subject_font = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .left, font: fontName+"-Regular", size: 24.0)
|
||||
|
||||
static let conference_waiting_room_no_video_font = TextStyle(fgColor: LightDarkColor(.white,.white), bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 16.0)
|
||||
|
||||
static let empty_list_font = TextStyle(fgColor: primaryTextColor, bgColor: LightDarkColor(.clear,.clear), allCaps: false, align: .center, font: fontName+"-Regular", size: 18.0)
|
||||
|
||||
|
||||
|
|
@ -203,7 +204,6 @@ class VoipTheme { // Names & values replicated from Android
|
|||
|
||||
// Buttons Icons (State colors) + Background colors
|
||||
|
||||
|
||||
static let call_terminate = ButtonTheme(
|
||||
tintableStateIcons:[UIButton.State.normal.rawValue : TintableIcon(name: "voip_hangup",tintColor: LightDarkColor(.white,.white))],
|
||||
backgroundStateColors: [
|
||||
|
|
@ -268,6 +268,12 @@ class VoipTheme { // Names & values replicated from Android
|
|||
],
|
||||
backgroundStateColors: button_background)
|
||||
|
||||
// Waiting room layout picker
|
||||
|
||||
static let conf_waiting_room_layout_picker = ButtonTheme(
|
||||
tintableStateIcons:[:],
|
||||
backgroundStateColors: button_toggle_background_reverse)
|
||||
|
||||
// AUdio routes
|
||||
static let route_bluetooth = ButtonTheme(
|
||||
tintableStateIcons:[
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class CallData {
|
|||
isRemotelyPaused.value = isCallRemotelyPaused()
|
||||
canBePaused.value = canCallBePaused()
|
||||
let conference = call.conference
|
||||
isInRemoteConference.value = conference != nil || CallManager.getAppData(call: call.getCobject!)?.isConference == true
|
||||
isInRemoteConference.value = conference != nil || isCallingAConference()
|
||||
if (conference != nil) {
|
||||
remoteConferenceSubject.value = conference?.subject != nil && (conference?.subject.count)! > 0 ? conference!.subject : VoipTexts.conference_default_title
|
||||
}
|
||||
|
|
@ -199,4 +199,8 @@ class CallData {
|
|||
isPaused.value = isCallPaused()
|
||||
}
|
||||
|
||||
func isCallingAConference() -> Bool {
|
||||
return CallManager.getAppData(call: call.getCobject!)?.isConference == true
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,6 +185,6 @@ class CallsViewModel {
|
|||
|
||||
updateUnreadChatCount()
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -181,6 +181,19 @@ class ConferenceViewModel {
|
|||
conference.addDelegate(delegate: self.conferenceDelegate!)
|
||||
isRecording.value = conference.isRecording
|
||||
updateConferenceLayout(conference: conference)
|
||||
|
||||
if let call = core.currentCall, CallManager.getAppData(call: call.getCobject!)?.isConference == true { // Apply waiting room preference
|
||||
if (ConferenceWaitingRoomViewModel.sharedModel.isSpeakerSelected.value == true) {
|
||||
ControlsViewModel.shared.forceSpeakerAudioRoute()
|
||||
} else {
|
||||
ControlsViewModel.shared.forceEarpieceAudioRoute()
|
||||
ControlsViewModel.shared.updateUI()
|
||||
}
|
||||
Core.get().micEnabled = ConferenceWaitingRoomViewModel.sharedModel.isMicrophoneMuted.value != true
|
||||
conference.layout = ConferenceWaitingRoomViewModel.sharedModel.joinLayout.value!
|
||||
updateConferenceLayout(conference: conference)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func configureConference(_ conference: Conference) {
|
||||
|
|
|
|||
|
|
@ -137,10 +137,9 @@ class ControlsViewModel {
|
|||
|
||||
func toggleVideo() {
|
||||
if let conference = core.conference, conference.isIn {
|
||||
if let params = try?core.createConferenceParams(conference:conference) {
|
||||
let videoEnabled = conference.currentParams?.videoEnabled == true
|
||||
params.videoEnabled = !videoEnabled
|
||||
_ = conference.updateParams(params: params)
|
||||
if let currentCall = core.currentCall, let params = try?core.createCallParams(call: currentCall) {
|
||||
params.videoDirection = params.videoDirection == MediaDirection.RecvOnly ? MediaDirection.SendRecv : MediaDirection.RecvOnly
|
||||
try?currentCall.update(params: params)
|
||||
}
|
||||
} else if let currentCall = core.currentCall {
|
||||
let state = currentCall.state
|
||||
|
|
@ -159,7 +158,7 @@ class ControlsViewModel {
|
|||
}
|
||||
|
||||
|
||||
private func updateUI() {
|
||||
func updateUI() {
|
||||
updateVideoAvailable()
|
||||
updateVideoEnabled()
|
||||
updateMicState()
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ import linphonesw
|
|||
|
||||
|
||||
// Controls
|
||||
let controlsView = ControlsView(showVideo: true)
|
||||
let controlsView = ControlsView(showVideo: true, controlsViewModel: ControlsViewModel.shared)
|
||||
view.addSubview(controlsView)
|
||||
controlsView.alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).centerX().done()
|
||||
|
||||
|
|
@ -315,7 +315,11 @@ import linphonesw
|
|||
if (data?.isOutgoing.value == true || data?.isIncoming.value == true) {
|
||||
PhoneMainView.instance().popView(self.compositeViewDescription())
|
||||
} else {
|
||||
PhoneMainView.instance().changeCurrentView(self.compositeViewDescription())
|
||||
if (data!.isCallingAConference()) {
|
||||
PhoneMainView.instance().pop(toView: self.compositeViewDescription())
|
||||
} else {
|
||||
PhoneMainView.instance().changeCurrentView(self.compositeViewDescription())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
PhoneMainView.instance().changeCurrentView(self.compositeViewDescription())
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ import linphonesw
|
|||
cancelCall.alignParentLeft(withMargin:SharedLayoutConstants.margin_call_view_side_controls_buttons).alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).done()
|
||||
|
||||
// Controls
|
||||
let controlsView = ControlsView(showVideo: false)
|
||||
let controlsView = ControlsView(showVideo: false, controlsViewModel: ControlsViewModel.shared)
|
||||
view.addSubview(controlsView)
|
||||
controlsView.alignParentBottom(withMargin:SharedLayoutConstants.buttons_bottom_margin).centerX().done()
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* Copyright (c) 2010-2020 Belledonne Communications SARL.
|
||||
*
|
||||
* This file is part of linphone-iphone
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import Foundation
|
||||
import UIKit
|
||||
|
||||
class ConferenceLayoutPickerView: UIStackView {
|
||||
|
||||
// Layout constants
|
||||
let corner_radius = 6.7
|
||||
let margin = 10.0
|
||||
|
||||
init () {
|
||||
super.init(frame: .zero)
|
||||
axis = .vertical
|
||||
distribution = .equalCentering
|
||||
alignment = .center
|
||||
spacing = ControlsView.controls_button_spacing
|
||||
backgroundColor = VoipTheme.voip_gray
|
||||
layer.cornerRadius = corner_radius
|
||||
clipsToBounds = true
|
||||
|
||||
let padding = UIView()
|
||||
padding.height(margin/2).done()
|
||||
addArrangedSubview(padding)
|
||||
|
||||
|
||||
let grid = CallControlButton(imageInset : UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5),buttonTheme: VoipTheme.conf_waiting_room_layout_picker, onClickAction: {
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinLayout.value = .Grid
|
||||
ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.value = false
|
||||
|
||||
})
|
||||
grid.applyTintedIcons(tintedIcons: [UIButton.State.normal.rawValue : TintableIcon(name: "voip_conference_mosaic" ,tintColor: LightDarkColor(.white,.white))])
|
||||
addArrangedSubview(grid)
|
||||
|
||||
let activeSpeaker = CallControlButton(imageInset : UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5),buttonTheme: VoipTheme.conf_waiting_room_layout_picker, onClickAction: {
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinLayout.value = .ActiveSpeaker
|
||||
ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.value = false
|
||||
})
|
||||
activeSpeaker.applyTintedIcons(tintedIcons: [UIButton.State.normal.rawValue : TintableIcon(name: "voip_conference_active_speaker" ,tintColor: LightDarkColor(.white,.white))])
|
||||
addArrangedSubview(activeSpeaker)
|
||||
|
||||
let audioOnly = CallControlButton(imageInset : UIEdgeInsets(top: 5, left: 5, bottom: 5, right: 5),buttonTheme: VoipTheme.conf_waiting_room_layout_picker, onClickAction: {
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinLayout.value = .Legacy
|
||||
ConferenceWaitingRoomViewModel.sharedModel.showLayoutPicker.value = false
|
||||
})
|
||||
audioOnly.applyTintedIcons(tintedIcons: [UIButton.State.normal.rawValue : TintableIcon(name: "voip_conference_audio_only" ,tintColor: LightDarkColor(.white,.white))])
|
||||
addArrangedSubview(audioOnly)
|
||||
|
||||
ConferenceWaitingRoomViewModel.sharedModel.joinLayout.readCurrentAndObserve { layout in
|
||||
grid.isSelected = layout == .Grid
|
||||
activeSpeaker.isSelected = layout == .ActiveSpeaker
|
||||
audioOnly.isSelected = layout == .Legacy
|
||||
}
|
||||
|
||||
let padding2 = UIView()
|
||||
padding2.height(margin/2).done()
|
||||
addArrangedSubview(padding2)
|
||||
|
||||
|
||||
size(w:CGFloat(CallControlButton.default_size)+margin, h : 3*CGFloat(CallControlButton.default_size)+3*CGFloat(ControlsView.controls_button_spacing)+2*margin).done()
|
||||
|
||||
}
|
||||
|
||||
required init(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ class ControlsView: UIStackView {
|
|||
// Layout constants
|
||||
static let controls_button_spacing = 5.0
|
||||
|
||||
init (showVideo:Bool) {
|
||||
init (showVideo:Bool, controlsViewModel:ControlsViewModel) {
|
||||
super.init(frame: .zero)
|
||||
axis = .horizontal
|
||||
distribution = .equalSpacing
|
||||
|
|
@ -34,35 +34,35 @@ class ControlsView: UIStackView {
|
|||
|
||||
// Mute
|
||||
let mute = CallControlButton(buttonTheme: VoipTheme.call_mute, onClickAction: {
|
||||
ControlsViewModel.shared.toggleMuteMicrophone()
|
||||
controlsViewModel.toggleMuteMicrophone()
|
||||
})
|
||||
addArrangedSubview(mute)
|
||||
ControlsViewModel.shared.isMicrophoneMuted.readCurrentAndObserve { (muted) in
|
||||
controlsViewModel.isMicrophoneMuted.readCurrentAndObserve { (muted) in
|
||||
mute.isSelected = muted == true
|
||||
}
|
||||
ControlsViewModel.shared.isMuteMicrophoneEnabled.readCurrentAndObserve { (enabled) in
|
||||
controlsViewModel.isMuteMicrophoneEnabled.readCurrentAndObserve { (enabled) in
|
||||
mute.isEnabled = enabled == true
|
||||
}
|
||||
|
||||
// Speaker
|
||||
let speaker = CallControlButton(buttonTheme: VoipTheme.call_speaker, onClickAction: {
|
||||
ControlsViewModel.shared.toggleSpeaker()
|
||||
controlsViewModel.toggleSpeaker()
|
||||
})
|
||||
addArrangedSubview(speaker)
|
||||
ControlsViewModel.shared.isSpeakerSelected.readCurrentAndObserve { (selected) in
|
||||
controlsViewModel.isSpeakerSelected.readCurrentAndObserve { (selected) in
|
||||
speaker.isSelected = selected == true
|
||||
}
|
||||
|
||||
// Audio routes
|
||||
let routes = CallControlButton(buttonTheme: VoipTheme.call_audio_route, onClickAction: {
|
||||
ControlsViewModel.shared.toggleRoutesMenu()
|
||||
controlsViewModel.toggleRoutesMenu()
|
||||
})
|
||||
addArrangedSubview(routes)
|
||||
ControlsViewModel.shared.audioRoutesSelected.readCurrentAndObserve { (selected) in
|
||||
controlsViewModel.audioRoutesSelected.readCurrentAndObserve { (selected) in
|
||||
routes.isSelected = selected == true
|
||||
}
|
||||
|
||||
ControlsViewModel.shared.audioRoutesEnabled.readCurrentAndObserve { (routesEnabled) in
|
||||
controlsViewModel.audioRoutesEnabled.readCurrentAndObserve { (routesEnabled) in
|
||||
speaker.isHidden = routesEnabled == true
|
||||
routes.isHidden = !speaker.isHidden
|
||||
}
|
||||
|
|
@ -71,11 +71,11 @@ class ControlsView: UIStackView {
|
|||
if (showVideo) {
|
||||
let video = CallControlButton(buttonTheme: VoipTheme.call_video, onClickAction: {
|
||||
if AVCaptureDevice.authorizationStatus(for: .video) == .authorized {
|
||||
ControlsViewModel.shared.toggleVideo()
|
||||
controlsViewModel.toggleVideo()
|
||||
} else {
|
||||
AVCaptureDevice.requestAccess(for: .video, completionHandler: { (granted: Bool) in
|
||||
if granted {
|
||||
ControlsViewModel.shared.toggleVideo()
|
||||
controlsViewModel.toggleVideo()
|
||||
} else {
|
||||
VoipDialog(message:VoipTexts.camera_required_for_video).show()
|
||||
}
|
||||
|
|
@ -83,15 +83,15 @@ class ControlsView: UIStackView {
|
|||
}
|
||||
})
|
||||
addArrangedSubview(video)
|
||||
video.showActivityIndicatorDataSource = ControlsViewModel.shared.isVideoUpdateInProgress
|
||||
ControlsViewModel.shared.isVideoEnabled.readCurrentAndObserve { (selected) in
|
||||
video.showActivityIndicatorDataSource = controlsViewModel.isVideoUpdateInProgress
|
||||
controlsViewModel.isVideoEnabled.readCurrentAndObserve { (selected) in
|
||||
video.isSelected = selected == true
|
||||
}
|
||||
ControlsViewModel.shared.isVideoAvailable.readCurrentAndObserve { (available) in
|
||||
video.isEnabled = available == true && ControlsViewModel.shared.isVideoUpdateInProgress.value != true
|
||||
controlsViewModel.isVideoAvailable.readCurrentAndObserve { (available) in
|
||||
video.isEnabled = available == true && controlsViewModel.isVideoUpdateInProgress.value != true
|
||||
}
|
||||
ControlsViewModel.shared.isVideoUpdateInProgress.readCurrentAndObserve { (updateInProgress) in
|
||||
video.isEnabled = updateInProgress != true && ControlsViewModel.shared.isVideoAvailable.value == true
|
||||
controlsViewModel.isVideoUpdateInProgress.readCurrentAndObserve { (updateInProgress) in
|
||||
video.isEnabled = updateInProgress != true && controlsViewModel.isVideoAvailable.value == true
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
BIN
Resources/images/voip_conference_audio_only.png
Normal file
BIN
Resources/images/voip_conference_audio_only.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.2 KiB |
|
|
@ -684,6 +684,8 @@
|
|||
C65A5D3F27216E3A005BA038 /* CallData.swift in Sources */ = {isa = PBXBuildFile; fileRef = C65A5D3E27216E3A005BA038 /* CallData.swift */; };
|
||||
C65A5D45272196AE005BA038 /* OptionalExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = C65A5D44272196AE005BA038 /* OptionalExtensions.swift */; };
|
||||
C662D0A927EA2C5F00C02D4A /* ConferenceWaitingRoomViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = C662D0A827EA2C5F00C02D4A /* ConferenceWaitingRoomViewModel.swift */; };
|
||||
C662D0AD27EB874E00C02D4A /* voip_conference_audio_only.png in Resources */ = {isa = PBXBuildFile; fileRef = C662D0AC27EB874E00C02D4A /* voip_conference_audio_only.png */; };
|
||||
C662D0AF27EB894300C02D4A /* ConferenceLayoutPickerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C662D0AE27EB894300C02D4A /* ConferenceLayoutPickerView.swift */; };
|
||||
C666756F264C925800A0273C /* VFSUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DA657B261C950C0020CB43 /* VFSUtil.swift */; };
|
||||
C6667571264C925B00A0273C /* VFSUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6DA657B261C950C0020CB43 /* VFSUtil.swift */; };
|
||||
C66B03BB26E8EB1A009B5EDC /* UIChatReplyBubbleView.xib in Resources */ = {isa = PBXBuildFile; fileRef = C66B03BD26E8EB1A009B5EDC /* UIChatReplyBubbleView.xib */; };
|
||||
|
|
@ -1854,6 +1856,8 @@
|
|||
C65A5D3E27216E3A005BA038 /* CallData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallData.swift; sourceTree = "<group>"; };
|
||||
C65A5D44272196AE005BA038 /* OptionalExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalExtensions.swift; sourceTree = "<group>"; };
|
||||
C662D0A827EA2C5F00C02D4A /* ConferenceWaitingRoomViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConferenceWaitingRoomViewModel.swift; sourceTree = "<group>"; };
|
||||
C662D0AC27EB874E00C02D4A /* voip_conference_audio_only.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_conference_audio_only.png; sourceTree = "<group>"; };
|
||||
C662D0AE27EB894300C02D4A /* ConferenceLayoutPickerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = ConferenceLayoutPickerView.swift; path = Classes/Swift/Voip/Views/Fragments/ConferenceLayoutPickerView.swift; sourceTree = SOURCE_ROOT; };
|
||||
C66B03BC26E8EB1A009B5EDC /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/UIChatReplyBubbleView.xib; sourceTree = "<group>"; };
|
||||
C66B03C126E8EB82009B5EDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/UIChatReplyBubbleView.strings; sourceTree = "<group>"; };
|
||||
C66B03C326E8EB87009B5EDC /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/UIChatReplyBubbleView.strings; sourceTree = "<group>"; };
|
||||
|
|
@ -2666,6 +2670,7 @@
|
|||
633FEBE11D3CD5570014B822 /* images */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C662D0AC27EB874E00C02D4A /* voip_conference_audio_only.png */,
|
||||
C6AF9225275F3D890087ACDE /* voip_conference_new_selected.png */,
|
||||
C6AF921D275E51860087ACDE /* conference_schedule_calendar_default.png */,
|
||||
C6AF9213275D67EB0087ACDE /* conference_schedule_time_default.png */,
|
||||
|
|
@ -3554,16 +3559,17 @@
|
|||
C6EA2F492752237C008E60F8 /* Conference */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C6EA2F4C2754C691008E60F8 /* data */,
|
||||
C6EA2F4B2754C683008E60F8 /* views */,
|
||||
C6EA2F4D2754C69F008E60F8 /* models */,
|
||||
C6EA2F4C2754C691008E60F8 /* Data */,
|
||||
C6EA2F4B2754C683008E60F8 /* Views */,
|
||||
C6EA2F4D2754C69F008E60F8 /* ViewModels */,
|
||||
);
|
||||
path = Conference;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C6EA2F4B2754C683008E60F8 /* views */ = {
|
||||
C6EA2F4B2754C683008E60F8 /* Views */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C662D0AE27EB894300C02D4A /* ConferenceLayoutPickerView.swift */,
|
||||
C690CCB0275764CD00609077 /* ConferenceSchedulingView.swift */,
|
||||
C61E409A275A20A300CCE602 /* ConferenceSchedulingSummaryView.swift */,
|
||||
C6AF920D275D38090087ACDE /* ScheduledConferencesView.swift */,
|
||||
|
|
@ -3572,27 +3578,27 @@
|
|||
C6AF9229275F6BA10087ACDE /* ConferenceHistoryDetailsView.swift */,
|
||||
C6AF9219275E2E010087ACDE /* ConferenceWaitingRoomFragment.swift */,
|
||||
);
|
||||
path = views;
|
||||
path = Views;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C6EA2F4C2754C691008E60F8 /* data */ = {
|
||||
C6EA2F4C2754C691008E60F8 /* Data */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C6EA2F6A2754DDF9008E60F8 /* Duration.swift */,
|
||||
C6EA2F622754DBBE008E60F8 /* TimeZoneData.swift */,
|
||||
C6EA2F582754C91C008E60F8 /* ScheduledConferenceData.swift */,
|
||||
);
|
||||
path = data;
|
||||
path = Data;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
C6EA2F4D2754C69F008E60F8 /* models */ = {
|
||||
C6EA2F4D2754C69F008E60F8 /* ViewModels */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
C6EA2F662754DC45008E60F8 /* ConferenceSchedulingViewModel.swift */,
|
||||
C662D0A827EA2C5F00C02D4A /* ConferenceWaitingRoomViewModel.swift */,
|
||||
C6EA2F662754DC45008E60F8 /* ConferenceSchedulingViewModel.swift */,
|
||||
C6AF9217275E13790087ACDE /* ScheduledConferencesViewModel.swift */,
|
||||
);
|
||||
path = models;
|
||||
path = ViewModels;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D326483415887D4400930C67 /* Utils */ = {
|
||||
|
|
@ -3978,6 +3984,7 @@
|
|||
633FEF1F1D3CD55A0014B822 /* presence_online@2x.png in Resources */,
|
||||
633FEE641D3CD5590014B822 /* footer_chat_disabled.png in Resources */,
|
||||
633FEE9C1D3CD55A0014B822 /* numpad_0_default@2x.png in Resources */,
|
||||
C662D0AD27EB874E00C02D4A /* voip_conference_audio_only.png in Resources */,
|
||||
633FEE3C1D3CD5590014B822 /* contacts_all_default.png in Resources */,
|
||||
633FEE171D3CD5590014B822 /* chat_send_disabled@2x.png in Resources */,
|
||||
C61B1BF22667D075001A4E4A /* menu_security_default.png in Resources */,
|
||||
|
|
@ -5016,6 +5023,7 @@
|
|||
D3807FC315C28940005BE9BC /* DCRoundSwitchOutlineLayer.m in Sources */,
|
||||
CF7602E221086EB200749F76 /* RecordingsListTableView.m in Sources */,
|
||||
D3807FC515C28940005BE9BC /* DCRoundSwitchToggleLayer.m in Sources */,
|
||||
C662D0AF27EB894300C02D4A /* ConferenceLayoutPickerView.swift in Sources */,
|
||||
C6586149273E595700A0DBFC /* VoipExtraButtonsView.swift in Sources */,
|
||||
633E41821D74259000320475 /* AssistantLinkView.m in Sources */,
|
||||
C6710F5727229DEE00ED888F /* TextStyle.swift in Sources */,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue