Core-Telecom

La bibliothèque Core-Telecom simplifie le processus d'intégration de votre application d'appel à la plate-forme Android en fournissant un ensemble d'API robustes et cohérentes.

Si vous souhaitez explorer des implémentations pratiques, vous trouverez des exemples d'applications sur GitHub :

Configurer Core-Telecom

Ajoutez la dépendance androidx.core:core-telecom au fichier build.gradle de votre application :

dependencies {
    implementation ("androidx.core:core-telecom:1.0.0")
}

Déclarez l'autorisation MANAGE_OWN_CALLS dans votre fichier AndroidManifest.xml :

<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />

Enregistrer votre application

Enregistrez votre application d'appel auprès d'Android à l'aide de CallsManager pour commencer à ajouter des appels au système. Lors de l'enregistrement, spécifiez les fonctionnalités de votre application (par exemple, la prise en charge audio et vidéo) :

val callsManager = CallsManager(context)

val capabilities: @CallsManager.Companion.Capability Int =
    (CallsManager.CAPABILITY_BASELINE or
          CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING)

callsManager.registerAppWithTelecom(capabilities)

Gestion des appels

Utilisez les API Core-Telecom pour créer et gérer le cycle de vie d'un appel.

Créer un appel

L'objet CallAttributesCompat définit les propriétés d'un appel unique, qui peut présenter les caractéristiques suivantes :

  • displayName : nom de l'appelant.
  • address : adresse de l'appel (par exemple, numéro de téléphone, lien de réunion).
  • direction : entrant ou sortant.
  • callType : audio ou vidéo.
  • callCapabilities : prend en charge le transfert et la mise en attente.

Voici un exemple de création d'un appel entrant :

fun createIncomingCallAttributes(
    callerName: String,
    callerNumber: String,
    isVideoCall: Boolean): CallAttributesCompat {
    val addressUri = Uri.parse("YourAppScheme:$callerNumber")

    // Define capabilities supported by your call.
    val callCapabilities = CallAttributesCompat.CallCapability(
        supportsSetInactive = CallAttributesCompat.SUPPORTS_SET_INACTIVE // Call can be made inactive (implies hold)
    )

    return CallAttributesCompat(
        displayName = callerName,
        address = addressUri,
        direction = CallAttributesCompat.DIRECTION_INCOMING,
        callType = if (isVideoCall) CallAttributesCompat.CALL_TYPE_VIDEO_CALL else CallAttributesCompat.CALL_TYPE_AUDIO_CALL,
        callCapabilitiesCompat = callCapabilities
    )
}

Ajouter un appel

Utilisez callsManager.addCall avec CallAttributesCompat et des rappels pour ajouter un appel au système et gérer les mises à jour de la surface à distance. Le callControlScope dans le bloc addCall permet principalement à votre application de faire passer l'état de l'appel et de recevoir des mises à jour audio :

try {
    callsManager.addCall(
        INCOMING_CALL_ATTRIBUTES,
        onAnswerCall, // Watch needs to know if it can answer the call.
        onSetCallDisconnected,
        onSetCallActive,
        onSetCallInactive
    ) {
        // The call was successfully added once this scope runs.
        callControlScope = this
    }
}
catch(addCallException: Exception){
   // Handle the addCall failure.
}

Répondre à un appel

Répondez à un appel entrant dans le CallControlScope :

when (val result = answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
    is CallControlResult.Success -> { /* Call answered */ }
    is CallControlResult.Error -> { /* Handle error */ }
}

Refuser un appel

Refusez un appel à l'aide de disconnect() avec DisconnectCause.REJECTED dans le CallControlScope :

disconnect(DisconnectCause(DisconnectCause.REJECTED))

Activer un appel sortant

Définissez un appel sortant sur actif une fois que le correspondant répond :

when (val result = setActive()) {
    is CallControlResult.Success -> { /* Call active */ }
    is CallControlResult.Error -> { /* Handle error */ }
}

Mettre un appel en attente

Utilisez setInactive() pour mettre un appel en attente :

when (val result = setInactive()) {
    is CallControlResult.Success -> { /* Call on hold */ }
    is CallControlResult.Error -> { /* Handle error */ }
}

Mettre fin à un appel

Mettez fin à un appel à l'aide de disconnect() avec un DisconnectCause :

disconnect(DisconnectCause(DisconnectCause.LOCAL))

Gérer les points de terminaison audio des appels

Observez et gérez les points de terminaison audio à l'aide des Flow currentCallEndpoint, availableEndpoints et isMuted dans le CallControlScope. N'utilisez pas les API AudioManager#setCommunicationDevice ni AudioManager#startBluetoothSco pour gérer les routes audio lorsque vous utilisez Telecom. Cela entraînerait des problèmes audio lors de votre appel.

fun observeAudioStateChanges(callControlScope: CallControlScope) {
    with(callControlScope) {
        launch { currentCallEndpoint.collect { /* Update UI */ } }
        launch { availableEndpoints.collect { /* Update UI */ } }
        launch { isMuted.collect { /* Handle mute state */ } }
    }
}

Modifiez l'appareil audio actif à l'aide de requestEndpointChange() :

coroutineScope.launch {
     callControlScope.requestEndpointChange(callEndpoint)
}

Assistance au premier plan

La bibliothèque utilise ConnectionService (niveau d'API Android 13 et inférieur) ou foregroundtypes (niveau d'API Android 14 et supérieur) pour l'assistance au premier plan.

Conformément aux exigences de premier plan, l'application doit publier une notification pour informer les utilisateurs qu'elle s'exécute au premier plan.

Pour vous assurer que votre application bénéficie d'une priorité d'exécution au premier plan, créez une notification une fois que vous avez ajouté l'appel à la plate-forme. La priorité au premier plan est supprimée lorsque votre application met fin à l'appel ou que votre notification n'est plus valide.

En savoir plus sur les services de premier plan.

Assistance pour les surfaces à distance

Les appareils à distance (montres connectées, casques Bluetooth, Android Auto) peuvent gérer les appels sans interaction directe avec le téléphone. Votre application doit implémenter des lambdas de rappel (onAnswerCall, onSetCallDisconnected, onSetCallActive, onSetCallInactive) fournis à CallsManager.addCall pour gérer les actions lancées par ces appareils.

Lorsqu'une action à distance se produit, le lambda correspondant est appelé.

La réussite du lambda indique que la commande a été traitée. Si la commande ne peut pas être respectée, le lambda doit générer une exception.

Une implémentation appropriée garantit un contrôle d'appel fluide sur différents appareils. Testez minutieusement avec différentes surfaces à distance.

Extensions d'appel

En plus de gérer l'état de l'appel et la route audio de vos appels, la bibliothèque prend également en charge les extensions d'appel, qui sont des fonctionnalités facultatives que votre application peut implémenter pour une expérience d'appel plus riche sur les surfaces à distance, telles qu'Android Auto. Ces fonctionnalités incluent les salles de réunion, la mise en silence des appels et des icônes d'appel supplémentaires. Lorsque votre application implémente une extension, les informations qu'elle fournit sont synchronisées avec tous les appareils connectés qui prennent également en charge l'affichage de ces extensions dans leur interface utilisateur. Cela signifie que ces fonctionnalités seront également disponibles sur les appareils à distance pour que les utilisateurs puissent interagir avec elles.

Créer un appel avec des extensions

Lorsque vous créez un appel, au lieu d'utiliser CallManager#addCall pour créer l'appel, vous pouvez utiliser CallManager#addCallWithExtensions, qui donne à l' application accès à un champ d'application différent appelé ExtensionInitializationScope. Ce champ d'application permet à l'application d'initialiser l'ensemble des extensions facultatives qu'elle prend en charge. De plus, ce champ d'application fournit une méthode supplémentaire, onCall, qui fournit un CallControlScope à l'application une fois l'échange et l'initialisation des fonctionnalités d'extension terminés.

scope.launch {
    mCallsManager.addCallWithExtensions(
        attributes,
        onAnswer,
        onDisconnect,
        onSetActive,
        onSetInactive
    ) {
        // Initialize extension-specific code...

        // After the call has been initialized, perform in-call actions
        onCall {
            // Example: process call state updates
            callStateFlow.onEach { newState ->
                // handle call state updates and notify telecom
            }.launchIn(this)

            // Use initialized extensions...
        }
    }
}

Prendre en charge les participants à l'appel

Si votre application prend en charge les participants aux appels pour les réunions ou les appels de groupe, utilisez addParticipantExtension pour déclarer la prise en charge de cette extension et utilisez les API associées pour mettre à jour les surfaces à distance lorsque les participants changent.

mCallsManager.addCallWithExtensions(...) {
        // Initialize extensions...

        // Notifies Jetpack that this app supports the participant
        // extension and provides the initial participants state in the call.
        val participantExtension = addParticipantExtension(
            initialParticipants,
            initialActiveParticipant
        )

        // After the call has been initialized, perform in-call control actions
        onCall {
            // other in-call control and extension actions...

            // Example: update remote surfaces when the call participants change
            participantsFlow.onEach { newParticipants ->
                participantExtension.updateParticipants(newParticipants)
            }.launchIn(this)
        }
    }

En plus d'informer les surfaces à distance des participants à l'appel, le participant actif peut également être mis à jour à l'aide de ParticipantExtension#updateActiveParticipant.

La prise en charge des actions facultatives liées aux participants à l'appel est également disponible. L'application peut utiliser ParticipantExtension#addRaiseHandSupport pour prendre en charge la notion de participants levant la main lors de l'appel et voir quels autres participants ont également levé la main.

mCallsManager.addCallWithExtensions(...) {
        // Initialize extensions...

        // Notifies Jetpack that this app supports the participant
        // extension and provides the initial list of participants in the call.
        val participantExtension = addParticipantExtension(initialParticipants)
        // Notifies Jetpack that this app supports the notion of participants
        // being able to raise and lower their hands.
        val raiseHandState = participantExtension.addRaiseHandSupport(
                initialRaisedHands
            ) { onHandRaisedStateChanged ->
                // handle this user's raised hand state changed updates from
                // remote surfaces.
            }

        // After the call has been initialized, perform in-call control actions
        onCall {
            // other in-call control and extension actions...

            // Example: update remote surfaces when the call participants change
            participantsFlow.onEach { newParticipants ->
                participantExtension.updateParticipants(newParticipants)
            }.launchIn(this)
            // notify remote surfaces of which of the participants have their
            // hands raised
            raisedHandsFlow.onEach { newRaisedHands ->
                raiseHandState.updateRaisedHands(newRaisedHands)
            }.launchIn(this)
        }
    }

Prendre en charge la mise en silence des appels

La mise en silence des appels permet à un utilisateur de demander à l'application de mettre en silence l'audio sortant d'un appel sans couper physiquement le micro de l'appareil. Cette fonctionnalité est gérée par appel. Jetpack gère donc la complexité de la gestion de l'état de mise en silence global des appels cellulaires en cours lorsqu'un appel VOIP est actif. Cela réduit le risque d'erreur lors de la mise en silence de l'audio sortant dans les scénarios d'appels multiples, tout en permettant des fonctionnalités utiles telles que les indications "Parlez-vous ?" lorsque l'utilisateur parle sans se rendre compte que la mise en silence des appels est activée.

mCallsManager.addCallWithExtensions(...) {
        // Initialize extensions...

        // Add support for locally silencing the call's outgoing audio and
        // register a handler for when the user changes the call silence state
        // from a remote surface.
        val callSilenceExtension = addLocalCallSilenceExtension(
            initialCallSilenceState = false
        ) { newCallSilenceStateRequest ->
            // handle the user's request to enable/disable call silence from
            // a remote surface
        }

        // After the call has been initialized, perform in-call control actions
        onCall {
            // other in-call control and extension actions...

            // When the call's call silence state changes, update remote
            // surfaces of the new state.
            callSilenceState.onEach { isSilenced ->
                callSilenceExtension.updateIsLocallySilenced(isSilenced)
            }.launchIn(this)
        }
    }

Prendre en charge les icônes d'appel

Une icône d'appel permet à l'application de spécifier une icône personnalisée représentant l'appel à afficher sur les surfaces à distance pendant l'appel. Cette icône peut également être mise à jour pendant la durée de l'appel.

mCallsManager.addCallWithExtensions(...) {
        // Initialize extensions...

        // Add support for a custom call icon to be displayed during the
        // lifetime of the call.
        val callIconExtension = addCallIconExtension(
            initialCallIconUri = initialUri
        )

        // After the call has been initialized, perform in-call control actions
        onCall {
            // other in-call control and extension actions...

            // When the call's icon changes, update remote surfaces by providing
            // the new URI.
            callIconUri.onEach { newIconUri ->
                callIconExtension.updateCallIconUri(newIconUri)
            }.launchIn(this)
        }
    }

Ajouter au journal d'appels système

Vous pouvez ajouter les appels VoIP de votre application au journal d'appels système afin qu'ils s'affichent dans le composeur système et que les utilisateurs puissent rappeler à partir de là. Pour en savoir plus, consultez Historique des appels unifié.