Fix Active speaker view

This commit is contained in:
Benoit Martins 2024-04-19 09:46:33 +02:00
parent c350def616
commit cd1ae4c19d
5 changed files with 208 additions and 126 deletions

View file

@ -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

View file

@ -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()
}
}

View file

@ -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()

View file

@ -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 {

View file

@ -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