diff --git a/Classes/Swift/Conference/ViewModels/ConferenceSchedulingViewModel.swift b/Classes/Swift/Conference/ViewModels/ConferenceSchedulingViewModel.swift index 57a9d015b..642b319eb 100644 --- a/Classes/Swift/Conference/ViewModels/ConferenceSchedulingViewModel.swift +++ b/Classes/Swift/Conference/ViewModels/ConferenceSchedulingViewModel.swift @@ -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 { diff --git a/Classes/Swift/Conference/Views/ConferenceSchedulingSummaryView.swift b/Classes/Swift/Conference/Views/ConferenceSchedulingSummaryView.swift index 8807c571c..906cc0934 100644 --- a/Classes/Swift/Conference/Views/ConferenceSchedulingSummaryView.swift +++ b/Classes/Swift/Conference/Views/ConferenceSchedulingSummaryView.swift @@ -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() } diff --git a/Classes/Swift/Conference/Views/ConferenceSchedulingView.swift b/Classes/Swift/Conference/Views/ConferenceSchedulingView.swift index 27f0934eb..c2e85d1ef 100644 --- a/Classes/Swift/Conference/Views/ConferenceSchedulingView.swift +++ b/Classes/Swift/Conference/Views/ConferenceSchedulingView.swift @@ -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 + } + } diff --git a/Classes/Swift/Voip/Theme/VoipTexts.swift b/Classes/Swift/Voip/Theme/VoipTexts.swift index 8d4ba6618..9a8ef3428 100644 --- a/Classes/Swift/Voip/Theme/VoipTexts.swift +++ b/Classes/Swift/Voip/Theme/VoipTexts.swift @@ -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:"") diff --git a/Classes/Swift/Voip/ViewModels/CallData.swift b/Classes/Swift/Voip/ViewModels/CallData.swift index 14dbb09ea..7affaea99 100644 --- a/Classes/Swift/Voip/ViewModels/CallData.swift +++ b/Classes/Swift/Voip/ViewModels/CallData.swift @@ -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() diff --git a/Classes/Swift/Voip/ViewModels/ConferenceViewModel.swift b/Classes/Swift/Voip/ViewModels/ConferenceViewModel.swift index 8b57549d3..cf52a8964 100644 --- a/Classes/Swift/Voip/ViewModels/ConferenceViewModel.swift +++ b/Classes/Swift/Voip/ViewModels/ConferenceViewModel.swift @@ -37,17 +37,21 @@ class ConferenceViewModel { let conferenceCreationPending = MutableLiveData() let conferenceParticipants = MutableLiveData<[ConferenceParticipantData]>() let conferenceParticipantDevices = MutableLiveData<[ConferenceParticipantDeviceData]>() - let conferenceDisplayMode = MutableLiveData() + let conferenceDisplayMode = MutableLiveData() let isRecording = MutableLiveData() let isRemotelyRecorded = MutableLiveData() - let participantAdminStatusChangedEvent = MutableLiveData() - let maxParticipantsForMosaicLayout = ConfigManager.instance().lpConfigIntForKey(key: "max_conf_part_mosaic_layout",defaultValue: 6) let speakingParticipant = MutableLiveData() + let participantAdminStatusChangedEvent = MutableLiveData() + + let firstToJoinEvent = MutableLiveData() + + let allParticipantsLeftEvent = MutableLiveData() + 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 +}