- Updated scheduling/group call tunnel

- Synchronized ConferenceViewModel with Android
This commit is contained in:
Christophe Deschamps 2022-06-09 11:47:20 +02:00
parent 1a9c6060c2
commit 8c66c4f96a
6 changed files with 88 additions and 66 deletions

View file

@ -167,6 +167,9 @@ class ConferenceSchedulingViewModel {
}.first
continueEnabled.value = false
selectedAddresses.value = []
}
@ -190,23 +193,11 @@ class ConferenceSchedulingViewModel {
do {
conferenceCreationInProgress.value = true
guard let localAddress = core.defaultAccount?.params?.identityAddress else {
guard let localAccount = core.defaultAccount, let localAddress = localAccount.params?.identityAddress else {
Log.e("[Conference Creation] Couldn't get local address from default account!")
return
}
/*
// TODO: Temporary workaround for chat room, to be removed once we can get matching chat room from conference
let chatRoomParams = try core.createDefaultChatRoomParams()
chatRoomParams.backend = ChatRoomBackend.FlexisipChat
chatRoomParams.groupEnabled = true
chatRoomParams.subject = subject.value!
let chatRoom = try core.createChatRoom(params: chatRoomParams, localAddr: localAddress, participants: selectedAddresses.value!)
Log.i("[Conference Creation] Creating chat room with same subject [\(subject.value)] & participants as for conference")
chatRoom.addDelegate(delegate: chatRooomDelegate!)
// END OF TODO
*/
let conferenceInfo = try Factory.Instance.createConferenceInfo()
conferenceInfo.organizer = localAddress
subject.value.map { conferenceInfo.subject = $0}
@ -217,6 +208,7 @@ class ConferenceSchedulingViewModel {
conferenceInfo.dateTime = time_t(timestamp)
scheduledDuration.value.map { conferenceInfo.duration = UInt(ConferenceSchedulingViewModel.durationList[$0].value) }
}
conferenceScheduler?.account = localAccount
conferenceScheduler?.info = conferenceInfo // Will trigger the conference creation automatically
} catch {

View file

@ -140,7 +140,7 @@ import SVProgressHUD
contentView.addSubview(viaChatLabel)
viaChatLabel.matchParentSideBorders(insetedByDx: form_margin).alignUnder(view: schedulingStack,withMargin: 2*form_margin).done()
ConferenceSchedulingViewModel.shared.sendInviteViaChat.readCurrentAndObserve { (sendChat) in
viaChatLabel.isHidden = sendChat != true
viaChatLabel.isHidden = sendChat != true || ConferenceSchedulingViewModel.shared.scheduleForLater.value != true
}
// Participants
@ -173,8 +173,8 @@ import SVProgressHUD
let createButton = FormButton(backgroundStateColors: VoipTheme.primary_colors_background)
contentView.addSubview(createButton)
ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { _ in
createButton.title = ConferenceSchedulingViewModel.shared.scheduleForLater.value == true ? VoipTexts.conference_schedule.uppercased() : VoipTexts.conference_schedule_create.uppercased()
createButton.addSidePadding()
createButton.title = ConferenceSchedulingViewModel.shared.scheduleForLater.value == true ? VoipTexts.conference_schedule_start.uppercased() : VoipTexts.conference_group_call_create.uppercased()
createButton.addSidePadding()
}
ConferenceSchedulingViewModel.shared.conferenceCreationInProgress.observe { progress in
@ -211,8 +211,9 @@ import SVProgressHUD
}
}
}
ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { _ in
createButton.title = ConferenceSchedulingViewModel.shared.scheduleForLater.value == true ? VoipTexts.conference_schedule.uppercased() : VoipTexts.conference_schedule_create.uppercased()
ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { (later) in
createButton.title = ConferenceSchedulingViewModel.shared.scheduleForLater.value == true ? VoipTexts.conference_schedule_start.uppercased() : VoipTexts.conference_group_call_create.uppercased()
viaChatLabel.isHidden = later != true || ConferenceSchedulingViewModel.shared.sendInviteViaChat.value != true
createButton.addSidePadding()
}

View file

@ -45,7 +45,7 @@ import linphonesw
self.gotoParticipantsListSelection()
},
nextActionEnableCondition: ConferenceSchedulingViewModel.shared.continueEnabled,
title:VoipTexts.conference_schedule_title)
title:VoipTexts.conference_group_call_title)
let subjectLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_subject_title)
subjectLabel.addIndicatorIcon(iconName: "voip_mandatory")
@ -78,7 +78,6 @@ import linphonesw
let scheduleForm = UIView()
schedulingStack.addArrangedSubview(scheduleForm)
scheduleForm.matchParentSideBorders().done()
ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { (forLater) in scheduleForm.isHidden = forLater != true }
// Left column (Date & Time)
let leftColumn = UIView()
@ -146,6 +145,8 @@ import linphonesw
contentView.addSubview(viaChatLabel)
viaChatLabel.toRightOf(viaChatSwitch,withLeftMargin: form_margin).alignUnder(view: schedulingStack,withMargin: 2*form_margin).alignHorizontalCenterWith(viaChatSwitch).done()
/* Hidden as in Android 9.6.2022
let viaMailSwitch = StyledCheckBox(liveValue: ConferenceSchedulingViewModel.shared.sendInviteViaEmail)
contentView.addSubview(viaMailSwitch)
viaMailSwitch.alignParentLeft(withMargin: form_margin).alignUnder(view: viaChatSwitch,withMargin: 2*form_margin).done()
@ -153,7 +154,6 @@ import linphonesw
let viaMailLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_send_invite_email)
contentView.addSubview(viaMailLabel)
viaMailLabel.toRightOf(viaMailSwitch,withLeftMargin: form_margin).alignUnder(view: viaChatLabel,withMargin: 2*form_margin).alignHorizontalCenterWith(viaMailSwitch).done()
// Encryption
let encryptLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_encryption)
@ -175,17 +175,27 @@ import linphonesw
let encryptedIcon = UIImageView(image: UIImage(named: "security_toggle_icon_green"))
encryptedIcon.contentMode = .scaleAspectFit
encryptCombo.addArrangedSubview(encryptedIcon)
*/
// Mandatory label
let mandatoryLabel = StyledLabel(VoipTheme.conference_scheduling_font, VoipTexts.conference_schedule_mandatory_field)
mandatoryLabel.addIndicatorIcon(iconName: "voip_mandatory", trailing: false)
contentView.addSubview(mandatoryLabel)
mandatoryLabel.alignUnder(view: encryptCombo,withMargin: 4*form_margin).centerX().matchParentSideBorders().done()
mandatoryLabel.alignUnder(view: viaChatSwitch,withMargin: 2*form_margin).centerX().matchParentSideBorders().done()
mandatoryLabel.textAlignment = .center
mandatoryLabel.alignParentBottom().done()
// Schedule for later observer
ConferenceSchedulingViewModel.shared.scheduleForLater.readCurrentAndObserve { (forLater) in
scheduleForm.isHidden = forLater != true
super.titleLabel.text = forLater == true ? VoipTexts.conference_schedule_title : VoipTexts.conference_group_call_title
viaChatSwitch.isHidden = forLater != true
viaChatLabel.isHidden = forLater != true
}
}

View file

@ -59,7 +59,8 @@ import UIKit
static let call_remotely_paused_title = NSLocalizedString("Call has been paused by remote.",comment:"")
// Conference
static let conference_schedule_title = NSLocalizedString("Start a conference",comment:"")
static let conference_schedule_title = NSLocalizedString("Schedule a meeting",comment:"")
static let conference_group_call_title = NSLocalizedString("Start a group call",comment:"")
static let conference_schedule_later = NSLocalizedString("Do you want to schedule this conference for later?",comment:"")
static let conference_schedule_mandatory_field = NSLocalizedString("Mandatory",comment:"")
static let conference_schedule_subject_title = NSLocalizedString("Subject",comment:"")
@ -77,15 +78,17 @@ import UIKit
static let conference_schedule_send_invite_chat_summary = NSLocalizedString("Invite will be sent out from my \(appName) account",comment:"")
static let conference_schedule_participants_list = NSLocalizedString("Participants list",comment:"")
static let conference_schedule_summary = NSLocalizedString("Conference info",comment:"")
static let conference_schedule_create = NSLocalizedString("Create conference",comment:"")
static let conference_schedule_start = NSLocalizedString("Schedule meeting",comment:"")
static let conference_group_call_create = NSLocalizedString("Start group call",comment:"")
static let conference_schedule = NSLocalizedString("Schedule conference",comment:"")
static let conference_schedule_address_copied_to_clipboard = NSLocalizedString("Conference address copied into clipboard",comment:"")
static let conference_schedule_creation_failure = NSLocalizedString("Failed to create conference!",comment:"")
static let conference_schedule_info_not_sent_to_participant = NSLocalizedString("Failed to send conference info to a participant",comment:"")
static let conference_paused_title = NSLocalizedString("You are currently out of the conference.",comment:"")
static let conference_paused_subtitle = NSLocalizedString("Click on play button to join it back.",comment:"")
static let conference_default_title = NSLocalizedString("Remote conference",comment:"")
static let conference_local_title = NSLocalizedString("Local conference",comment:"")
static let conference_default_title = NSLocalizedString("Remote group call",comment:"")
static let conference_local_title = NSLocalizedString("Local group call",comment:"")
static let conference_invite_title = NSLocalizedString("Conference invite:",comment:"")
static let conference_description_title = NSLocalizedString("Description:",comment:"")
static let conference_invite_join = NSLocalizedString("Join",comment:"")

View file

@ -90,7 +90,7 @@ class CallData {
let conference = call.conference
isInRemoteConference.value = conference != nil || isCallingAConference()
if (conference != nil) {
remoteConferenceSubject.value = conference?.subject != nil && (conference?.subject.count)! > 0 ? conference!.subject : VoipTexts.conference_default_title
remoteConferenceSubject.value = ConferenceViewModel.getConferenceSubject(conference: conference!)
}
isOutgoing.value = isOutGoing()
isIncoming.value = isInComing()

View file

@ -37,17 +37,21 @@ class ConferenceViewModel {
let conferenceCreationPending = MutableLiveData<Bool>()
let conferenceParticipants = MutableLiveData<[ConferenceParticipantData]>()
let conferenceParticipantDevices = MutableLiveData<[ConferenceParticipantDeviceData]>()
let conferenceDisplayMode = MutableLiveData<ConferenceLayout>()
let conferenceDisplayMode = MutableLiveData<ConferenceDisplayMode>()
let isRecording = MutableLiveData<Bool>()
let isRemotelyRecorded = MutableLiveData<Bool>()
let participantAdminStatusChangedEvent = MutableLiveData<ConferenceParticipantData>()
let maxParticipantsForMosaicLayout = ConfigManager.instance().lpConfigIntForKey(key: "max_conf_part_mosaic_layout",defaultValue: 6)
let speakingParticipant = MutableLiveData<ConferenceParticipantDeviceData>()
let participantAdminStatusChangedEvent = MutableLiveData<ConferenceParticipantData>()
let firstToJoinEvent = MutableLiveData<Bool>()
let allParticipantsLeftEvent = MutableLiveData<Bool>()
private var conferenceDelegate : ConferenceDelegateStub?
private var coreDelegate : CoreDelegateStub?
@ -66,6 +70,9 @@ class ConferenceViewModel {
onParticipantRemoved: {(conference: Conference, participant: Participant) in
Log.i("[Conference] \(conference) \(participant) Participant removed")
self.updateParticipantsList(conference)
if (self.conferenceParticipants.value?.count == 0) {
self.allParticipantsLeftEvent.value = true
}
},
onParticipantDeviceAdded: {(conference: Conference, participantDevice: ParticipantDevice) in
Log.i("[Conference] \(conference) Participant device \(participantDevice) added")
@ -100,7 +107,7 @@ class ConferenceViewModel {
},
onStateChanged: { (conference: Conference, state: Conference.State) in
Log.i("[Conference] State changed: \(state)")
self.isVideoConference.value = conference.currentParams?.isVideoEnabled
self.isVideoConference.value = conference.currentParams?.videoEnabled
if (state == .Created) {
self.configureConference(conference)
self.conferenceCreationPending.value = false
@ -141,8 +148,6 @@ class ConferenceViewModel {
Core.get().addDelegate(delegate: coreDelegate!)
conferenceParticipants.value = []
conferenceParticipantDevices.value = []
conferenceDisplayMode.value = .Grid
subject.value = VoipTexts.conference_default_title
if let conference = core.conference != nil ? core.conference : core.currentCall?.conference {
Log.i("[Conference] Found an existing conference: \(conference) in state \(conference.state)")
@ -182,42 +187,30 @@ class ConferenceViewModel {
func initConference(_ conference: Conference) {
conferenceExists.value = true
self.conference.value = conference
conference.addDelegate(delegate: self.conferenceDelegate!)
isRecording.value = conference.isRecording
subject.value = ConferenceViewModel.getConferenceSubject(conference: conference)
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
changeLayout(layout: ConferenceWaitingRoomViewModel.sharedModel.joinLayout.value!)
updateConferenceLayout(conference: conference)
}
}
func configureConference(_ conference: Conference) {
self.updateParticipantsList(conference)
if (conferenceParticipants.value?.count == 0) {
firstToJoinEvent.value = true
}
self.updateParticipantsDevicesList(conference)
isConferenceLocallyPaused.value = !conference.isIn
self.isMeAdmin.value = conference.me?.isAdmin == true
isVideoConference.value = conference.currentParams?.videoEnabled == true
self.subject.value = conference.subject.isEmpty ? (
conference.me?.isFocus == true ? (
VoipTexts.conference_local_title
) : (
VoipTexts.conference_default_title
)
) : (
conference.subject
)
subject.value = ConferenceViewModel.getConferenceSubject(conference: conference)
updateConferenceLayout(conference: conference)
}
@ -238,12 +231,13 @@ class ConferenceViewModel {
}
func changeLayout(layout: ConferenceLayout) {
func changeLayout(layout: ConferenceDisplayMode) {
Log.i("[Conference] Trying to change conference layout to $layout")
if let conference = conference.value, let call = conference.call, let params = try?call.core?.createCallParams(call: call) {
params.videoEnabled = true // TODO AUdioLonly layout != ConferenceDisplayMode.AUDIO_ONLY
params.conferenceVideoLayout = layout
params.videoEnabled = layout != .AudioOnly
params.conferenceVideoLayout = layout == ConferenceDisplayMode.Grid ? .Grid : .ActiveSpeaker
try?call.update(params: params)
conferenceDisplayMode.value = layout
let list = sortDevicesDataList(devices: conferenceParticipantDevices.value!)
conferenceParticipantDevices.value = list
@ -254,7 +248,7 @@ class ConferenceViewModel {
private func updateConferenceLayout(conference: Conference) {
if let call = conference.call, let params = call.params {
conferenceDisplayMode.value = params.conferenceVideoLayout
conferenceDisplayMode.value = !params.videoEnabled ? ConferenceDisplayMode.AudioOnly : params.conferenceVideoLayout == .Grid ? .Grid : .ActiveSpeaker
let list = sortDevicesDataList(devices: conferenceParticipantDevices.value!)
conferenceParticipantDevices.value = list
Log.i("[Conference] Conference current layout is: \(conferenceDisplayMode.value)")
@ -275,7 +269,7 @@ class ConferenceViewModel {
conferenceParticipantDevices.value = []
}
private func updateParticipantsList(_ conference: Conference) {
self.conferenceParticipants.value?.forEach{ $0.destroy()}
var participants :[ConferenceParticipantData] = []
@ -370,7 +364,7 @@ class ConferenceViewModel {
return devices
}
func togglePlayPause () {
if (isConferenceLocallyPaused.value == true) {
resumeConference()
@ -412,6 +406,25 @@ class ConferenceViewModel {
}
}
static func getConferenceSubject(conference:Conference) -> String? {
if (conference.subject.count > 0) {
return conference.subject
} else {
let conferenceInfo = Core.get().findConferenceInformationFromUri(uri: conference.conferenceAddress!)
if (conferenceInfo != nil) {
return conferenceInfo?.subject
} else {
if (conference.me?.isFocus == true) {
return VoipTexts.conference_local_title
} else {
return VoipTexts.conference_default_title
}
}
}
}
}
@objc class ConferenceViewModelBridge : NSObject {
@ -425,12 +438,15 @@ class ConferenceViewModel {
}
enum FlexDirection {
case ROW
case ROW_REVERSE
case COLUMN
case COLUMN_REVERSE
}
enum ConferenceDisplayMode {
case Grid
case ActiveSpeaker
case AudioOnly
}