SwiftFlutterCallkitIncomingPlugin.swift 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712
  1. import Flutter
  2. import UIKit
  3. import CallKit
  4. import AVFoundation
  5. @available(iOS 10.0, *)
  6. public class SwiftFlutterCallkitIncomingPlugin: NSObject, FlutterPlugin, CXProviderDelegate {
  7. static let ACTION_DID_UPDATE_DEVICE_PUSH_TOKEN_VOIP = "com.hiennv.flutter_callkit_incoming.DID_UPDATE_DEVICE_PUSH_TOKEN_VOIP"
  8. static let ACTION_CALL_INCOMING = "com.hiennv.flutter_callkit_incoming.ACTION_CALL_INCOMING"
  9. static let ACTION_CALL_START = "com.hiennv.flutter_callkit_incoming.ACTION_CALL_START"
  10. static let ACTION_CALL_ACCEPT = "com.hiennv.flutter_callkit_incoming.ACTION_CALL_ACCEPT"
  11. static let ACTION_CALL_DECLINE = "com.hiennv.flutter_callkit_incoming.ACTION_CALL_DECLINE"
  12. static let ACTION_CALL_ENDED = "com.hiennv.flutter_callkit_incoming.ACTION_CALL_ENDED"
  13. static let ACTION_CALL_TIMEOUT = "com.hiennv.flutter_callkit_incoming.ACTION_CALL_TIMEOUT"
  14. static let ACTION_CALL_CUSTOM = "com.hiennv.flutter_callkit_incoming.ACTION_CALL_CUSTOM"
  15. static let ACTION_CALL_TOGGLE_HOLD = "com.hiennv.flutter_callkit_incoming.ACTION_CALL_TOGGLE_HOLD"
  16. static let ACTION_CALL_TOGGLE_MUTE = "com.hiennv.flutter_callkit_incoming.ACTION_CALL_TOGGLE_MUTE"
  17. static let ACTION_CALL_TOGGLE_DMTF = "com.hiennv.flutter_callkit_incoming.ACTION_CALL_TOGGLE_DMTF"
  18. static let ACTION_CALL_TOGGLE_GROUP = "com.hiennv.flutter_callkit_incoming.ACTION_CALL_TOGGLE_GROUP"
  19. static let ACTION_CALL_TOGGLE_AUDIO_SESSION = "com.hiennv.flutter_callkit_incoming.ACTION_CALL_TOGGLE_AUDIO_SESSION"
  20. @objc public private(set) static var sharedInstance: SwiftFlutterCallkitIncomingPlugin!
  21. private var streamHandlers: WeakArray<EventCallbackHandler> = WeakArray([])
  22. private var callManager: CallManager
  23. private var sharedProvider: CXProvider? = nil
  24. private var outgoingCall : Call?
  25. private var answerCall : Call?
  26. private var data: Data?
  27. private var isFromPushKit: Bool = false
  28. private var silenceEvents: Bool = false
  29. private let devicePushTokenVoIP = "DevicePushTokenVoIP"
  30. private func sendEvent(_ event: String, _ body: [String : Any?]?) {
  31. if silenceEvents {
  32. print(event, " silenced")
  33. return
  34. } else {
  35. streamHandlers.reap().forEach { handler in
  36. handler?.send(event, body ?? [:])
  37. }
  38. }
  39. }
  40. @objc public func sendEventCustom(_ event: String, body: NSDictionary?) {
  41. streamHandlers.reap().forEach { handler in
  42. handler?.send(event, body ?? [:])
  43. }
  44. }
  45. public static func sharePluginWithRegister(with registrar: FlutterPluginRegistrar) {
  46. if(sharedInstance == nil){
  47. sharedInstance = SwiftFlutterCallkitIncomingPlugin(messenger: registrar.messenger())
  48. }
  49. sharedInstance.shareHandlers(with: registrar)
  50. }
  51. public static func register(with registrar: FlutterPluginRegistrar) {
  52. sharePluginWithRegister(with: registrar)
  53. }
  54. private static func createMethodChannel(messenger: FlutterBinaryMessenger) -> FlutterMethodChannel {
  55. return FlutterMethodChannel(name: "flutter_callkit_incoming", binaryMessenger: messenger)
  56. }
  57. private static func createEventChannel(messenger: FlutterBinaryMessenger) -> FlutterEventChannel {
  58. return FlutterEventChannel(name: "flutter_callkit_incoming_events", binaryMessenger: messenger)
  59. }
  60. public init(messenger: FlutterBinaryMessenger) {
  61. callManager = CallManager()
  62. }
  63. private func shareHandlers(with registrar: FlutterPluginRegistrar) {
  64. registrar.addMethodCallDelegate(self, channel: Self.createMethodChannel(messenger: registrar.messenger()))
  65. let eventsHandler = EventCallbackHandler()
  66. self.streamHandlers.append(eventsHandler)
  67. Self.createEventChannel(messenger: registrar.messenger()).setStreamHandler(eventsHandler)
  68. }
  69. public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
  70. switch call.method {
  71. case "showCallkitIncoming":
  72. guard let args = call.arguments else {
  73. result("OK")
  74. return
  75. }
  76. if let getArgs = args as? [String: Any] {
  77. self.data = Data(args: getArgs)
  78. showCallkitIncoming(self.data!, fromPushKit: false)
  79. }
  80. result("OK")
  81. break
  82. case "showMissCallNotification":
  83. result("OK")
  84. break
  85. case "startCall":
  86. guard let args = call.arguments else {
  87. result("OK")
  88. return
  89. }
  90. if let getArgs = args as? [String: Any] {
  91. self.data = Data(args: getArgs)
  92. self.startCall(self.data!, fromPushKit: false)
  93. }
  94. result("OK")
  95. break
  96. case "endCall":
  97. guard let args = call.arguments else {
  98. result("OK")
  99. return
  100. }
  101. if(self.isFromPushKit){
  102. self.endCall(self.data!)
  103. }else{
  104. if let getArgs = args as? [String: Any] {
  105. self.data = Data(args: getArgs)
  106. self.endCall(self.data!)
  107. }
  108. }
  109. result("OK")
  110. break
  111. case "muteCall":
  112. guard let args = call.arguments as? [String: Any] ,
  113. let callId = args["id"] as? String,
  114. let isMuted = args["isMuted"] as? Bool else {
  115. result("OK")
  116. return
  117. }
  118. self.muteCall(callId, isMuted: isMuted)
  119. result("OK")
  120. break
  121. case "isMuted":
  122. guard let args = call.arguments as? [String: Any] ,
  123. let callId = args["id"] as? String else{
  124. result(false)
  125. return
  126. }
  127. guard let callUUID = UUID(uuidString: callId),
  128. let call = self.callManager.callWithUUID(uuid: callUUID) else {
  129. result(false)
  130. return
  131. }
  132. result(call.isMuted)
  133. break
  134. case "holdCall":
  135. guard let args = call.arguments as? [String: Any] ,
  136. let callId = args["id"] as? String,
  137. let onHold = args["isOnHold"] as? Bool else {
  138. result("OK")
  139. return
  140. }
  141. self.holdCall(callId, onHold: onHold)
  142. result("OK")
  143. break
  144. case "callConnected":
  145. guard let args = call.arguments else {
  146. result("OK")
  147. return
  148. }
  149. if(self.isFromPushKit){
  150. self.connectedCall(self.data!)
  151. }else{
  152. if let getArgs = args as? [String: Any] {
  153. self.data = Data(args: getArgs)
  154. self.connectedCall(self.data!)
  155. }
  156. }
  157. result("OK")
  158. break
  159. case "activeCalls":
  160. result(self.callManager.activeCalls())
  161. break;
  162. case "endAllCalls":
  163. self.callManager.endCallAlls()
  164. result("OK")
  165. break
  166. case "getDevicePushTokenVoIP":
  167. result(self.getDevicePushTokenVoIP())
  168. break;
  169. case "silenceEvents":
  170. guard let silence = call.arguments as? Bool else {
  171. result("OK")
  172. return
  173. }
  174. self.silenceEvents = silence
  175. result("OK")
  176. break;
  177. case "requestNotificationPermission":
  178. result("OK")
  179. break
  180. case "requestFullIntentPermission":
  181. result("OK")
  182. break
  183. case "hideCallkitIncoming":
  184. result("OK")
  185. break
  186. case "endNativeSubsystemOnly":
  187. result("OK")
  188. break
  189. case "setAudioRoute":
  190. result("OK")
  191. break
  192. default:
  193. result(FlutterMethodNotImplemented)
  194. }
  195. }
  196. @objc public func setDevicePushTokenVoIP(_ deviceToken: String) {
  197. UserDefaults.standard.set(deviceToken, forKey: devicePushTokenVoIP)
  198. self.sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_DID_UPDATE_DEVICE_PUSH_TOKEN_VOIP, ["deviceTokenVoIP":deviceToken])
  199. }
  200. @objc public func getDevicePushTokenVoIP() -> String {
  201. return UserDefaults.standard.string(forKey: devicePushTokenVoIP) ?? ""
  202. }
  203. @objc public func getAcceptedCall() -> Data? {
  204. NSLog("Call data ids \(String(describing: data?.uuid)) \(String(describing: answerCall?.uuid.uuidString))")
  205. if data?.uuid.lowercased() == answerCall?.uuid.uuidString.lowercased() {
  206. return data
  207. }
  208. return nil
  209. }
  210. @objc public func showCallkitIncoming(_ data: Data, fromPushKit: Bool) {
  211. self.isFromPushKit = fromPushKit
  212. if(fromPushKit){
  213. self.data = data
  214. }
  215. var handle: CXHandle?
  216. handle = CXHandle(type: self.getHandleType(data.handleType), value: data.getEncryptHandle())
  217. let callUpdate = CXCallUpdate()
  218. callUpdate.remoteHandle = handle
  219. callUpdate.supportsDTMF = data.supportsDTMF
  220. callUpdate.supportsHolding = data.supportsHolding
  221. callUpdate.supportsGrouping = data.supportsGrouping
  222. callUpdate.supportsUngrouping = data.supportsUngrouping
  223. callUpdate.hasVideo = data.type > 0 ? true : false
  224. callUpdate.localizedCallerName = data.nameCaller
  225. initCallkitProvider(data)
  226. let uuid = UUID(uuidString: data.uuid)
  227. configurAudioSession()
  228. self.sharedProvider?.reportNewIncomingCall(with: uuid!, update: callUpdate) { error in
  229. if(error == nil) {
  230. self.configurAudioSession()
  231. let call = Call(uuid: uuid!, data: data)
  232. call.handle = data.handle
  233. self.callManager.addCall(call)
  234. self.sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_INCOMING, data.toJSON())
  235. self.endCallNotExist(data)
  236. }
  237. }
  238. }
  239. @objc public func startCall(_ data: Data, fromPushKit: Bool) {
  240. self.isFromPushKit = fromPushKit
  241. if(fromPushKit){
  242. self.data = data
  243. }
  244. initCallkitProvider(data)
  245. self.callManager.startCall(data)
  246. }
  247. @objc public func muteCall(_ callId: String, isMuted: Bool) {
  248. guard let callId = UUID(uuidString: callId),
  249. let call = self.callManager.callWithUUID(uuid: callId) else {
  250. return
  251. }
  252. if call.isMuted == isMuted {
  253. self.sendMuteEvent(callId.uuidString, isMuted)
  254. } else {
  255. self.callManager.muteCall(call: call, isMuted: isMuted)
  256. }
  257. }
  258. @objc public func holdCall(_ callId: String, onHold: Bool) {
  259. guard let callId = UUID(uuidString: callId),
  260. let call = self.callManager.callWithUUID(uuid: callId) else {
  261. return
  262. }
  263. if call.isOnHold == onHold {
  264. self.sendMuteEvent(callId.uuidString, onHold)
  265. } else {
  266. self.callManager.holdCall(call: call, onHold: onHold)
  267. }
  268. }
  269. @objc public func endCall(_ data: Data) {
  270. var call: Call? = nil
  271. if(self.isFromPushKit){
  272. call = Call(uuid: UUID(uuidString: self.data!.uuid)!, data: data)
  273. self.isFromPushKit = false
  274. self.sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_ENDED, data.toJSON())
  275. }else {
  276. call = Call(uuid: UUID(uuidString: data.uuid)!, data: data)
  277. }
  278. self.callManager.endCall(call: call!)
  279. }
  280. @objc public func connectedCall(_ data: Data) {
  281. var call: Call? = nil
  282. if(self.isFromPushKit){
  283. call = Call(uuid: UUID(uuidString: self.data!.uuid)!, data: data)
  284. self.isFromPushKit = false
  285. }else {
  286. call = Call(uuid: UUID(uuidString: data.uuid)!, data: data)
  287. }
  288. self.callManager.connectedCall(call: call!)
  289. }
  290. @objc public func activeCalls() -> [[String: Any]] {
  291. return self.callManager.activeCalls()
  292. }
  293. @objc public func endAllCalls() {
  294. self.isFromPushKit = false
  295. self.callManager.endCallAlls()
  296. }
  297. public func saveEndCall(_ uuid: String, _ reason: Int) {
  298. switch reason {
  299. case 1:
  300. self.sharedProvider?.reportCall(with: UUID(uuidString: uuid)!, endedAt: Date(), reason: CXCallEndedReason.failed)
  301. break
  302. case 2, 6:
  303. self.sharedProvider?.reportCall(with: UUID(uuidString: uuid)!, endedAt: Date(), reason: CXCallEndedReason.remoteEnded)
  304. break
  305. case 3:
  306. self.sharedProvider?.reportCall(with: UUID(uuidString: uuid)!, endedAt: Date(), reason: CXCallEndedReason.unanswered)
  307. break
  308. case 4:
  309. self.sharedProvider?.reportCall(with: UUID(uuidString: uuid)!, endedAt: Date(), reason: CXCallEndedReason.answeredElsewhere)
  310. break
  311. case 5:
  312. self.sharedProvider?.reportCall(with: UUID(uuidString: uuid)!, endedAt: Date(), reason: CXCallEndedReason.declinedElsewhere)
  313. break
  314. default:
  315. break
  316. }
  317. }
  318. func endCallNotExist(_ data: Data) {
  319. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(data.duration)) {
  320. let call = self.callManager.callWithUUID(uuid: UUID(uuidString: data.uuid)!)
  321. if (call != nil && self.answerCall == nil && self.outgoingCall == nil) {
  322. self.callEndTimeout(data)
  323. }
  324. }
  325. }
  326. func callEndTimeout(_ data: Data) {
  327. self.saveEndCall(data.uuid, 3)
  328. guard let call = self.callManager.callWithUUID(uuid: UUID(uuidString: data.uuid)!) else {
  329. return
  330. }
  331. sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_TIMEOUT, data.toJSON())
  332. if let appDelegate = UIApplication.shared.delegate as? CallkitIncomingAppDelegate {
  333. appDelegate.onTimeOut(call)
  334. }
  335. }
  336. func getHandleType(_ handleType: String?) -> CXHandle.HandleType {
  337. var typeDefault = CXHandle.HandleType.generic
  338. switch handleType {
  339. case "number":
  340. typeDefault = CXHandle.HandleType.phoneNumber
  341. break
  342. case "email":
  343. typeDefault = CXHandle.HandleType.emailAddress
  344. default:
  345. typeDefault = CXHandle.HandleType.generic
  346. }
  347. return typeDefault
  348. }
  349. func initCallkitProvider(_ data: Data) {
  350. if(self.sharedProvider == nil){
  351. self.sharedProvider = CXProvider(configuration: createConfiguration(data))
  352. self.sharedProvider?.setDelegate(self, queue: nil)
  353. }
  354. self.callManager.setSharedProvider(self.sharedProvider!)
  355. }
  356. func createConfiguration(_ data: Data) -> CXProviderConfiguration {
  357. let configuration = CXProviderConfiguration(localizedName: data.appName)
  358. configuration.supportsVideo = data.supportsVideo
  359. configuration.maximumCallGroups = data.maximumCallGroups
  360. configuration.maximumCallsPerCallGroup = data.maximumCallsPerCallGroup
  361. configuration.supportedHandleTypes = [
  362. CXHandle.HandleType.generic,
  363. CXHandle.HandleType.emailAddress,
  364. CXHandle.HandleType.phoneNumber
  365. ]
  366. if #available(iOS 11.0, *) {
  367. configuration.includesCallsInRecents = data.includesCallsInRecents
  368. }
  369. if !data.iconName.isEmpty {
  370. if let image = UIImage(named: data.iconName) {
  371. configuration.iconTemplateImageData = image.pngData()
  372. } else {
  373. print("Unable to load icon \(data.iconName).");
  374. }
  375. }
  376. if !data.ringtonePath.isEmpty || data.ringtonePath != "system_ringtone_default" {
  377. configuration.ringtoneSound = data.ringtonePath
  378. }
  379. return configuration
  380. }
  381. func sendDefaultAudioInterruptionNofificationToStartAudioResource(){
  382. var userInfo : [AnyHashable : Any] = [:]
  383. let intrepEndeRaw = AVAudioSession.InterruptionType.ended.rawValue
  384. userInfo[AVAudioSessionInterruptionTypeKey] = intrepEndeRaw
  385. userInfo[AVAudioSessionInterruptionOptionKey] = AVAudioSession.InterruptionOptions.shouldResume.rawValue
  386. NotificationCenter.default.post(name: AVAudioSession.interruptionNotification, object: self, userInfo: userInfo)
  387. }
  388. func configurAudioSession(){
  389. if data?.configureAudioSession != false {
  390. let session = AVAudioSession.sharedInstance()
  391. do{
  392. try session.setCategory(AVAudioSession.Category.playAndRecord, options: [
  393. .allowBluetoothA2DP,
  394. .duckOthers,
  395. .allowBluetooth,
  396. ])
  397. try session.setMode(self.getAudioSessionMode(data?.audioSessionMode))
  398. try session.setActive(data?.audioSessionActive ?? true)
  399. try session.setPreferredSampleRate(data?.audioSessionPreferredSampleRate ?? 44100.0)
  400. try session.setPreferredIOBufferDuration(data?.audioSessionPreferredIOBufferDuration ?? 0.005)
  401. }catch{
  402. print(error)
  403. }
  404. }
  405. }
  406. func getAudioSessionMode(_ audioSessionMode: String?) -> AVAudioSession.Mode {
  407. var mode = AVAudioSession.Mode.default
  408. switch audioSessionMode {
  409. case "gameChat":
  410. mode = AVAudioSession.Mode.gameChat
  411. break
  412. case "measurement":
  413. mode = AVAudioSession.Mode.measurement
  414. break
  415. case "moviePlayback":
  416. mode = AVAudioSession.Mode.moviePlayback
  417. break
  418. case "spokenAudio":
  419. mode = AVAudioSession.Mode.spokenAudio
  420. break
  421. case "videoChat":
  422. mode = AVAudioSession.Mode.videoChat
  423. break
  424. case "videoRecording":
  425. mode = AVAudioSession.Mode.videoRecording
  426. break
  427. case "voiceChat":
  428. mode = AVAudioSession.Mode.voiceChat
  429. break
  430. case "voicePrompt":
  431. if #available(iOS 12.0, *) {
  432. mode = AVAudioSession.Mode.voicePrompt
  433. } else {
  434. // Fallback on earlier versions
  435. }
  436. break
  437. default:
  438. mode = AVAudioSession.Mode.default
  439. }
  440. return mode
  441. }
  442. public func providerDidReset(_ provider: CXProvider) {
  443. for call in self.callManager.calls {
  444. call.endCall()
  445. }
  446. self.callManager.removeAllCalls()
  447. }
  448. public func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
  449. let call = Call(uuid: action.callUUID, data: self.data!, isOutGoing: true)
  450. call.handle = action.handle.value
  451. configurAudioSession()
  452. call.hasStartedConnectDidChange = { [weak self] in
  453. self?.sharedProvider?.reportOutgoingCall(with: call.uuid, startedConnectingAt: call.connectData)
  454. }
  455. call.hasConnectDidChange = { [weak self] in
  456. self?.sharedProvider?.reportOutgoingCall(with: call.uuid, connectedAt: call.connectedData)
  457. }
  458. self.outgoingCall = call;
  459. self.callManager.addCall(call)
  460. self.sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_START, self.data?.toJSON())
  461. action.fulfill()
  462. }
  463. public func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
  464. guard let call = self.callManager.callWithUUID(uuid: action.callUUID) else{
  465. action.fail()
  466. return
  467. }
  468. self.configurAudioSession()
  469. DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(1200)) {
  470. self.configurAudioSession()
  471. }
  472. call.hasConnectDidChange = { [weak self] in
  473. self?.sharedProvider?.reportOutgoingCall(with: call.uuid, connectedAt: call.connectedData)
  474. }
  475. self.answerCall = call
  476. sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_ACCEPT, self.data?.toJSON())
  477. if let appDelegate = UIApplication.shared.delegate as? CallkitIncomingAppDelegate {
  478. appDelegate.onAccept(call, action)
  479. }else {
  480. action.fulfill()
  481. }
  482. }
  483. // private func checkUnlockedAndFulfill(action: CXAnswerCallAction, counter: Int) {
  484. // if UIApplication.shared.isProtectedDataAvailable {
  485. // action.fulfill()
  486. // } else if counter > 180 { // fail if waiting for more then 3 minutes
  487. // action.fail()
  488. // } else {
  489. // DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
  490. // self.checkUnlockedAndFulfill(action: action, counter: counter + 1)
  491. // }
  492. // }
  493. // }
  494. public func provider(_ provider: CXProvider, perform action: CXEndCallAction) {
  495. guard let call = self.callManager.callWithUUID(uuid: action.callUUID) else {
  496. if(self.answerCall == nil && self.outgoingCall == nil){
  497. sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_TIMEOUT, self.data?.toJSON())
  498. } else {
  499. sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_ENDED, self.data?.toJSON())
  500. }
  501. action.fail()
  502. return
  503. }
  504. call.endCall()
  505. self.callManager.removeCall(call)
  506. if (self.answerCall == nil && self.outgoingCall == nil) {
  507. sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_DECLINE, self.data?.toJSON())
  508. if let appDelegate = UIApplication.shared.delegate as? CallkitIncomingAppDelegate {
  509. appDelegate.onDecline(call, action)
  510. } else {
  511. action.fulfill()
  512. }
  513. }else {
  514. self.answerCall = nil
  515. sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_ENDED, call.data.toJSON())
  516. if let appDelegate = UIApplication.shared.delegate as? CallkitIncomingAppDelegate {
  517. appDelegate.onEnd(call, action)
  518. } else {
  519. action.fulfill()
  520. }
  521. }
  522. }
  523. public func provider(_ provider: CXProvider, perform action: CXSetHeldCallAction) {
  524. guard let call = self.callManager.callWithUUID(uuid: action.callUUID) else {
  525. action.fail()
  526. return
  527. }
  528. call.isOnHold = action.isOnHold
  529. call.isMuted = action.isOnHold
  530. self.callManager.setHold(call: call, onHold: action.isOnHold)
  531. sendHoldEvent(action.callUUID.uuidString, action.isOnHold)
  532. action.fulfill()
  533. }
  534. public func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
  535. guard let call = self.callManager.callWithUUID(uuid: action.callUUID) else {
  536. action.fail()
  537. return
  538. }
  539. call.isMuted = action.isMuted
  540. sendMuteEvent(action.callUUID.uuidString, action.isMuted)
  541. action.fulfill()
  542. }
  543. public func provider(_ provider: CXProvider, perform action: CXSetGroupCallAction) {
  544. guard (self.callManager.callWithUUID(uuid: action.callUUID)) != nil else {
  545. action.fail()
  546. return
  547. }
  548. self.sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_TOGGLE_GROUP, [ "id": action.callUUID.uuidString, "callUUIDToGroupWith" : action.callUUIDToGroupWith?.uuidString])
  549. action.fulfill()
  550. }
  551. public func provider(_ provider: CXProvider, perform action: CXPlayDTMFCallAction) {
  552. guard (self.callManager.callWithUUID(uuid: action.callUUID)) != nil else {
  553. action.fail()
  554. return
  555. }
  556. self.sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_TOGGLE_DMTF, [ "id": action.callUUID.uuidString, "digits": action.digits, "type": action.type.rawValue ])
  557. action.fulfill()
  558. }
  559. public func provider(_ provider: CXProvider, timedOutPerforming action: CXAction) {
  560. guard let call = self.callManager.callWithUUID(uuid: action.uuid) else {
  561. action.fail()
  562. return
  563. }
  564. sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_TIMEOUT, self.data?.toJSON())
  565. if let appDelegate = UIApplication.shared.delegate as? CallkitIncomingAppDelegate {
  566. appDelegate.onTimeOut(call)
  567. }
  568. action.fulfill()
  569. }
  570. public func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
  571. if let appDelegate = UIApplication.shared.delegate as? CallkitIncomingAppDelegate {
  572. appDelegate.didActivateAudioSession(audioSession)
  573. }
  574. if(self.answerCall?.hasConnected ?? false){
  575. sendDefaultAudioInterruptionNofificationToStartAudioResource()
  576. return
  577. }
  578. if(self.outgoingCall?.hasConnected ?? false){
  579. sendDefaultAudioInterruptionNofificationToStartAudioResource()
  580. return
  581. }
  582. self.outgoingCall?.startCall(withAudioSession: audioSession) {success in
  583. if success {
  584. self.callManager.addCall(self.outgoingCall!)
  585. self.outgoingCall?.startAudio()
  586. }
  587. }
  588. self.answerCall?.ansCall(withAudioSession: audioSession) { success in
  589. if success{
  590. self.answerCall?.startAudio()
  591. }
  592. }
  593. sendDefaultAudioInterruptionNofificationToStartAudioResource()
  594. configurAudioSession()
  595. self.sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_TOGGLE_AUDIO_SESSION, [ "isActivate": true ])
  596. }
  597. public func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
  598. if let appDelegate = UIApplication.shared.delegate as? CallkitIncomingAppDelegate {
  599. appDelegate.didDeactivateAudioSession(audioSession)
  600. }
  601. if self.outgoingCall?.isOnHold ?? false || self.answerCall?.isOnHold ?? false{
  602. print("Call is on hold")
  603. return
  604. }
  605. self.sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_TOGGLE_AUDIO_SESSION, [ "isActivate": false ])
  606. }
  607. private func sendMuteEvent(_ id: String, _ isMuted: Bool) {
  608. self.sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_TOGGLE_MUTE, [ "id": id, "isMuted": isMuted ])
  609. }
  610. private func sendHoldEvent(_ id: String, _ isOnHold: Bool) {
  611. self.sendEvent(SwiftFlutterCallkitIncomingPlugin.ACTION_CALL_TOGGLE_HOLD, [ "id": id, "isOnHold": isOnHold ])
  612. }
  613. }
  614. class EventCallbackHandler: NSObject, FlutterStreamHandler {
  615. private var eventSink: FlutterEventSink?
  616. public func send(_ event: String, _ body: Any) {
  617. let data: [String : Any] = [
  618. "event": event,
  619. "body": body
  620. ]
  621. eventSink?(data)
  622. }
  623. func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? {
  624. self.eventSink = events
  625. return nil
  626. }
  627. func onCancel(withArguments arguments: Any?) -> FlutterError? {
  628. self.eventSink = nil
  629. return nil
  630. }
  631. }