From d96abf151423a5dd87997c962082570e502c147e Mon Sep 17 00:00:00 2001 From: Benoit Martins Date: Fri, 19 Apr 2024 13:49:21 +0200 Subject: [PATCH] Add pause status to conf view --- Linphone/Localizable.xcstrings | 3 + Linphone/UI/Call/CallView.swift | 242 +++++++++++------- Linphone/UI/Call/Model/ParticipantModel.swift | 4 +- .../UI/Call/ViewModel/CallViewModel.swift | 63 ++++- 4 files changed, 203 insertions(+), 109 deletions(-) diff --git a/Linphone/Localizable.xcstrings b/Linphone/Localizable.xcstrings index 5a2e07f9e..402f26965 100644 --- a/Linphone/Localizable.xcstrings +++ b/Linphone/Localizable.xcstrings @@ -334,6 +334,9 @@ }, "En continuant, vous acceptez ces conditions, " : { + }, + "En pause" : { + }, "Error" : { diff --git a/Linphone/UI/Call/CallView.swift b/Linphone/UI/Call/CallView.swift index 463f395a3..cb9a50048 100644 --- a/Linphone/UI/Call/CallView.swift +++ b/Linphone/UI/Call/CallView.swift @@ -551,113 +551,146 @@ struct CallView: View { ) } } else if callViewModel.isConference && !telecomManager.outgoingCallStarted && callViewModel.activeSpeakerParticipant != nil { - VStack { - 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 { - 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("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 { + if callViewModel.activeSpeakerParticipant!.onPause { VStack { VStack { - LinphoneVideoViewHolder { view in - DispatchQueue.main.asyncAfter(deadline: .now() + 1) { - coreContext.doOnCoreQueue { core in - core.nativeVideoWindow = view - } - } - } - .onTapGesture { - if callViewModel.videoDisplayed { - fullscreenVideo.toggle() - } - } + Spacer() + + Image("pause") + .renderingMode(.template) + .resizable() + .foregroundStyle(.white) + .frame(width: 40, height: 40) + + Text("En pause") + .frame(maxWidth: .infinity, alignment: .center) + .foregroundStyle(Color.white) + .default_text_style_500(styleSize: 14) + .lineLimit(1) + .padding(.horizontal, 10) + + 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 ) - .cornerRadius(20) 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 + geometry.safeAreaInsets.bottom + 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 { + VStack { + 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 { + 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("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 { + 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: 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 + ) + } } if callViewModel.isConference && !telecomManager.outgoingCallStarted && callViewModel.activeSpeakerParticipant != nil && callViewModel.activeSpeakerParticipant!.isMuted { @@ -762,6 +795,25 @@ struct CallView: View { .lineLimit(1) .padding(.horizontal, 10) + Spacer() + } + } else if callViewModel.participantList[index].onPause { + VStack { + Spacer() + + Image("pause") + .renderingMode(.template) + .resizable() + .foregroundStyle(.white) + .frame(width: 40, height: 40) + + Text("En pause") + .frame(maxWidth: .infinity, alignment: .center) + .foregroundStyle(Color.white) + .default_text_style_500(styleSize: 14) + .lineLimit(1) + .padding(.horizontal, 10) + Spacer() } } else { diff --git a/Linphone/UI/Call/Model/ParticipantModel.swift b/Linphone/UI/Call/Model/ParticipantModel.swift index 8a69570fe..2f5c9786a 100644 --- a/Linphone/UI/Call/Model/ParticipantModel.swift +++ b/Linphone/UI/Call/Model/ParticipantModel.swift @@ -29,9 +29,10 @@ class ParticipantModel: ObservableObject { @Published var name: String @Published var avatarModel: ContactAvatarModel @Published var isJoining: Bool + @Published var onPause: Bool @Published var isMuted: Bool - init(address: Address, isJoining: Bool, isMuted: Bool) { + init(address: Address, isJoining: Bool, onPause: Bool, isMuted: Bool) { self.address = address self.sipUri = address.asStringUriOnly() @@ -58,6 +59,7 @@ class ParticipantModel: ObservableObject { : ContactAvatarModel(friend: nil, name: nameTmp, withPresence: false) self.isJoining = isJoining + self.onPause = onPause self.isMuted = isMuted } } diff --git a/Linphone/UI/Call/ViewModel/CallViewModel.swift b/Linphone/UI/Call/ViewModel/CallViewModel.swift index 143981ae1..db717f91a 100644 --- a/Linphone/UI/Call/ViewModel/CallViewModel.swift +++ b/Linphone/UI/Call/ViewModel/CallViewModel.swift @@ -208,27 +208,30 @@ class CallViewModel: ObservableObject { self.participantList = [] if conf.me?.address != nil { - self.myParticipantModel = ParticipantModel(address: conf.me!.address!, isJoining: false, isMuted: false) + self.myParticipantModel = ParticipantModel(address: conf.me!.address!, isJoining: false, onPause: false, isMuted: false) } else if self.currentCall?.callLog?.localAddress != nil { - self.myParticipantModel = ParticipantModel(address: self.currentCall!.callLog!.localAddress!, isJoining: false, isMuted: false) + self.myParticipantModel = ParticipantModel(address: self.currentCall!.callLog!.localAddress!, isJoining: false, onPause: false, isMuted: false) } if conf.activeSpeakerParticipantDevice?.address != nil { self.activeSpeakerParticipant = ParticipantModel( address: conf.activeSpeakerParticipantDevice!.address!, isJoining: false, + onPause: conf.activeSpeakerParticipantDevice!.state == .OnHold, 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, + onPause: conf.participantDeviceList.first!.state == .OnHold, isMuted: conf.participantDeviceList.first!.isMuted ) } else if conf.participantList.last?.address != nil { self.activeSpeakerParticipant = ParticipantModel( address: conf.participantDeviceList.last!.address!, isJoining: false, + onPause: conf.participantDeviceList.last!.state == .OnHold, isMuted: conf.participantDeviceList.last!.isMuted ) } @@ -250,7 +253,12 @@ class CallViewModel: ObservableObject { if participantDevice.address != nil && !conf.isMe(uri: participantDevice.address!.clone()!) { if !conf.isMe(uri: participantDevice.address!.clone()!) { self.participantList.append( - ParticipantModel(address: participantDevice.address!, isJoining: participantDevice.state == .Joining || participantDevice.state == .Alerting, isMuted: participantDevice.isMuted) + ParticipantModel( + address: participantDevice.address!, + isJoining: participantDevice.state == .Joining || participantDevice.state == .Alerting, + onPause: participantDevice.state == .OnHold, + isMuted: participantDevice.isMuted + ) ) } } @@ -280,7 +288,12 @@ class CallViewModel: ObservableObject { self.currentCall?.conference?.publisher?.onActiveSpeakerParticipantDevice?.postOnMainQueue {(cbValue: (conference: Conference, participantDevice: ParticipantDevice)) in if cbValue.participantDevice.address != nil { let activeSpeakerParticipantTmp = self.activeSpeakerParticipant - self.activeSpeakerParticipant = ParticipantModel(address: cbValue.participantDevice.address!, isJoining: false, isMuted: cbValue.participantDevice.isMuted) + self.activeSpeakerParticipant = ParticipantModel( + address: cbValue.participantDevice.address!, + isJoining: false, + onPause: cbValue.participantDevice.state == .OnHold, + isMuted: cbValue.participantDevice.isMuted + ) if self.activeSpeakerParticipant != nil { let friend = ContactsManager.shared.getFriendWithAddress(address: self.activeSpeakerParticipant!.address) @@ -304,7 +317,12 @@ class CallViewModel: ObservableObject { if participantDevice.address != nil && !cbValue.conference.isMe(uri: participantDevice.address!.clone()!) { if !cbValue.conference.isMe(uri: participantDevice.address!.clone()!) { self.participantList.append( - ParticipantModel(address: participantDevice.address!, isJoining: participantDevice.state == .Joining || participantDevice.state == .Alerting, isMuted: participantDevice.isMuted) + ParticipantModel( + address: participantDevice.address!, + isJoining: participantDevice.state == .Joining || participantDevice.state == .Alerting, + onPause: participantDevice.state == .OnHold, + isMuted: participantDevice.isMuted + ) ) } } @@ -322,7 +340,12 @@ class CallViewModel: ObservableObject { if participantDevice.address != nil && !cbValue.conference.isMe(uri: participantDevice.address!.clone()!) { if !cbValue.conference.isMe(uri: participantDevice.address!.clone()!) { self.participantList.append( - ParticipantModel(address: participantDevice.address!, isJoining: participantDevice.state == .Joining || participantDevice.state == .Alerting, isMuted: participantDevice.isMuted) + ParticipantModel( + address: participantDevice.address!, + isJoining: participantDevice.state == .Joining || participantDevice.state == .Alerting, + onPause: participantDevice.state == .OnHold, + isMuted: participantDevice.isMuted + ) ) } } @@ -339,11 +362,20 @@ class CallViewModel: ObservableObject { if participantDevice.address != nil && !cbValue.conference.isMe(uri: participantDevice.address!.clone()!) { if !cbValue.conference.isMe(uri: participantDevice.address!.clone()!) { self.participantList.append( - ParticipantModel(address: participantDevice.address!, isJoining: participantDevice.state == .Joining || participantDevice.state == .Alerting, isMuted: participantDevice.isMuted) + ParticipantModel( + address: participantDevice.address!, + isJoining: participantDevice.state == .Joining || participantDevice.state == .Alerting, + onPause: participantDevice.state == .OnHold, + isMuted: participantDevice.isMuted + ) ) } } }) + + if cbValue.conference.participantDeviceList.count == 1 { + self.activeSpeakerParticipant = nil + } } } ) @@ -367,12 +399,17 @@ class CallViewModel: ObservableObject { Log.info( "[CallViewModel] Participant device \(cbValue.device.address!.asStringUriOnly()) state changed \(cbValue.state)" ) - - self.participantList.forEach({ participantDevice in - if participantDevice.address.equal(address2: cbValue.device.address!) { - participantDevice.isJoining = cbValue.state == .Joining || cbValue.state == .Alerting - } - }) + if self.activeSpeakerParticipant != nil && self.activeSpeakerParticipant!.address.equal(address2: cbValue.device.address!) { + self.activeSpeakerParticipant!.onPause = cbValue.state == .OnHold + self.activeSpeakerParticipant!.isJoining = cbValue.state == .Joining || cbValue.state == .Alerting + } else { + self.participantList.forEach({ participantDevice in + if participantDevice.address.equal(address2: cbValue.device.address!) { + participantDevice.onPause = cbValue.state == .OnHold + participantDevice.isJoining = cbValue.state == .Joining || cbValue.state == .Alerting + } + }) + } } ) }