From 2ba2e4095897782fbd0df48a733bd2b75c217600 Mon Sep 17 00:00:00 2001 From: Benoit Martins Date: Tue, 7 May 2024 14:32:59 +0200 Subject: [PATCH] Change call view in landscape mode --- Linphone/TelecomManager/TelecomManager.swift | 23 +- Linphone/UI/Call/CallView.swift | 351 ++++++++++++++----- Linphone/UI/Call/Model/CallStatsModel.swift | 6 + 3 files changed, 295 insertions(+), 85 deletions(-) diff --git a/Linphone/TelecomManager/TelecomManager.swift b/Linphone/TelecomManager/TelecomManager.swift index 9579262ba..7a353d160 100644 --- a/Linphone/TelecomManager/TelecomManager.swift +++ b/Linphone/TelecomManager/TelecomManager.swift @@ -33,6 +33,7 @@ class CallAppData: NSObject { } +// swiftlint:disable type_body_length class TelecomManager: ObservableObject { static let shared = TelecomManager() static var uuidReplacedCall: String? @@ -388,21 +389,33 @@ class TelecomManager: ObservableObject { if call.conference != nil { if call.conference!.activeSpeakerParticipantDevice != nil { let direction = call.conference?.activeSpeakerParticipantDevice!.getStreamCapability(streamType: StreamType.Video) - self.remoteConfVideo = direction == .SendRecv || direction == .SendOnly + self.remoteConfVideo = false + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.remoteConfVideo = direction == .SendRecv || direction == .SendOnly + } } else if call.conference!.participantList.first != nil && call.conference!.participantDeviceList.first != nil && call.conference!.participantList.first?.address != nil && call.conference!.participantList.first!.address!.clone()!.equal(address2: (call.conference!.me?.address)!) { let direction = call.conference!.participantDeviceList.first!.getStreamCapability(streamType: StreamType.Video) - self.remoteConfVideo = direction == .SendRecv || direction == .SendOnly + self.remoteConfVideo = false + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.remoteConfVideo = direction == .SendRecv || direction == .SendOnly + } } else if call.conference!.participantList.last != nil && call.conference!.participantDeviceList.last != nil && call.conference!.participantList.last?.address != nil { let direction = call.conference!.participantDeviceList.last!.getStreamCapability(streamType: StreamType.Video) - self.remoteConfVideo = direction == .SendRecv || direction == .SendOnly + self.remoteConfVideo = false + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.remoteConfVideo = direction == .SendRecv || direction == .SendOnly + } } else { self.remoteConfVideo = false } } else { - self.remoteConfVideo = call.currentParams!.videoEnabled && call.currentParams!.videoDirection == .SendRecv || call.currentParams!.videoDirection == .RecvOnly + self.remoteConfVideo = false + DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) { + self.remoteConfVideo = call.currentParams!.videoEnabled && call.currentParams!.videoDirection == .SendRecv || call.currentParams!.videoDirection == .RecvOnly + } } /* @@ -715,5 +728,5 @@ class TelecomManager: ObservableObject { ]) } } - +// swiftlint:enable type_body_length // swiftlint:enable cyclomatic_complexity diff --git a/Linphone/UI/Call/CallView.swift b/Linphone/UI/Call/CallView.swift index 6191d845c..cac96dcae 100644 --- a/Linphone/UI/Call/CallView.swift +++ b/Linphone/UI/Call/CallView.swift @@ -1033,8 +1033,10 @@ struct CallView: View { } // swiftlint:enable function_body_length + // swiftlint:disable:next cyclomatic_complexity func activeSpeakerMode(geometry: GeometryProxy) -> some View { ZStack { + let isLandscapeMode = (orientation == .landscapeLeft || orientation == .landscapeRight || UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) if callViewModel.activeSpeakerParticipant!.onPause { VStack { VStack { @@ -1056,8 +1058,8 @@ struct CallView: View { 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 - 160 : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 - 160 + geometry.safeAreaInsets.bottom + width: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.width - (isLandscapeMode ? 160 : 0) : geometry.size.width - 8 - (isLandscapeMode ? 160 : 0), + height: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom - (!isLandscapeMode ? 160 : 0) - (isLandscapeMode && fullscreenVideo && !telecomManager.isPausedByRemote ? 40 : 0) : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 - (!isLandscapeMode ? 160 : 0) + geometry.safeAreaInsets.bottom - (isLandscapeMode && fullscreenVideo && !telecomManager.isPausedByRemote ? 40 : 0) ) Spacer() @@ -1068,76 +1070,84 @@ struct CallView: View { ) } 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 + HStack { + VStack { + Spacer() + HStack { + 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 { - 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 - } - } + Image("profil-picture-default") + .resizable() + .frame(width: 200, height: 200) + .clipShape(Circle()) } - } - } else { - Image("profil-picture-default") - .resizable() - .frame(width: 200, height: 200) - .clipShape(Circle()) } + + Spacer() } + .frame( + width: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.width - (isLandscapeMode ? 160 : 0) : geometry.size.width - 8 - (isLandscapeMode ? 160 : 0), + height: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom - (!isLandscapeMode ? 160 : 0) - (isLandscapeMode && fullscreenVideo && !telecomManager.isPausedByRemote ? 40 : 0) : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 - (!isLandscapeMode ? 160 : 0) - (isLandscapeMode && fullscreenVideo && !telecomManager.isPausedByRemote ? 40 : 0) + geometry.safeAreaInsets.bottom + ) - Spacer() + if isLandscapeMode { + 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 - 160 : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 - 160 + geometry.safeAreaInsets.bottom - ) Spacer() } @@ -1148,18 +1158,24 @@ struct CallView: View { VStack { if telecomManager.remoteConfVideo && !telecomManager.outgoingCallStarted && callViewModel.activeSpeakerParticipant != nil && displayVideo { - VStack { - LinphoneVideoViewHolder { view in - coreContext.doOnCoreQueue { core in - core.nativeVideoWindow = view + HStack { + VStack { + LinphoneVideoViewHolder { view in + coreContext.doOnCoreQueue { core in + core.nativeVideoWindow = view + } } } + .frame( + width: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.width - (isLandscapeMode ? 160 : 0) : geometry.size.width - 8 - (isLandscapeMode ? 160 : 0), + height: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom - (!isLandscapeMode ? 160 : 0) - (isLandscapeMode && fullscreenVideo && !telecomManager.isPausedByRemote ? 40 : 0) : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 - (!isLandscapeMode ? 160 : 0) - (isLandscapeMode && fullscreenVideo && !telecomManager.isPausedByRemote ? 40 : 0) + geometry.safeAreaInsets.bottom + ) + .cornerRadius(20) + + if isLandscapeMode { + 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 - 160 : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 - 160 + geometry.safeAreaInsets.bottom - ) - .cornerRadius(20) } Spacer() } @@ -1184,6 +1200,11 @@ struct CallView: View { .padding(5) .background(.white) .cornerRadius(40) + + if isLandscapeMode { + Spacer() + .frame(width: 160) + } } Spacer() } @@ -1204,9 +1225,178 @@ struct CallView: View { .lineLimit(1) .padding(.horizontal, 10) .padding(.bottom, 6) + .padding(.top, isLandscapeMode && fullscreenVideo && !telecomManager.isPausedByRemote ? -70 : 0) - ScrollView(.horizontal) { - HStack { + if !isLandscapeMode { + ScrollView(.horizontal) { + HStack { + ZStack { + VStack { + Spacer() + + if callViewModel.myParticipantModel != nil { + Avatar(contactAvatarModel: callViewModel.myParticipantModel!.avatarModel, avatarSize: 50, hidePresence: true) + } + + Spacer() + } + .frame(width: 140, height: 140) + + if callViewModel.videoDisplayed { + LinphoneVideoViewHolder { view in + coreContext.doOnCoreQueue { core in + core.nativePreviewWindow = view + } + } + .frame(width: angleDegree == 0 ? 120*1.2 : 160*1.2, height: angleDegree == 0 ? 160*1.2 : 120*1.2) + .scaledToFill() + .clipped() + } + + VStack(alignment: .leading) { + Spacer() + + if callViewModel.myParticipantModel != nil { + Text(callViewModel.myParticipantModel!.name) + .frame(maxWidth: .infinity, alignment: .leading) + .foregroundStyle(Color.white) + .default_text_style_500(styleSize: 14) + .lineLimit(1) + .padding(.horizontal, 10) + .padding(.bottom, 6) + } + } + .frame(width: 140, height: 140) + } + .frame(width: 140, height: 140) + .background(Color.gray600) + .overlay( + RoundedRectangle(cornerRadius: 20) + .stroke(callViewModel.myParticipantModel != nil && callViewModel.myParticipantModel!.isSpeaking ? .white : .clear, lineWidth: 4) + ) + .cornerRadius(20) + + ForEach(0.. 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 + geometry.safeAreaInsets.bottom + ) + .padding(.bottom, 10) + .padding(.leading, -10) + + if isLandscapeMode { + HStack { + Spacer() + ScrollView(.vertical) { + VStack { ZStack { VStack { Spacer() @@ -1360,18 +1550,19 @@ struct CallView: View { } } } + .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 + ) + .padding(.bottom, 10) + .padding(.leading, -10) } - .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 - ) - .padding(.bottom, 10) - .padding(.leading, -10) } } + .padding(.top, fullscreenVideo && !telecomManager.isPausedByRemote && (orientation == .landscapeLeft || orientation == .landscapeRight || UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) ? 50 : 10) .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 + maxHeight: fullscreenVideo && !telecomManager.isPausedByRemote ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom - ((orientation == .landscapeLeft || orientation == .landscapeRight || UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) ? 50 : 10) : geometry.size.height - (minBottomSheetHeight * geometry.size.height > 80 ? minBottomSheetHeight * geometry.size.height : 78) - 40 - 20 + geometry.safeAreaInsets.bottom - ((orientation == .landscapeLeft || orientation == .landscapeRight || UIScreen.main.bounds.size.width > UIScreen.main.bounds.size.height) ? 50 : 10) ) .contentShape(Rectangle()) .onTapGesture { diff --git a/Linphone/UI/Call/Model/CallStatsModel.swift b/Linphone/UI/Call/Model/CallStatsModel.swift index 98f788a7c..d48e09f96 100644 --- a/Linphone/UI/Call/Model/CallStatsModel.swift +++ b/Linphone/UI/Call/Model/CallStatsModel.swift @@ -43,6 +43,9 @@ class CallStatsModel: ObservableObject { let clockRate = (payloadType?.clockRate != nil ? payloadType!.clockRate : 0) / 1000 let codecLabel = "Codec: " + "\(payloadType != nil ? payloadType!.mimeType : "")/\(clockRate) kHz" + guard !(stats.uploadBandwidth.rounded().isNaN || stats.uploadBandwidth.rounded().isInfinite || stats.downloadBandwidth.rounded().isNaN || stats.downloadBandwidth.rounded().isInfinite) else { + return + } let uploadBandwidth = Int(stats.uploadBandwidth.rounded()) let downloadBandwidth = Int(stats.downloadBandwidth.rounded()) let bandwidthLabel = "Bandwidth: " + "↑ \(uploadBandwidth) kbits/s ↓ \(downloadBandwidth) kbits/s" @@ -58,6 +61,9 @@ class CallStatsModel: ObservableObject { let clockRate = (payloadType?.clockRate != nil ? payloadType!.clockRate : 0) / 1000 let codecLabel = "Codec: " + "\(payloadType != nil ? payloadType!.mimeType : "null")/\(clockRate) kHz" + guard !(stats.uploadBandwidth.rounded().isNaN || stats.uploadBandwidth.rounded().isInfinite || stats.downloadBandwidth.rounded().isNaN || stats.downloadBandwidth.rounded().isInfinite) else { + return + } let uploadBandwidth = Int(stats.uploadBandwidth.rounded()) let downloadBandwidth = Int(stats.downloadBandwidth.rounded()) let bandwidthLabel = "Bandwidth: " + "↑ \(uploadBandwidth) kbits/s ↓ \(downloadBandwidth) kbits/s"