diff --git a/Classes/Swift/CallManager.swift b/Classes/Swift/CallManager.swift
index 7d3a0eec4..a99fb228b 100644
--- a/Classes/Swift/CallManager.swift
+++ b/Classes/Swift/CallManager.swift
@@ -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
}
diff --git a/Classes/Swift/Conference/data/Duration.swift b/Classes/Swift/Conference/Data/Duration.swift
similarity index 100%
rename from Classes/Swift/Conference/data/Duration.swift
rename to Classes/Swift/Conference/Data/Duration.swift
diff --git a/Classes/Swift/Conference/data/ScheduledConferenceData.swift b/Classes/Swift/Conference/Data/ScheduledConferenceData.swift
similarity index 100%
rename from Classes/Swift/Conference/data/ScheduledConferenceData.swift
rename to Classes/Swift/Conference/Data/ScheduledConferenceData.swift
diff --git a/Classes/Swift/Conference/data/TimeZoneData.swift b/Classes/Swift/Conference/Data/TimeZoneData.swift
similarity index 100%
rename from Classes/Swift/Conference/data/TimeZoneData.swift
rename to Classes/Swift/Conference/Data/TimeZoneData.swift
diff --git a/Classes/Swift/Conference/models/ConferenceSchedulingViewModel.swift b/Classes/Swift/Conference/ViewModels/ConferenceSchedulingViewModel.swift
similarity index 100%
rename from Classes/Swift/Conference/models/ConferenceSchedulingViewModel.swift
rename to Classes/Swift/Conference/ViewModels/ConferenceSchedulingViewModel.swift
diff --git a/Classes/Swift/Conference/ViewModels/ConferenceWaitingRoomViewModel.swift b/Classes/Swift/Conference/ViewModels/ConferenceWaitingRoomViewModel.swift
new file mode 100644
index 000000000..246f5f905
--- /dev/null
+++ b/Classes/Swift/Conference/ViewModels/ConferenceWaitingRoomViewModel.swift
@@ -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 .
+ */
+
+
+import Foundation
+import linphonesw
+
+class ConferenceWaitingRoomViewModel: ControlsViewModel {
+
+
+ static let sharedModel = ConferenceWaitingRoomViewModel()
+
+
+ let joinLayout = MutableLiveData()
+ let joinInProgress = MutableLiveData(false)
+ let showLayoutPicker = MutableLiveData()
+
+
+ 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() {
+
+ }
+
+}
diff --git a/Classes/Swift/Conference/models/ScheduledConferencesViewModel.swift b/Classes/Swift/Conference/ViewModels/ScheduledConferencesViewModel.swift
similarity index 100%
rename from Classes/Swift/Conference/models/ScheduledConferencesViewModel.swift
rename to Classes/Swift/Conference/ViewModels/ScheduledConferencesViewModel.swift
diff --git a/Classes/Swift/Conference/views/ConferenceHistoryDetailsView.swift b/Classes/Swift/Conference/Views/ConferenceHistoryDetailsView.swift
similarity index 100%
rename from Classes/Swift/Conference/views/ConferenceHistoryDetailsView.swift
rename to Classes/Swift/Conference/Views/ConferenceHistoryDetailsView.swift
diff --git a/Classes/Swift/Conference/views/ConferenceSchedulingSummaryView.swift b/Classes/Swift/Conference/Views/ConferenceSchedulingSummaryView.swift
similarity index 100%
rename from Classes/Swift/Conference/views/ConferenceSchedulingSummaryView.swift
rename to Classes/Swift/Conference/Views/ConferenceSchedulingSummaryView.swift
diff --git a/Classes/Swift/Conference/views/ConferenceSchedulingView.swift b/Classes/Swift/Conference/Views/ConferenceSchedulingView.swift
similarity index 100%
rename from Classes/Swift/Conference/views/ConferenceSchedulingView.swift
rename to Classes/Swift/Conference/Views/ConferenceSchedulingView.swift
diff --git a/Classes/Swift/Conference/views/ConferenceWaitingRoomFragment.swift b/Classes/Swift/Conference/Views/ConferenceWaitingRoomFragment.swift
similarity index 63%
rename from Classes/Swift/Conference/views/ConferenceWaitingRoomFragment.swift
rename to Classes/Swift/Conference/Views/ConferenceWaitingRoomFragment.swift
index 5a6857164..c2d1d6f12 100644
--- a/Classes/Swift/Conference/views/ConferenceWaitingRoomFragment.swift
+++ b/Classes/Swift/Conference/Views/ConferenceWaitingRoomFragment.swift
@@ -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()
-
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)
}
diff --git a/Classes/Swift/Conference/views/ICSBubbleView.swift b/Classes/Swift/Conference/Views/ICSBubbleView.swift
similarity index 100%
rename from Classes/Swift/Conference/views/ICSBubbleView.swift
rename to Classes/Swift/Conference/Views/ICSBubbleView.swift
diff --git a/Classes/Swift/Conference/views/ScheduledConferencesCell.swift b/Classes/Swift/Conference/Views/ScheduledConferencesCell.swift
similarity index 100%
rename from Classes/Swift/Conference/views/ScheduledConferencesCell.swift
rename to Classes/Swift/Conference/Views/ScheduledConferencesCell.swift
diff --git a/Classes/Swift/Conference/views/ScheduledConferencesView.swift b/Classes/Swift/Conference/Views/ScheduledConferencesView.swift
similarity index 100%
rename from Classes/Swift/Conference/views/ScheduledConferencesView.swift
rename to Classes/Swift/Conference/Views/ScheduledConferencesView.swift
diff --git a/Classes/Swift/Conference/models/ConferenceWaitingRoomViewModel.swift b/Classes/Swift/Conference/models/ConferenceWaitingRoomViewModel.swift
deleted file mode 100644
index bf428bf79..000000000
--- a/Classes/Swift/Conference/models/ConferenceWaitingRoomViewModel.swift
+++ /dev/null
@@ -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 .
- */
-
-
-import Foundation
-import linphonesw
-
-class ConferenceWaitingRoomViewModel {
-
- var core : Core { get { Core.get() } }
- static let shared = ConferenceWaitingRoomViewModel()
-
- let joinWithVideo = MutableLiveData()
- let layout = MutableLiveData()
- let joinInProgress = MutableLiveData(false)
-
- init () {
-
- }
-
-
-
-}
diff --git a/Classes/Swift/Extensions/IOS/UIVIewExtensions.swift b/Classes/Swift/Extensions/IOS/UIVIewExtensions.swift
index 46008f213..03ccbff0a 100644
--- a/Classes/Swift/Extensions/IOS/UIVIewExtensions.swift
+++ b/Classes/Swift/Extensions/IOS/UIVIewExtensions.swift
@@ -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
diff --git a/Classes/Swift/Extensions/LinphoneCore/ConferenceExtensions.swift b/Classes/Swift/Extensions/LinphoneCore/ConferenceExtensions.swift
index 483f3bc16..986c44007 100644
--- a/Classes/Swift/Extensions/LinphoneCore/ConferenceExtensions.swift
+++ b/Classes/Swift/Extensions/LinphoneCore/ConferenceExtensions.swift
@@ -29,5 +29,6 @@ extension Conference : CustomStringConvertible {
}
return ""
}
+
}
diff --git a/Classes/Swift/Voip/Theme/VoipTexts.swift b/Classes/Swift/Voip/Theme/VoipTexts.swift
index f8e1d2649..60e5bdafd 100644
--- a/Classes/Swift/Voip/Theme/VoipTexts.swift
+++ b/Classes/Swift/Voip/Theme/VoipTexts.swift
@@ -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:"")
diff --git a/Classes/Swift/Voip/Theme/VoipTheme.swift b/Classes/Swift/Voip/Theme/VoipTheme.swift
index 72d6ae29e..47ddd4ca6 100644
--- a/Classes/Swift/Voip/Theme/VoipTheme.swift
+++ b/Classes/Swift/Voip/Theme/VoipTheme.swift
@@ -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:[
diff --git a/Classes/Swift/Voip/ViewModels/CallData.swift b/Classes/Swift/Voip/ViewModels/CallData.swift
index 988c79729..a15fa2270 100644
--- a/Classes/Swift/Voip/ViewModels/CallData.swift
+++ b/Classes/Swift/Voip/ViewModels/CallData.swift
@@ -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
+ }
+
}
diff --git a/Classes/Swift/Voip/ViewModels/CallsViewModel.swift b/Classes/Swift/Voip/ViewModels/CallsViewModel.swift
index 294491b9e..284868eb0 100644
--- a/Classes/Swift/Voip/ViewModels/CallsViewModel.swift
+++ b/Classes/Swift/Voip/ViewModels/CallsViewModel.swift
@@ -185,6 +185,6 @@ class CallsViewModel {
updateUnreadChatCount()
}
-
+
}
diff --git a/Classes/Swift/Voip/ViewModels/ConferenceViewModel.swift b/Classes/Swift/Voip/ViewModels/ConferenceViewModel.swift
index 4a1aeb2c1..345e9f552 100644
--- a/Classes/Swift/Voip/ViewModels/ConferenceViewModel.swift
+++ b/Classes/Swift/Voip/ViewModels/ConferenceViewModel.swift
@@ -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) {
diff --git a/Classes/Swift/Voip/ViewModels/ControlsViewModel.swift b/Classes/Swift/Voip/ViewModels/ControlsViewModel.swift
index 5ee71fd59..35d9f9036 100644
--- a/Classes/Swift/Voip/ViewModels/ControlsViewModel.swift
+++ b/Classes/Swift/Voip/ViewModels/ControlsViewModel.swift
@@ -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()
diff --git a/Classes/Swift/Voip/Views/CompositeViewControllers/ActiveCallOrConferenceView.swift b/Classes/Swift/Voip/Views/CompositeViewControllers/ActiveCallOrConferenceView.swift
index e75519c6f..8583bef83 100644
--- a/Classes/Swift/Voip/Views/CompositeViewControllers/ActiveCallOrConferenceView.swift
+++ b/Classes/Swift/Voip/Views/CompositeViewControllers/ActiveCallOrConferenceView.swift
@@ -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())
diff --git a/Classes/Swift/Voip/Views/CompositeViewControllers/OutgoingCallView.swift b/Classes/Swift/Voip/Views/CompositeViewControllers/OutgoingCallView.swift
index 31286af27..613f05eb4 100644
--- a/Classes/Swift/Voip/Views/CompositeViewControllers/OutgoingCallView.swift
+++ b/Classes/Swift/Voip/Views/CompositeViewControllers/OutgoingCallView.swift
@@ -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()
diff --git a/Classes/Swift/Voip/Views/Fragments/ConferenceLayoutPickerView.swift b/Classes/Swift/Voip/Views/Fragments/ConferenceLayoutPickerView.swift
new file mode 100644
index 000000000..b9c010cc0
--- /dev/null
+++ b/Classes/Swift/Voip/Views/Fragments/ConferenceLayoutPickerView.swift
@@ -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 .
+ */
+
+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")
+ }
+
+}
+
+
diff --git a/Classes/Swift/Voip/Views/Fragments/ControlsView.swift b/Classes/Swift/Voip/Views/Fragments/ControlsView.swift
index 58c285aa5..b091a049c 100644
--- a/Classes/Swift/Voip/Views/Fragments/ControlsView.swift
+++ b/Classes/Swift/Voip/Views/Fragments/ControlsView.swift
@@ -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
}
}
diff --git a/Resources/images/voip_conference_audio_only.png b/Resources/images/voip_conference_audio_only.png
new file mode 100644
index 000000000..fd57a3f27
Binary files /dev/null and b/Resources/images/voip_conference_audio_only.png differ
diff --git a/linphone.xcodeproj/project.pbxproj b/linphone.xcodeproj/project.pbxproj
index 32681ee96..aa112396e 100644
--- a/linphone.xcodeproj/project.pbxproj
+++ b/linphone.xcodeproj/project.pbxproj
@@ -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 = ""; };
C65A5D44272196AE005BA038 /* OptionalExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OptionalExtensions.swift; sourceTree = ""; };
C662D0A827EA2C5F00C02D4A /* ConferenceWaitingRoomViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ConferenceWaitingRoomViewModel.swift; sourceTree = ""; };
+ C662D0AC27EB874E00C02D4A /* voip_conference_audio_only.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = voip_conference_audio_only.png; sourceTree = ""; };
+ 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 = ""; };
C66B03C126E8EB82009B5EDC /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/UIChatReplyBubbleView.strings; sourceTree = ""; };
C66B03C326E8EB87009B5EDC /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = fr.lproj/UIChatReplyBubbleView.strings; sourceTree = ""; };
@@ -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 = "";
};
- 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 = "";
};
- C6EA2F4C2754C691008E60F8 /* data */ = {
+ C6EA2F4C2754C691008E60F8 /* Data */ = {
isa = PBXGroup;
children = (
C6EA2F6A2754DDF9008E60F8 /* Duration.swift */,
C6EA2F622754DBBE008E60F8 /* TimeZoneData.swift */,
C6EA2F582754C91C008E60F8 /* ScheduledConferenceData.swift */,
);
- path = data;
+ path = Data;
sourceTree = "";
};
- C6EA2F4D2754C69F008E60F8 /* models */ = {
+ C6EA2F4D2754C69F008E60F8 /* ViewModels */ = {
isa = PBXGroup;
children = (
- C6EA2F662754DC45008E60F8 /* ConferenceSchedulingViewModel.swift */,
C662D0A827EA2C5F00C02D4A /* ConferenceWaitingRoomViewModel.swift */,
+ C6EA2F662754DC45008E60F8 /* ConferenceSchedulingViewModel.swift */,
C6AF9217275E13790087ACDE /* ScheduledConferencesViewModel.swift */,
);
- path = models;
+ path = ViewModels;
sourceTree = "";
};
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 */,