You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

JMCallKitProxy.swift 6.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. /*
  2. * Copyright @ 2019-present 8x8, Inc.
  3. * Copyright @ 2018-2019 Atlassian Pty Ltd
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. import CallKit
  18. import Foundation
  19. /// JitsiMeet CallKit proxy
  20. // NOTE: The methods this class exposes are meant to be called in the UI thread.
  21. // All delegate methods called by JMCallKitEmitter will be called in the UI thread.
  22. @objc public final class JMCallKitProxy: NSObject {
  23. private override init() {}
  24. // MARK: - CallKit proxy
  25. private static let provider: CXProvider = {
  26. let configuration = CXProviderConfiguration(localizedName: "")
  27. return CXProvider(configuration: configuration)
  28. }()
  29. private static var providerConfiguration: CXProviderConfiguration? {
  30. didSet {
  31. guard let configuration = providerConfiguration else { return }
  32. provider.configuration = configuration
  33. provider.setDelegate(emitter, queue: nil)
  34. }
  35. }
  36. private static let callController: CXCallController = {
  37. return CXCallController()
  38. }()
  39. private static let emitter: JMCallKitEmitter = {
  40. return JMCallKitEmitter()
  41. }()
  42. /// Enables the proxy in between CallKit and the consumers of the SDK.
  43. /// Defaults to enabled, set to false when you don't want to use CallKit.
  44. @objc public static var enabled: Bool = true {
  45. didSet {
  46. if enabled == false {
  47. provider.setDelegate(nil, queue: nil)
  48. provider.invalidate()
  49. }
  50. }
  51. }
  52. @objc public static func configureProvider(localizedName: String,
  53. ringtoneSound: String?,
  54. iconTemplateImageData: Data?) {
  55. guard enabled else { return }
  56. let configuration = CXProviderConfiguration(localizedName: localizedName)
  57. configuration.iconTemplateImageData = iconTemplateImageData
  58. configuration.maximumCallGroups = 1
  59. configuration.maximumCallsPerCallGroup = 1
  60. configuration.ringtoneSound = ringtoneSound
  61. configuration.supportedHandleTypes = [CXHandle.HandleType.generic]
  62. configuration.supportsVideo = true
  63. providerConfiguration = configuration
  64. }
  65. @objc public static func isProviderConfigured() -> Bool {
  66. return providerConfiguration != nil
  67. }
  68. @objc public static func addListener(_ listener: JMCallKitListener) {
  69. emitter.addListener(listener)
  70. }
  71. @objc public static func removeListener(_ listener: JMCallKitListener) {
  72. emitter.removeListener(listener)
  73. }
  74. @objc public static func hasActiveCallForUUID(_ callUUID: String) -> Bool {
  75. let activeCallForUUID = callController.callObserver.calls.first {
  76. $0.uuid == UUID(uuidString: callUUID)
  77. }
  78. guard activeCallForUUID != nil else { return false }
  79. return true
  80. }
  81. @objc public static func reportNewIncomingCall(
  82. UUID: UUID,
  83. handle: String?,
  84. displayName: String?,
  85. hasVideo: Bool,
  86. completion: @escaping (Error?) -> Void) {
  87. guard enabled else { return }
  88. let callUpdate = makeCXUpdate(handle: handle,
  89. displayName: displayName,
  90. hasVideo: hasVideo)
  91. provider.reportNewIncomingCall(with: UUID,
  92. update: callUpdate,
  93. completion: completion)
  94. }
  95. @objc public static func reportCallUpdate(with UUID: UUID,
  96. handle: String?,
  97. displayName: String?,
  98. hasVideo: Bool) {
  99. guard enabled else { return }
  100. let callUpdate = makeCXUpdate(handle: handle,
  101. displayName: displayName,
  102. hasVideo: hasVideo)
  103. provider.reportCall(with: UUID, updated: callUpdate)
  104. }
  105. @objc public static func reportCall(
  106. with UUID: UUID,
  107. endedAt dateEnded: Date?,
  108. reason endedReason: CXCallEndedReason) {
  109. guard enabled else { return }
  110. provider.reportCall(with: UUID,
  111. endedAt: dateEnded,
  112. reason: endedReason)
  113. }
  114. @objc public static func reportOutgoingCall(
  115. with UUID: UUID,
  116. startedConnectingAt dateStartedConnecting: Date?) {
  117. guard enabled else { return }
  118. provider.reportOutgoingCall(with: UUID,
  119. startedConnectingAt: dateStartedConnecting)
  120. }
  121. @objc public static func reportOutgoingCall(
  122. with UUID: UUID,
  123. connectedAt dateConnected: Date?) {
  124. guard enabled else { return }
  125. provider.reportOutgoingCall(with: UUID, connectedAt: dateConnected)
  126. }
  127. @objc public static func request(
  128. _ transaction: CXTransaction,
  129. completion: @escaping (Error?) -> Swift.Void) {
  130. guard enabled else { return }
  131. // XXX keep track of muted actions to avoid "ping-pong"ing. See
  132. // JMCallKitEmitter for details on the CXSetMutedCallAction handling.
  133. for action in transaction.actions {
  134. if (action as? CXSetMutedCallAction) != nil {
  135. emitter.addMuteAction(action.uuid)
  136. }
  137. }
  138. callController.request(transaction, completion: completion)
  139. }
  140. // MARK: - Callkit Proxy helpers
  141. private static func makeCXUpdate(handle: String?,
  142. displayName: String?,
  143. hasVideo: Bool) -> CXCallUpdate {
  144. let update = CXCallUpdate()
  145. update.supportsDTMF = false
  146. update.supportsHolding = false
  147. update.supportsGrouping = false
  148. update.supportsUngrouping = false
  149. update.hasVideo = hasVideo
  150. if let handle = handle {
  151. update.remoteHandle = CXHandle(type: .generic,
  152. value: handle)
  153. }
  154. if let displayName = displayName {
  155. update.localizedCallerName = displayName
  156. }
  157. return update
  158. }
  159. }