Udostępnij przez


Integracja z zestawem CallKit

W tym dokumencie omówimy sposób integracji biblioteki CallKit z aplikacją systemu iOS.

Wymagania wstępne

Integracja CallKit (w SDK)

Integracja zestawu CallKit w zestawie SDK usług Azure Communication Services dla systemu iOS obsługuje interakcję z zestawem CallKit dla nas. Aby wykonać wszystkie operacje wywołania, takie jak wyciszenie/odwyciszenie, zawieszenie/wznawianie, wystarczy wywołać interfejs API w zestawie SDK usług Azure Communication Services.

Inicjowanie agenta połączeń za pomocą opcji CallKitOptions

Przy użyciu skonfigurowanej instancji CallKitOptions możemy utworzyć element CallAgent z obsługą elementu CallKit.

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
})

Określanie informacji adresata połączenia dla połączeń wychodzących

Najpierw musimy utworzyć wystąpienie StartCallOptions() dla połączeń wychodzących lub JoinCallOptions() dla połączeń grupowych.

let options = StartCallOptions()

lub

let options = JoinCallOptions()

Następnie utwórz wystąpienie klasy CallKitRemoteInfo

options.callKitRemoteInfo = CallKitRemoteInfo()
  1. Przypisz wartość callKitRemoteInfo.displayNameForCallKit, aby dostosować nazwę wyświetlaną odbiorcom połączeń i skonfigurować wartość CXHandle. Ta wartość określona w parametrze displayNameForCallKit jest dokładnie taka, jak jest wyświetlana w ostatnim dzienniku połączeń telefonicznych. w ostatnim dzienniku połączeń telefonicznych.
options.callKitRemoteInfo.displayNameForCallKit = "DISPLAY_NAME"
  1. Przypisz wartość cxHandle, którą aplikacja otrzymuje, gdy użytkownik oddzwania do tego kontaktu.
options.callKitRemoteInfo.cxHandle = CXHandle(type: .generic, value: "VALUE_TO_CXHANDLE")

Określanie informacji adresata połączenia dla połączeń przychodzących

Najpierw musimy utworzyć wystąpienie klasy CallKitOptions:

let callKitOptions = CallKitOptions(with: createProviderConfig())

Skonfiguruj właściwości CallKitOptions wystąpienia:

Blok przekazywany do zmiennej provideRemoteInfo zostanie wywołany przez SDK, gdy odbierzemy połączenie przychodzące i musimy uzyskać nazwę wyświetlaną dla rozmówcy, którą przekażemy do 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
}

Konfigurowanie sesji audio

Konfiguracja sesji audio zostanie wywołana przed umieszczeniem lub zaakceptowaniem połączenia przychodzącego i przed wznowieniem połączenia po jego wstrzymaniu.

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
}

UWAGA: W przypadkach, gdy firma Contoso skonfigurowała już sesje audio, nie udostępniaj nil, ale zwraca błąd nil w bloku.

callKitOptions.configureAudioSession = self.configureAudioSession

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

jeśli podano nil dla configureAudioSession, to SDK wywołuje domyślną implementację w SDK.

Obsługa przychodzącego ładunku powiadomień push

Gdy aplikacja odbiera dane powiadomień push, musimy wywołać handlePush, aby je przetworzyć. Wywołanie zestawu SDK usług Azure Communication Services spowoduje wywołanie IncomingCall zdarzenia.

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, didReceiveIncomingCall incomingcall: IncomingCall) {
}

Możemy użyć reportIncomingCall do obsługi powiadomień push, gdy aplikacja jest zamknięta lub w innej sytuacji.

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)
      }
  }
}

Integracja z zestawem CallKit (w aplikacji)

Jeśli chcesz zintegrować zestaw CallKit w aplikacji i nie używać implementacji CallKit w zestawie SDK, zapoznaj się z przykładowym przewodnikiem Szybki start tutaj. Jedną z ważnych rzeczy, o które należy zadbać, jest rozpoczęcie dźwięku w odpowiednim czasie. Podobnie jak poniżej

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)

Wyciszenie głośnika i mikrofonu gwarantuje, że fizyczne urządzenia audio nie będą używane, aż CallKit wywoła didActivateAudioSession w CXProviderDelegate. W przeciwnym razie połączenie może zostać porzucone lub dźwięk nie będzie działać. Kiedy didActivateAudioSession należy uruchomić strumienie audio.

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()
}
    

Ważne jest również wyciszenie wychodzącego dźwięku przed zatrzymaniem dźwięku w przypadkach, gdy zestaw CallKit nie wywołuje elementu didActivateAudioSession. Następnie użytkownik może ręcznie cofnąć wyciszenie mikrofonu.

Uwaga / Notatka

W niektórych przypadkach CallKit nie wywołuje didActivateAudioSession, mimo że aplikacja ma podwyższone uprawnienia do dźwięku, w takim przypadku dźwięk pozostanie wyciszony do czasu otrzymania wywołania zwrotnego. Interfejs użytkownika musi odzwierciedlać stan głośnika i mikrofonu. Zdalni uczestnicy w trakcie rozmowy zobaczą, że użytkownik również wyciszył dźwięk. W takich przypadkach użytkownik będzie musiał ręcznie cofnąć wyciszenie.

Dalsze kroki