From 96bdf5150cc0001e0a5ac2977d9f273b7e686469 Mon Sep 17 00:00:00 2001 From: Benoit Martins Date: Mon, 8 Jan 2024 16:57:26 +0100 Subject: [PATCH] Record call --- Linphone/Core/CoreContext.swift | 1 - Linphone/LinphoneApp.swift | 8 +++- .../TelecomManager/ProviderDelegate.swift | 8 ++-- Linphone/TelecomManager/TelecomManager.swift | 47 ++++++++++++++++--- Linphone/UI/Call/CallView.swift | 24 +++++++++- .../UI/Call/ViewModel/CallViewModel.swift | 13 +++-- Linphone/UI/Main/ContentView.swift | 9 +++- Linphone/UI/Main/Fragments/ToastView.swift | 7 +++ 8 files changed, 97 insertions(+), 20 deletions(-) diff --git a/Linphone/Core/CoreContext.swift b/Linphone/Core/CoreContext.swift index 98bdb7127..736500641 100644 --- a/Linphone/Core/CoreContext.swift +++ b/Linphone/Core/CoreContext.swift @@ -104,7 +104,6 @@ final class CoreContext: ObservableObject { self.mCore.videoCaptureEnabled = true self.mCore.videoDisplayEnabled = true - self.mCore.recordAwareEnabled = true let videoActivationPolicy = self.mCore.videoActivationPolicy! videoActivationPolicy.automaticallyAccept = true diff --git a/Linphone/LinphoneApp.swift b/Linphone/LinphoneApp.swift index 61e22bdf3..950f6722f 100644 --- a/Linphone/LinphoneApp.swift +++ b/Linphone/LinphoneApp.swift @@ -30,6 +30,7 @@ struct LinphoneApp: App { @State private var historyViewModel: HistoryViewModel? @State private var historyListViewModel: HistoryListViewModel? @State private var startCallViewModel: StartCallViewModel? + @State private var callViewModel: CallViewModel? var body: some Scene { WindowGroup { @@ -43,13 +44,15 @@ struct LinphoneApp: App { && editContactViewModel != nil && historyViewModel != nil && historyListViewModel != nil - && startCallViewModel != nil { + && startCallViewModel != nil + && callViewModel != nil { ContentView( contactViewModel: contactViewModel!, editContactViewModel: editContactViewModel!, historyViewModel: historyViewModel!, historyListViewModel: historyListViewModel!, - startCallViewModel: startCallViewModel! + startCallViewModel: startCallViewModel!, + callViewModel: callViewModel! ) } else { SplashScreen() @@ -62,6 +65,7 @@ struct LinphoneApp: App { historyViewModel = HistoryViewModel() historyListViewModel = HistoryListViewModel() startCallViewModel = StartCallViewModel() + callViewModel = CallViewModel() } } } diff --git a/Linphone/TelecomManager/ProviderDelegate.swift b/Linphone/TelecomManager/ProviderDelegate.swift index c12cbed92..55ff1cf40 100644 --- a/Linphone/TelecomManager/ProviderDelegate.swift +++ b/Linphone/TelecomManager/ProviderDelegate.swift @@ -209,9 +209,11 @@ extension ProviderDelegate: CXProviderDelegate { let callInfo = callInfos[uuid] let callId = callInfo?.callId ?? "" - DispatchQueue.main.async { - withAnimation { - TelecomManager.shared.callInProgress = true + if TelecomManager.shared.callInProgress == false { + DispatchQueue.main.async { + withAnimation { + TelecomManager.shared.callInProgress = true + } } } CoreContext.shared.doOnCoreQueue { core in diff --git a/Linphone/TelecomManager/TelecomManager.swift b/Linphone/TelecomManager/TelecomManager.swift index 855e70305..a5270c0e3 100644 --- a/Linphone/TelecomManager/TelecomManager.swift +++ b/Linphone/TelecomManager/TelecomManager.swift @@ -42,6 +42,8 @@ class TelecomManager: ObservableObject { @Published var callInProgress: Bool = false @Published var callStarted: Bool = false + @Published var remoteVideo: Bool = false + @Published var isRemoteRecording: Bool = false var actionToFulFill: CXCallAction? var callkitAudioSessionActivated: Bool? @@ -125,6 +127,19 @@ class TelecomManager: ObservableObject { } } } + + private func makeRecordFilePath() -> String{ + var filePath = "recording_" + let now = Date() + let dateFormat = DateFormatter() + dateFormat.dateFormat = "E-d-MMM-yyyy-HH-mm-ss" + let date = dateFormat.string(from: now) + filePath = filePath.appending("\(date).mkv") + + let paths = NSSearchPathForDirectoriesInDomains(.cachesDirectory, .userDomainMask, true) + let writablePath = paths[0] + return writablePath.appending("/\(filePath)") + } func doCall(core: Core, addr: Address, isSas: Bool, isVideo: Bool, isConference: Bool = false) throws { // let displayName = FastAddressBook.displayName(for: addr.getCobject) @@ -154,6 +169,9 @@ class TelecomManager: ObservableObject { // let writablePath = AppManager.recordingFilePathFromCall(address: addr.username! ) // Log.directLog(BCTBX_LOG_DEBUG, text: "record file path: \(writablePath)") // lcallParams.recordFile = writablePath + + lcallParams.recordFile = makeRecordFilePath() + if isSas { lcallParams.mediaEncryption = .ZRTP } @@ -184,9 +202,12 @@ class TelecomManager: ObservableObject { } DispatchQueue.main.async { + self.callStarted = true - withAnimation { - self.callInProgress = true + if self.callInProgress == false { + withAnimation { + self.callInProgress = true + } } } } @@ -195,6 +216,8 @@ class TelecomManager: ObservableObject { func acceptCall(core: Core, call: Call, hasVideo: Bool) { do { let callParams = try core.createCallParams(call: call) + + callParams.recordFile = makeRecordFilePath() callParams.videoEnabled = hasVideo /*if (ConfigManager.instance().lpConfigBoolForKey(key: "edge_opt_preference")) { let low_bandwidth = (AppManager.network() == .network_2g) @@ -311,12 +334,22 @@ class TelecomManager: ObservableObject { if cstate == .PushIncomingReceived { displayIncomingCall(call: call, handle: "Calling", hasVideo: false, callId: callId, displayName: "Calling") } else { - let video = (core.videoActivationPolicy?.automaticallyAccept ?? false) && (call.remoteParams?.videoEnabled ?? false) + remoteVideo = (core.videoActivationPolicy?.automaticallyAccept ?? false) && (call.remoteParams?.videoEnabled ?? false) - if video { + if remoteVideo { Log.info("[Call] Remote video is activated") } + isRemoteRecording = call.remoteParams?.isRecording ?? false + + if isRemoteRecording && ToastViewModel.shared.toastMessage == "" { + + ToastViewModel.shared.toastMessage = "\(call.remoteAddress) is recording" + ToastViewModel.shared.displayToast.toggle() + + Log.info("[Call] Call is recording by \(call.remoteAddress)") + } + if call.userData == nil { let appData = CallAppData() TelecomManager.setAppData(sCall: call, appData: appData) @@ -351,7 +384,7 @@ class TelecomManager: ObservableObject { providerDelegate.callInfos.updateValue(callInfo!, forKey: uuid!) providerDelegate.uuids.removeValue(forKey: callId) providerDelegate.uuids.updateValue(uuid!, forKey: callInfo!.callId) - providerDelegate.updateCall(uuid: uuid!, handle: addr!.asStringUriOnly(), hasVideo: video, displayName: displayName) + providerDelegate.updateCall(uuid: uuid!, handle: addr!.asStringUriOnly(), hasVideo: remoteVideo, displayName: displayName) } } else if TelecomManager.callKitEnabled(core: core) { /* @@ -374,9 +407,9 @@ class TelecomManager: ObservableObject { if uuid != nil { // Tha app is now registered, updated the call already existed. - providerDelegate.updateCall(uuid: uuid!, handle: addr!.asStringUriOnly(), hasVideo: video, displayName: displayName) + providerDelegate.updateCall(uuid: uuid!, handle: addr!.asStringUriOnly(), hasVideo: remoteVideo, displayName: displayName) } else { - displayIncomingCall(call: call, handle: addr!.asStringUriOnly(), hasVideo: video, callId: callId, displayName: displayName) + displayIncomingCall(call: call, handle: addr!.asStringUriOnly(), hasVideo: remoteVideo, callId: callId, displayName: displayName) } } /* else if UIApplication.shared.applicationState != .active { // not support callkit , use notif diff --git a/Linphone/UI/Call/CallView.swift b/Linphone/UI/Call/CallView.swift index cd23af51e..dc681036d 100644 --- a/Linphone/UI/Call/CallView.swift +++ b/Linphone/UI/Call/CallView.swift @@ -273,7 +273,7 @@ struct CallView: View { .frame(width: 32, height: 32) } .frame(width: 60, height: 60) - .background(Color.gray500) + .background(callViewModel.isRecording ? Color.redDanger500 : Color.gray500) .cornerRadius(40) Text("Record") @@ -572,6 +572,28 @@ struct CallView: View { ) } + if callViewModel.isRecording { + HStack { + VStack { + Image("record-fill") + .renderingMode(.template) + .resizable() + .foregroundStyle(Color.redDanger500) + .frame(width: 32, height: 32) + .padding(10) + .if(fullscreenVideo) { view in + view.padding(.top, 30) + } + Spacer() + } + Spacer() + } + .frame( + maxWidth: fullscreenVideo ? geometry.size.width : geometry.size.width - 8, + maxHeight: fullscreenVideo ? geometry.size.height + geometry.safeAreaInsets.top + geometry.safeAreaInsets.bottom : geometry.size.height - 140 + ) + } + if !telecomManager.callStarted && !fullscreenVideo { VStack { ActivityIndicator() diff --git a/Linphone/UI/Call/ViewModel/CallViewModel.swift b/Linphone/UI/Call/ViewModel/CallViewModel.swift index 834b8874b..382e41e5e 100644 --- a/Linphone/UI/Call/ViewModel/CallViewModel.swift +++ b/Linphone/UI/Call/ViewModel/CallViewModel.swift @@ -33,6 +33,8 @@ class CallViewModel: ObservableObject { @Published var avatarModel: ContactAvatarModel? @Published var micMutted: Bool = false @Published var cameraDisplayed: Bool = false + @Published var isRecording: Bool = false + @Published var isRemoteRecording: Bool = false @State var timeElapsed: Int = 0 let timer = Timer.publish(every: 1, on: .main, in: .common).autoconnect() @@ -40,7 +42,6 @@ class CallViewModel: ObservableObject { var currentCall: Call? init() { - do { try AVAudioSession.sharedInstance().setCategory(.playAndRecord, mode: .voiceChat, options: .allowBluetooth) try AVAudioSession.sharedInstance().setActive(true) @@ -48,6 +49,10 @@ class CallViewModel: ObservableObject { } + resetCallView() + } + + func resetCallView() { coreContext.doOnCoreQueue { core in if core.currentCall != nil && core.currentCall!.remoteAddress != nil { self.currentCall = core.currentCall @@ -70,6 +75,7 @@ class CallViewModel: ObservableObject { //self.avatarModel = ??? self.micMutted = self.currentCall!.microphoneMuted self.cameraDisplayed = self.currentCall!.cameraEnabled == true + self.isRecording = self.currentCall!.params!.isRecording } } } @@ -164,10 +170,9 @@ class CallViewModel: ObservableObject { } else { Log.info("[CallViewModel] Starting call recording \(self.currentCall!.params!.isRecording)") self.currentCall!.startRecording() - Log.info("[CallViewModel] Starting call recording \(self.currentCall!.params!.isRecording)") } - //var recording = self.currentCall!.params!.isRecording - //isRecording.postValue(recording) + + self.isRecording = self.currentCall!.params!.isRecording } } } diff --git a/Linphone/UI/Main/ContentView.swift b/Linphone/UI/Main/ContentView.swift index 64f63dc5b..ee403524a 100644 --- a/Linphone/UI/Main/ContentView.swift +++ b/Linphone/UI/Main/ContentView.swift @@ -38,6 +38,7 @@ struct ContentView: View { @ObservedObject var historyViewModel: HistoryViewModel @ObservedObject var historyListViewModel: HistoryListViewModel @ObservedObject var startCallViewModel: StartCallViewModel + @ObservedObject var callViewModel: CallViewModel @State var index = 0 @State private var orientation = UIDevice.current.orientation @@ -661,9 +662,12 @@ struct ContentView: View { } if telecomManager.callInProgress { - CallView(callViewModel: CallViewModel()) + CallView(callViewModel: callViewModel) .zIndex(3) .transition(.scale.combined(with: .move(edge: .top))) + .onAppear { + callViewModel.resetCallView() + } } // if sharedMainViewModel.displayToast { @@ -722,7 +726,8 @@ struct ContentView: View { editContactViewModel: EditContactViewModel(), historyViewModel: HistoryViewModel(), historyListViewModel: HistoryListViewModel(), - startCallViewModel: StartCallViewModel() + startCallViewModel: StartCallViewModel(), + callViewModel: CallViewModel() ) } // swiftlint:enable type_body_length diff --git a/Linphone/UI/Main/Fragments/ToastView.swift b/Linphone/UI/Main/Fragments/ToastView.swift index 40d5e7e16..025479ae0 100644 --- a/Linphone/UI/Main/Fragments/ToastView.swift +++ b/Linphone/UI/Main/Fragments/ToastView.swift @@ -54,6 +54,13 @@ struct ToastView: View { .foregroundStyle(Color.greenSuccess500) .default_text_style(styleSize: 15) .padding(8) + + case let str where str.contains("is recording"): + Text(toastViewModel.toastMessage) + .multilineTextAlignment(.center) + .foregroundStyle(Color.redDanger500) + .default_text_style(styleSize: 15) + .padding(8) case "Failed": Text("Invalid QR code!")