Compartilhar via


Integração com o CallKit

Neste documento, veremos como integrar o CallKit ao seu aplicativo iOS.

Pré-requisitos

Integração do CallKit (dentro do SDK)

A integração do CallKit no SDK do iOS dos Serviços de Comunicação do Azure lida com a interação com o CallKit para nós. Para executar quaisquer operações de chamada como silenciar/desativar o som, reter/retomar, só precisamos chamar a API no SDK dos Serviços de Comunicação do Azure.

Inicializar agente de chamada com CallKitOptions

Com a instância configurada do , podemos criar o CallAgent tratamento com do .CallKitOptionsCallKit

let options = CallAgentOptions()
let callKitOptions = CallKitOptions(with: createProviderConfig())
options.callKitOptions = callKitOptions

// Configure the properties of `CallKitOptions` instance here

self.callClient!.createCallAgent(userCredential: userCredential,
    options: options,
    completionHandler: { (callAgent, error) in
    // Initialization
})

Especificar informações do destinatário da chamada para chamadas de saída

Primeiro, precisamos criar uma instância de para chamadas de saída ou JoinCallOptions() para chamadas em StartCallOptions() grupo:

let options = StartCallOptions()

ou

let options = JoinCallOptions()

Em seguida, crie uma instância de CallKitRemoteInfo

options.callKitRemoteInfo = CallKitRemoteInfo()
  1. Atribua valor para personalizar o nome de exibição para callKitRemoteInfo.displayNameForCallKit destinatários de chamada e configure CXHandle o valor. Esse valor especificado em displayNameForCallKit é exatamente como ele aparece no último log de chamadas discadas. no último registro de chamadas discado.
options.callKitRemoteInfo.displayNameForCallKit = "DISPLAY_NAME"
  1. Atribuir o valor é o que o aplicativo recebe quando o cxHandle usuário chama de volta esse contato
options.callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")

Especificar informações do destinatário da chamada para chamadas recebidas

Primeiro precisamos criar uma instância de CallKitOptions:

let callKitOptions = CallKitOptions(with: createProviderConfig())

Configure as propriedades da CallKitOptions instância:

O bloco que é passado para variável provideRemoteInfo será chamado pelo SDK quando recebermos uma chamada de entrada e precisamos obter um nome de exibição para o chamador de entrada, que precisamos passar para o CallKit.

callKitOptions.provideRemoteInfo = self.provideCallKitRemoteInfo

func provideCallKitRemoteInfo(callerInfo: CallerInfo) -> CallKitRemoteInfo
{
    let callKitRemoteInfo = CallKitRemoteInfo()
    callKitRemoteInfo.displayName = "CALL_TO_PHONENUMBER_BY_APP"      
    callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")
    return callKitRemoteInfo
}

Configurar sessão de áudio

Configurar sessão de áudio será chamado antes de fazer ou aceitar chamada de entrada e antes de retomar a chamada depois de ter sido colocada em espera.

callKitOptions.configureAudioSession = self.configureAudioSession

public func configureAudioSession() -> Error? {
    let audioSession: AVAudioSession = AVAudioSession.sharedInstance()
    var configError: Error?
    do {
        try audioSession.setCategory(.playAndRecord)
    } catch {
        configError = error
    }
    return configError
}

Observação : nos casos em que a Contoso já configurou sessões de áudio NÃO fornecer nil , mas retornar nil erro no bloco

callKitOptions.configureAudioSession = self.configureAudioSession

public func configureAudioSession() -> Error? {
    return nil
}

se nil for fornecido configureAudioSession , o SDK chama a implementação padrão no SDK.

Lidar com a carga de notificação por push recebida

Quando o aplicativo recebe a carga de notificação por push recebida, precisamos ligar handlePush para processá-la. O SDK de Chamada dos Serviços de Comunicação do Azure gerará o IncomingCall evento.

public func handlePushNotification(_ pushPayload: PKPushPayload)
{
    let callNotification = PushNotificationInfo.fromDictionary(pushPayload.dictionaryPayload)
    if let agent = self.callAgent {
        agent.handlePush(notification: callNotification) { (error) in }
    }
}

// Event raised by the SDK
public func callAgent(_ callAgent: CallAgent, didRecieveIncomingCall incomingcall: IncomingCall) {
}

Podemos usar reportIncomingCall para lidar com notificações push quando o aplicativo é fechado ou de outra forma.

if let agent = self.callAgent {
  /* App is not in a killed state */
  agent.handlePush(notification: callNotification) { (error) in }
} else {
  /* App is in a killed state */
  CallClient.reportIncomingCall(with: callNotification, callKitOptions: callKitOptions) { (error) in
      if (error == nil) {
          DispatchQueue.global().async {
              self.callClient = CallClient()
              let options = CallAgentOptions()
              let callKitOptions = CallKitOptions(with: createProviderConfig())
              callKitOptions.provideRemoteInfo = self.provideCallKitRemoteInfo
              callKitOptions.configureAudioSession = self.configureAudioSession
              options.callKitOptions = callKitOptions
              self.callClient!.createCallAgent(userCredential: userCredential,
                  options: options,
                  completionHandler: { (callAgent, error) in
                  if (error == nil) {
                      self.callAgent = callAgent
                      self.callAgent!.handlePush(notification: callNotification) { (error) in }
                  }
              })
          }
      } else {
          os_log("SDK couldn't handle push notification", log:self.log)
      }
  }
}

Integração do CallKit (dentro do aplicativo)

Se você deseja integrar o CallKit no aplicativo e não usar a implementação do CallKit no SDK, consulte o exemplo de início rápido aqui. Mas uma das coisas importantes a se cuidar é iniciar o áudio no momento certo. Curta a seguir

let outgoingAudioOptions = OutgoingAudioOptions()
outgoingAudioOptions.muted = true

let incomingAudioOptions = IncomingAudioOptions()
incomingAudioOptions.muted = true

var copyAcceptCallOptions = AcceptCallOptions()
copyStartCallOptions.outgoingAudioOptions = outgoingAudioOptions
copyStartCallOptions.incomingAudioOptions = incomingAudioOptions

callAgent.startCall(participants: participants,
                    options: copyStartCallOptions,
                    completionHandler: completionBlock)

O silenciamento do alto-falante e do microfone garante que os dispositivos de áudio físicos não sejam usados até que o CallKit ligue o didActivateAudioSession CXProviderDelegate. Caso contrário, a chamada pode ser descartada ou o áudio não funcionará. Quando didActivateAudioSession é quando os fluxos de áudio devem ser iniciados.

func provider(_ provider: CXProvider, didActivate audioSession: AVAudioSession) {
    Task {
        guard let activeCall = await self.callKitHelper.getActiveCall() else {
            print("No active calls found when activating audio session !!")
            return
        }

        try await startAudio(call: activeCall)
    }
}

func provider(_ provider: CXProvider, didDeactivate audioSession: AVAudioSession) {
    Task {
        guard let activeCall = await self.callKitHelper.getActiveCall() else {
            print("No active calls found when deactivating audio session !!")
            return
        }

        try await stopAudio(call: activeCall)
    }
}

private func stopAudio(call: Call) async throws {
    try await self.callKitHelper.muteCall(callId: call.id, isMuted: true)
    try await call.stopAudio(stream: call.activeOutgoingAudioStream)

    try await call.stopAudio(stream: call.activeIncomingAudioStream)
    try await call.muteIncomingAudio()
}

private func startAudio(call: Call) async throws {
    try await call.startAudio(stream: LocalOutgoingAudioStream())
    try await self.callKitHelper.muteCall(callId: call.id, isMuted: false)

    try await call.startAudio(stream: RemoteIncomingAudioStream())
    try await call.unmuteIncomingAudio()
}
    

É importante também silenciar o áudio de saída antes de parar o áudio nos casos em que o CallKit não invoca didActivateAudioSessiono . O usuário pode então desativar manualmente o som do microfone.

Observação

Em alguns casos, o CallKit não chama didActivateAudioSession mesmo que o aplicativo tenha permissões de áudio elevadas, nesse caso o áudio permanecerá mudo até que a chamada de volta seja recebida. E a interface do usuário precisa refletir o estado do alto-falante e do microfone. O(s) participante(s) remoto(s) na chamada verá que o usuário também silenciou o áudio. O usuário terá que desativar manualmente o som nesses casos.

Próximas etapas