diff --git a/Linphone/Contacts/ContactsManager.swift b/Linphone/Contacts/ContactsManager.swift index 91af15221..5033d25b4 100644 --- a/Linphone/Contacts/ContactsManager.swift +++ b/Linphone/Contacts/ContactsManager.swift @@ -299,10 +299,14 @@ final class ContactsManager: ObservableObject { clonedAddress!.clean() let sipUri = clonedAddress!.asStringUriOnly() if friendList != nil { - var friend = friendList!.friends.first(where: {$0.addresses.contains(where: {$0.asStringUriOnly() == sipUri})}) - if friend == nil { - friend = linphoneFriendList!.friends.first(where: {$0.addresses.contains(where: {$0.asStringUriOnly() == sipUri})}) + var friend: Friend? + self.coreContext.doOnCoreQueue { _ in + friend = self.friendList!.friends.first(where: {$0.addresses.contains(where: {$0.asStringUriOnly() == sipUri})}) + if friend == nil { + friend = self.linphoneFriendList!.friends.first(where: {$0.addresses.contains(where: {$0.asStringUriOnly() == sipUri})}) + } } + return friend } else { return nil diff --git a/Linphone/Core/CoreContext.swift b/Linphone/Core/CoreContext.swift index 30adf40a9..a886f426e 100644 --- a/Linphone/Core/CoreContext.swift +++ b/Linphone/Core/CoreContext.swift @@ -286,7 +286,7 @@ final class CoreContext: ObservableObject { Log.info("App is in foreground, PUBLISHING presence as Online") self.updatePresence(core: self.mCore, presence: ConsolidatedPresence.Online) - //try? self.mCore.start() + try? self.mCore.start() } } @@ -300,10 +300,7 @@ final class CoreContext: ObservableObject { // Flexisip will handle the Busy status depending on other devices self.updatePresence(core: self.mCore, presence: ConsolidatedPresence.Offline) // self.mCore.iterate() - - if self.mCore.currentCall == nil { - //self.mCore.stop() - } + self.mCore.stop() } } diff --git a/Linphone/UI/Call/CallView.swift b/Linphone/UI/Call/CallView.swift index 03f2cbdcd..4b4b73faa 100644 --- a/Linphone/UI/Call/CallView.swift +++ b/Linphone/UI/Call/CallView.swift @@ -394,7 +394,7 @@ struct CallView: View { // swiftlint:disable:next cyclomatic_complexity func simpleCallView(geometry: GeometryProxy) -> some View { ZStack { - if !callViewModel.isConference { + if callViewModel.isOneOneCall { VStack { Spacer() ZStack { @@ -519,100 +519,145 @@ struct CallView: View { maxHeight: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 + geometry.safeAreaInsets.bottom ) } + + if telecomManager.outgoingCallStarted { + VStack { + ActivityIndicator(color: .white) + .frame(width: 20, height: 20) + .padding(.top, 60) + + Text(callViewModel.counterToMinutes()) + .onAppear { + callViewModel.timeElapsed = 0 + } + .onReceive(callViewModel.timer) { _ in + callViewModel.timeElapsed = callViewModel.currentCall?.duration ?? 0 + + } + .onDisappear { + callViewModel.timeElapsed = 0 + } + .padding(.top) + .foregroundStyle(.white) + + Spacer() + } + .background(.clear) + .frame( + maxWidth: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.width : geometry.size.width - 8, + maxHeight: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 + geometry.safeAreaInsets.bottom + ) + } } else if callViewModel.isConference && !telecomManager.outgoingCallStarted && callViewModel.activeSpeakerParticipant != nil { VStack { - Spacer() - ZStack { - if callViewModel.activeSpeakerParticipant?.address != nil { - let addressFriend = contactsManager.getFriendWithAddress(address: callViewModel.activeSpeakerParticipant!.address) - - let contactAvatarModel = addressFriend != nil - ? ContactsManager.shared.avatarListModel.first(where: { - ($0.friend!.consolidatedPresence == .Online || $0.friend!.consolidatedPresence == .Busy) - && $0.friend!.name == addressFriend!.name - && $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly() - }) - : ContactAvatarModel(friend: nil, name: "", withPresence: false) - - if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty { - if contactAvatarModel != nil { - Avatar(contactAvatarModel: contactAvatarModel!, avatarSize: 200, hidePresence: true) + VStack { + Spacer() + ZStack { + if callViewModel.activeSpeakerParticipant?.address != nil { + let addressFriend = contactsManager.getFriendWithAddress(address: callViewModel.activeSpeakerParticipant!.address) + + let contactAvatarModel = addressFriend != nil + ? ContactsManager.shared.avatarListModel.first(where: { + ($0.friend!.consolidatedPresence == .Online || $0.friend!.consolidatedPresence == .Busy) + && $0.friend!.name == addressFriend!.name + && $0.friend!.address!.asStringUriOnly() == addressFriend!.address!.asStringUriOnly() + }) + : ContactAvatarModel(friend: nil, name: "", withPresence: false) + + if addressFriend != nil && addressFriend!.photo != nil && !addressFriend!.photo!.isEmpty { + if contactAvatarModel != nil { + Avatar(contactAvatarModel: contactAvatarModel!, avatarSize: 200, hidePresence: true) + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + displayVideo = true + } + } + } + } else { + if callViewModel.activeSpeakerParticipant!.address.displayName != nil { + Image(uiImage: contactsManager.textToImage( + firstName: callViewModel.activeSpeakerParticipant!.address.displayName!, + lastName: callViewModel.activeSpeakerParticipant!.address.displayName!.components(separatedBy: " ").count > 1 + ? callViewModel.activeSpeakerParticipant!.address.displayName!.components(separatedBy: " ")[1] + : "")) + .resizable() + .frame(width: 200, height: 200) + .clipShape(Circle()) .onAppear { DispatchQueue.main.asyncAfter(deadline: .now() + 1) { displayVideo = true } } - } - } else { - if callViewModel.activeSpeakerParticipant!.address.displayName != nil { - Image(uiImage: contactsManager.textToImage( - firstName: callViewModel.activeSpeakerParticipant!.address.displayName!, - lastName: callViewModel.activeSpeakerParticipant!.address.displayName!.components(separatedBy: " ").count > 1 - ? callViewModel.activeSpeakerParticipant!.address.displayName!.components(separatedBy: " ")[1] - : "")) - .resizable() - .frame(width: 200, height: 200) - .clipShape(Circle()) - .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - displayVideo = true + + } else { + Image(uiImage: contactsManager.textToImage( + firstName: callViewModel.activeSpeakerParticipant!.address.username ?? "Username Error", + lastName: callViewModel.activeSpeakerParticipant!.address.username!.components(separatedBy: " ").count > 1 + ? callViewModel.activeSpeakerParticipant!.address.username!.components(separatedBy: " ")[1] + : "")) + .resizable() + .frame(width: 200, height: 200) + .clipShape(Circle()) + .onAppear { + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + displayVideo = true + } } } - } else { - Image(uiImage: contactsManager.textToImage( - firstName: callViewModel.activeSpeakerParticipant!.address.username ?? "Username Error", - lastName: callViewModel.activeSpeakerParticipant!.address.username!.components(separatedBy: " ").count > 1 - ? callViewModel.activeSpeakerParticipant!.address.username!.components(separatedBy: " ")[1] - : "")) + } + } else { + Image("profil-picture-default") .resizable() .frame(width: 200, height: 200) .clipShape(Circle()) - .onAppear { - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - displayVideo = true - } - } - } - } - } else { - Image("profil-picture-default") - .resizable() - .frame(width: 200, height: 200) - .clipShape(Circle()) } + + Spacer() } + .frame( + width: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.width : geometry.size.width - 8, + height: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 - 160 + geometry.safeAreaInsets.bottom + ) Spacer() } + .frame( + maxWidth: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.width : geometry.size.width - 8, + maxHeight: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 + geometry.safeAreaInsets.bottom + ) if telecomManager.remoteConfVideo && !telecomManager.outgoingCallStarted && callViewModel.activeSpeakerParticipant != nil && displayVideo { - LinphoneVideoViewHolder { view in - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - coreContext.doOnCoreQueue { core in - core.nativeVideoWindow = view + VStack { + VStack { + LinphoneVideoViewHolder { view in + DispatchQueue.main.asyncAfter(deadline: .now() + 1) { + coreContext.doOnCoreQueue { core in + core.nativeVideoWindow = view + } + } + } + .onTapGesture { + if callViewModel.videoDisplayed { + fullscreenVideo.toggle() + } } } + .frame( + width: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.width : geometry.size.width - 8, + height: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 - 160 + geometry.safeAreaInsets.bottom + ) + .cornerRadius(20) + + Spacer() } .frame( - width: - angleDegree == 0 - ? (fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.width : geometry.size.width - 8) - : (fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 + geometry.safeAreaInsets.bottom), - height: - angleDegree == 0 - ? (fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 + geometry.safeAreaInsets.bottom) - : (fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.width : geometry.size.width - 8) + width: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.width : geometry.size.width - 8, + height: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 + geometry.safeAreaInsets.bottom ) - .scaledToFill() - .clipped() - .onTapGesture { - if callViewModel.videoDisplayed { - fullscreenVideo.toggle() - } - } } + if callViewModel.isConference && !telecomManager.outgoingCallStarted && callViewModel.activeSpeakerParticipant != nil && callViewModel.activeSpeakerParticipant!.isMuted { VStack { HStack { @@ -728,9 +773,11 @@ struct CallView: View { LinphoneVideoViewHolder { view in coreContext.doOnCoreQueue { core in - let participantVideo = core.currentCall?.conference?.participantList.first(where: {$0.address!.equal(address2: callViewModel.participantList[index].address)}) - if participantVideo != nil && participantVideo!.devices.first != nil { - participantVideo!.devices.first!.nativeVideoWindowId = UnsafeMutableRawPointer(Unmanaged.passRetained(view).toOpaque()) + if index < callViewModel.participantList.count { + let participantVideo = core.currentCall?.conference?.participantList.first(where: {$0.address!.equal(address2: callViewModel.participantList[index].address)}) + if participantVideo != nil && participantVideo!.devices.first != nil { + participantVideo!.devices.first!.nativeVideoWindowId = UnsafeMutableRawPointer(Unmanaged.passRetained(view).toOpaque()) + } } } } @@ -810,35 +857,6 @@ struct CallView: View { maxHeight: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 + geometry.safeAreaInsets.bottom ) } - - if telecomManager.outgoingCallStarted { - VStack { - ActivityIndicator(color: .white) - .frame(width: 20, height: 20) - .padding(.top, 60) - - Text(callViewModel.counterToMinutes()) - .onAppear { - callViewModel.timeElapsed = 0 - } - .onReceive(callViewModel.timer) { _ in - callViewModel.timeElapsed = callViewModel.currentCall?.duration ?? 0 - - } - .onDisappear { - callViewModel.timeElapsed = 0 - } - .padding(.top) - .foregroundStyle(.white) - - Spacer() - } - .background(.clear) - .frame( - maxWidth: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.width : geometry.size.width - 8, - maxHeight: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 + geometry.safeAreaInsets.bottom - ) - } } .frame( maxWidth: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.width : geometry.size.width - 8, @@ -1007,7 +1025,7 @@ struct CallView: View { if orientation != .landscapeLeft && orientation != .landscapeRight { HStack(spacing: 0) { - if !callViewModel.isConference { + if callViewModel.isOneOneCall { VStack { Button { if callViewModel.calls.count < 2 { @@ -1231,7 +1249,7 @@ struct CallView: View { } .frame(width: geo.size.width * 0.25, height: geo.size.width * 0.25) - if !callViewModel.isConference { + if callViewModel.isOneOneCall { VStack { Button { callViewModel.toggleRecording() @@ -1473,7 +1491,7 @@ struct CallView: View { } .frame(width: geo.size.width * 0.125, height: geo.size.width * 0.125) - if !callViewModel.isConference { + if callViewModel.isOneOneCall { VStack { Button { callViewModel.toggleRecording() diff --git a/Linphone/UI/Call/ViewModel/CallViewModel.swift b/Linphone/UI/Call/ViewModel/CallViewModel.swift index 5054339df..143981ae1 100644 --- a/Linphone/UI/Call/ViewModel/CallViewModel.swift +++ b/Linphone/UI/Call/ViewModel/CallViewModel.swift @@ -28,9 +28,9 @@ class CallViewModel: ObservableObject { var coreContext = CoreContext.shared var telecomManager = TelecomManager.shared - @Published var displayName: String = "Example Linphone" + @Published var displayName: String = "" @Published var direction: Call.Dir = .Outgoing - @Published var remoteAddressString: String = "example.linphone@sip.linphone.org" + @Published var remoteAddressString: String = "" @Published var remoteAddress: Address? @Published var avatarModel: ContactAvatarModel? @Published var micMutted: Bool = false @@ -46,6 +46,7 @@ class CallViewModel: ObservableObject { @Published var isRemoteDeviceTrusted: Bool = false @Published var selectedCall: Call? @Published var isTransferInsteadCall: Bool = false + @Published var isOneOneCall: Bool = false @Published var isConference: Bool = false @Published var videoDisplayed: Bool = false @Published var participantList: [ParticipantModel] = [] @@ -92,11 +93,41 @@ class CallViewModel: ObservableObject { coreContext.doOnCoreQueue { core in if core.currentCall != nil && core.currentCall!.remoteAddress != nil { self.currentCall = core.currentCall + + var videoDisplayedTmp = false + do { + let params = try core.createCallParams(call: self.currentCall) + videoDisplayedTmp = params.videoDirection == MediaDirection.SendRecv || params.videoDirection == MediaDirection.SendOnly + } catch { + + } + + var isOneOneCallTmp = false + if self.currentCall?.remoteAddress != nil { + let conf = core.findConferenceInformationFromUri(uri: self.currentCall!.remoteAddress!) + + if conf == nil { + isOneOneCallTmp = true + } + } + + var isMediaEncryptedTmp = false + var isZrtpPqTmp = false + if self.currentCall != nil && self.currentCall!.currentParams != nil { + if self.currentCall!.currentParams!.mediaEncryption == .ZRTP || + self.currentCall!.currentParams!.mediaEncryption == .SRTP || + self.currentCall!.currentParams!.mediaEncryption == .DTLS { + + isMediaEncryptedTmp = true + isZrtpPqTmp = self.currentCall!.currentParams!.mediaEncryption == .ZRTP + } + } + DispatchQueue.main.async { self.direction = self.currentCall!.dir self.remoteAddressString = String(self.currentCall!.remoteAddress!.asStringUriOnly().dropFirst(4)) self.remoteAddress = self.currentCall!.remoteAddress! - + self.displayName = "" if self.currentCall?.conference != nil { self.displayName = self.currentCall?.conference?.subject ?? "" } else if self.currentCall?.remoteAddress != nil { @@ -123,15 +154,33 @@ class CallViewModel: ObservableObject { self.isRemoteDeviceTrusted = self.telecomManager.callInProgress ? isDeviceTrusted : false self.activeSpeakerParticipant = nil - do { - let params = try core.createCallParams(call: self.currentCall) - self.videoDisplayed = params.videoDirection == MediaDirection.SendRecv - } catch { - - } + self.avatarModel = nil + self.isRemoteRecording = false + self.zrtpPopupDisplayed = false + self.upperCaseAuthTokenToRead = "" + self.upperCaseAuthTokenToListen = "" + self.isMediaEncrypted = false + self.isZrtpPq = false + self.isOneOneCall = false + self.isConference = false + self.videoDisplayed = false + self.participantList = [] + self.activeSpeakerParticipant = nil + self.activeSpeakerName = "" + self.myParticipantModel = nil + + self.videoDisplayed = videoDisplayedTmp + self.isOneOneCall = isOneOneCallTmp + self.isMediaEncrypted = isMediaEncryptedTmp + self.isZrtpPq = isZrtpPqTmp self.getCallsList() - self.waitingForCreatedStateConference() + + if self.currentCall?.conference?.state == .Created { + self.getConference() + } else { + self.waitingForCreatedStateConference() + } } self.callSuscriptions.insert(self.currentCall!.publisher?.onEncryptionChanged?.postOnMainQueue {(cbVal: (call: Call, on: Bool, authenticationToken: String?)) in @@ -165,9 +214,23 @@ class CallViewModel: ObservableObject { } if conf.activeSpeakerParticipantDevice?.address != nil { - self.activeSpeakerParticipant = ParticipantModel(address: conf.activeSpeakerParticipantDevice!.address!, isJoining: false, isMuted: conf.activeSpeakerParticipantDevice!.isMuted) - } else if conf.participantList.first?.address != nil { - self.activeSpeakerParticipant = ParticipantModel(address: conf.participantDeviceList.first!.address!, isJoining: false, isMuted: conf.participantDeviceList.first!.isMuted) + self.activeSpeakerParticipant = ParticipantModel( + address: conf.activeSpeakerParticipantDevice!.address!, + isJoining: false, + isMuted: conf.activeSpeakerParticipantDevice!.isMuted + ) + } else if conf.participantList.first?.address != nil && conf.participantList.first!.address!.clone()!.equal(address2: (conf.me?.address)!) { + self.activeSpeakerParticipant = ParticipantModel( + address: conf.participantDeviceList.first!.address!, + isJoining: false, + isMuted: conf.participantDeviceList.first!.isMuted + ) + } else if conf.participantList.last?.address != nil { + self.activeSpeakerParticipant = ParticipantModel( + address: conf.participantDeviceList.last!.address!, + isJoining: false, + isMuted: conf.participantDeviceList.last!.isMuted + ) } if self.activeSpeakerParticipant != nil { diff --git a/Linphone/UI/Main/History/Fragments/HistoryListFragment.swift b/Linphone/UI/Main/History/Fragments/HistoryListFragment.swift index 43fa2f917..38f5d4b5a 100644 --- a/Linphone/UI/Main/History/Fragments/HistoryListFragment.swift +++ b/Linphone/UI/Main/History/Fragments/HistoryListFragment.swift @@ -223,7 +223,7 @@ struct HistoryListFragment: View { ) */ - let reutest = try Factory.Instance.createAddress(addr: "sip:conference-focus@sip.linphone.org;conf-id=7M7oqGrZS;gr=0ee3f37f-6df2-0071-bb9a-a4e24be30135") + let reutest = try Factory.Instance.createAddress(addr: "sip:conference-focus@sip.linphone.org;conf-id=~~zKKyETb;gr=0ee3f37f-6df2-0071-bb9a-a4e24be30135") telecomManager.meetingWaitingRoomDisplayed = true telecomManager.meetingWaitingRoomSelected = reutest @@ -244,7 +244,7 @@ struct HistoryListFragment: View { ) */ - let reutest = try Factory.Instance.createAddress(addr: "sip:conference-focus@sip.linphone.org;conf-id=7M7oqGrZS;gr=0ee3f37f-6df2-0071-bb9a-a4e24be30135") + let reutest = try Factory.Instance.createAddress(addr: "sip:conference-focus@sip.linphone.org;conf-id=~~zKKyETb;gr=0ee3f37f-6df2-0071-bb9a-a4e24be30135") telecomManager.meetingWaitingRoomDisplayed = true telecomManager.meetingWaitingRoomSelected = reutest