diff --git a/swift/CallTutorial/CallTutorial.xcodeproj/project.pbxproj b/swift/CallTutorial/CallTutorial.xcodeproj/project.pbxproj index c858a80..52cd544 100644 --- a/swift/CallTutorial/CallTutorial.xcodeproj/project.pbxproj +++ b/swift/CallTutorial/CallTutorial.xcodeproj/project.pbxproj @@ -14,6 +14,7 @@ 6604166024D451F40064FC6C /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6604165F24D451F40064FC6C /* Preview Assets.xcassets */; }; 6604166324D451F40064FC6C /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6604166124D451F40064FC6C /* LaunchScreen.storyboard */; }; 6604166B24D453240064FC6C /* CallExample.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6604166A24D453240064FC6C /* CallExample.swift */; }; + 66B14FBB24DAF68E0041952F /* CallKitProviderDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 66B14FBA24DAF68E0041952F /* CallKitProviderDelegate.swift */; }; E1569C6E0564D0FD3FF9B4AF /* Pods_CallTutorial.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1DC1D5CBF786F45B5E0D811F /* Pods_CallTutorial.framework */; }; /* End PBXBuildFile section */ @@ -28,6 +29,7 @@ 6604166224D451F40064FC6C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 6604166424D451F40064FC6C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6604166A24D453240064FC6C /* CallExample.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallExample.swift; sourceTree = ""; }; + 66B14FBA24DAF68E0041952F /* CallKitProviderDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallKitProviderDelegate.swift; sourceTree = ""; }; 79CE65CD0070AF94E21BFCE7 /* Pods-CallTutorial.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CallTutorial.debug.xcconfig"; path = "Target Support Files/Pods-CallTutorial/Pods-CallTutorial.debug.xcconfig"; sourceTree = ""; }; C26219FCE26251F1C3C2E96E /* Pods-CallTutorial.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CallTutorial.release.xcconfig"; path = "Target Support Files/Pods-CallTutorial/Pods-CallTutorial.release.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ @@ -84,6 +86,7 @@ children = ( 6604165624D451F10064FC6C /* AppDelegate.swift */, 6604165824D451F10064FC6C /* SceneDelegate.swift */, + 66B14FBA24DAF68E0041952F /* CallKitProviderDelegate.swift */, 6604166A24D453240064FC6C /* CallExample.swift */, 6604165A24D451F10064FC6C /* ContentView.swift */, 6604165C24D451F40064FC6C /* Assets.xcassets */, @@ -221,6 +224,7 @@ 6604165724D451F10064FC6C /* AppDelegate.swift in Sources */, 6604165924D451F10064FC6C /* SceneDelegate.swift in Sources */, 6604165B24D451F10064FC6C /* ContentView.swift in Sources */, + 66B14FBB24DAF68E0041952F /* CallKitProviderDelegate.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/swift/CallTutorial/CallTutorial/CallExample.swift b/swift/CallTutorial/CallTutorial/CallExample.swift index 3fd9380..d91e040 100644 --- a/swift/CallTutorial/CallTutorial/CallExample.swift +++ b/swift/CallTutorial/CallTutorial/CallExample.swift @@ -37,6 +37,9 @@ class CallExampleContext : ObservableObject @Published var passwd : String = "peche5" @Published var loggedIn: Bool = false + var providerDelegate : CallKitProviderDelegate! + @Published var enableCallKit = false; + init() { mCallStateTracer.tutorialContext = self @@ -56,17 +59,19 @@ class CallExampleContext : ObservableObject // main loop for receiving notifications and doing background linphonecore work: mCore.autoIterateEnabled = true + mCore.callkitEnabled = true try? mCore.start() mVideoDevices = mCore.videoDevicesList mCore.addDelegate(delegate: mCallStateTracer) mCore.addDelegate(delegate: mRegistrationDelegate) + + providerDelegate = CallKitProviderDelegate(context : self) } func registrationExample() { - let factory = Factory.Instance do { let proxy_cfg = try mCore.createProxyConfig() @@ -120,20 +125,24 @@ class CallExampleContext : ObservableObject } + // Initiate a call + func outgoingCallKitCallExample() + { + providerDelegate.outgoingCall(uuid: UUID()) + } + // Terminate a call func stopCall() { - if (callRunning) + if ((callRunning || isCallIncoming) && mCall.state != Call.State.End) { - if (mCall.state != Call.State.End){ - // terminate the call - print("Terminating the call...\n") - do { - try mCall.terminate() - } catch { - print(error) - } - } + // terminate the call + print("Terminating the call...\n") + do { + try mCall.terminate() + } catch { + print(error) + } } } @@ -211,10 +220,15 @@ class CallStateDelegate: CoreDelegate { if (cstate == .IncomingReceived) { tutorialContext.mCall = call tutorialContext.isCallIncoming = true + if (tutorialContext.enableCallKit) + { + tutorialContext.providerDelegate.reportIncomingCall(call: call, uuid: UUID(), handle: "Incoming Test Call", hasVideo: false) + } } else if (cstate == .OutgoingRinging) { tutorialContext.callRunning = true } else if (cstate == .End) { tutorialContext.callRunning = false + tutorialContext.isCallIncoming = false; } } } diff --git a/swift/CallTutorial/CallTutorial/CallKitProviderDelegate.swift b/swift/CallTutorial/CallTutorial/CallKitProviderDelegate.swift new file mode 100644 index 0000000..877e815 --- /dev/null +++ b/swift/CallTutorial/CallTutorial/CallKitProviderDelegate.swift @@ -0,0 +1,107 @@ +// +// ProviderDelegate.swift +// CallTutorial +// +// Created by QuentinArguillere on 05/08/2020. +// Copyright © 2020 BelledonneCommunications. All rights reserved. +// + +import Foundation +import CallKit +import linphonesw +import AVFoundation + + +class CallKitProviderDelegate : NSObject +{ + private let provider: CXProvider + let mCallController = CXCallController() + var tutorialContext : CallExampleContext! + + init(context : CallExampleContext) + { + tutorialContext = context + let providerConfiguration = CXProviderConfiguration(localizedName: Bundle.main.infoDictionary!["CFBundleName"] as! String) + providerConfiguration.supportsVideo = true + providerConfiguration.supportedHandleTypes = [.generic] + + providerConfiguration.maximumCallsPerCallGroup = 10 + providerConfiguration.maximumCallGroups = 2 + + provider = CXProvider(configuration: providerConfiguration) + super.init() + provider.setDelegate(self, queue: nil) + + } + + func reportIncomingCall(call:Call?, uuid: UUID, handle: String, hasVideo: Bool) { + let update = CXCallUpdate() + update.remoteHandle = CXHandle(type:.generic, value: handle) + update.hasVideo = hasVideo + + provider.reportNewIncomingCall(with: uuid, update: update) { error in + + } + } + + func outgoingCall(uuid : UUID) + { + let handle = CXHandle(type: .generic, value: "Outgoing Call") + let startCallAction = CXStartCallAction(call: uuid, handle: handle) + let transaction = CXTransaction(action: startCallAction) + + mCallController.request(transaction, completion: { error in + print("lalalalala") + }) + } + +} + + + +extension CallKitProviderDelegate: CXProviderDelegate { + + func provider(_ provider: CXProvider, perform action: CXEndCallAction) { + tutorialContext.stopCall() + action.fulfill() + } + + func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) { + tutorialContext.acceptCall() + action.fulfill() + } + + func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) { + + } + + func provider(_ provider: CXProvider, perform action: CXStartCallAction) { + tutorialContext.outgoingCallExample() + action.fulfill() + } + + func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) { + + } + + func provider(_ provider: CXProvider, perform action: CXPlayDTMFCallAction) { + + } + + func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) { + + } + + func providerDidReset(_ provider: CXProvider) { + + } + + + func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) { + tutorialContext.mCore.activateAudioSession(actived: true) + } + + func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) { + tutorialContext.mCore.activateAudioSession(actived: false) + } +} diff --git a/swift/CallTutorial/CallTutorial/ContentView.swift b/swift/CallTutorial/CallTutorial/ContentView.swift index d3c49b1..e96d347 100644 --- a/swift/CallTutorial/CallTutorial/ContentView.swift +++ b/swift/CallTutorial/CallTutorial/ContentView.swift @@ -83,10 +83,10 @@ struct ContentView: View { VStack(alignment: .leading) { Toggle(isOn: $tutorialContext.audioEnabled) { Text("Audio") - }.frame(width : 120.0) + }.frame(width : 140.0) Toggle(isOn: $tutorialContext.videoEnabled) { Text("Video") - }.frame(width : 120.0) + }.frame(width : 140.0) Button(action: tutorialContext.changeVideoDevice) { Text(" Change camera ") @@ -127,7 +127,7 @@ struct ContentView: View { .background(Color.green) } Button(action: tutorialContext.stopCall) { - Text("Stop Call") + Text(tutorialContext.isCallIncoming ? "Decline" : "Stop Call") .font(.largeTitle) .foregroundColor(Color.white) .frame(width: 180.0, height: 42.0) @@ -141,6 +141,19 @@ struct ContentView: View { } .padding(.top) } + + VStack(alignment: .leading) { + Button(action: tutorialContext.outgoingCallKitCallExample) { + Text("CallKit Call") + .font(.largeTitle) + .foregroundColor(Color.white) + .frame(width: 180.0, height: 42.0) + .background(Color.green) + } + Toggle(isOn: $tutorialContext.enableCallKit) { + Text("CallKit incoming Call detection") + }.frame(width : 290.0) + }.padding(.top, 10.0) Spacer() Group { Toggle(isOn: $tutorialContext.logsEnabled) { diff --git a/swift/CallTutorial/CallTutorial/Info.plist b/swift/CallTutorial/CallTutorial/Info.plist index 27a1721..f1eba1b 100644 --- a/swift/CallTutorial/CallTutorial/Info.plist +++ b/swift/CallTutorial/CallTutorial/Info.plist @@ -2,6 +2,10 @@ + UIBackgroundModes + + voip + NSCameraUsageDescription Camera access NSMicrophoneUsageDescription