Core-Telecom

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

Si vous souhaitez découvrir 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 appelante 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 compatibilité avec l'audio et la 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 avoir les caractéristiques suivantes:

  • displayName: nom de l'appelant.
  • address: adresse de l'appel (par exemple, numéro de téléphone, lien de la réunion).
  • direction: entrante ou sortante.
  • callType: audio ou vidéo.
  • callCapabilities: compatible avec 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 les rappels pour ajouter un nouvel appel au système et gérer les mises à jour de surface à distance. 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épondre à un appel entrant dans CallControlScope:

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

Refuser un appel

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

disconnect(DisconnectCause(DisconnectCause.REJECTED))

Activer un appel sortant

Définissez un appel sortant comme actif une fois que la personne à l'autre bout de la ligne a répondu:

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

Déconnecter un appel à l'aide de disconnect() avec un DisconnectCause:

disconnect(DisconnectCause(DisconnectCause.LOCAL))

Gérer les points de terminaison audio des appels

Observer et gérer les points de terminaison audio à l'aide des Flow currentCallEndpoint, availableEndpoints et isMuted dans le CallControlScope

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

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

coroutineScope.launch {
     callControlScope.requestEndpointChange(callEndpoint)
}

Compatibilité avec le premier plan

La bibliothèque utilise ConnectionService (Android 13, niveau d'API 33 ou version antérieure) ou foregroundtypes (Android 14, niveau d'API 34 ou version ultérieure) pour la compatibilité avec le premier plan.

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

Pour vous assurer que votre application bénéficie de la priorité d'exécution au premier plan, créez une notification une fois que vous avez ajouté l'appel avec la plate-forme. La priorité de 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 à distance pour Surface

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 les lambdas de rappel (onAnswerCall, onSetCallDisconnected, onSetCallActive et 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 de l'exécution du lambda indique que la commande a été traitée. Si la commande ne peut pas être exécutée, le lambda doit générer une exception.

Une implémentation correcte garantit un contrôle fluide des appels sur différents appareils. Testez minutieusement avec différentes surfaces de télécommande.

Extensions d'appel

En plus de gérer l'état de l'appel et le routage 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 des surfaces distantes, telles qu'Android Auto. Ces fonctionnalités incluent les salles de réunion, le 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 permettent également d'afficher ces extensions dans leur UI. Cela signifie que ces fonctionnalités seront également disponibles sur les appareils distants 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, vous pouvez utiliser CallManager#addCallWithExtensions, qui donne à l'application accès à un autre champ d'application 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 renvoie un CallControlScope à l'application une fois l'échange de fonctionnalités d'extension et l'initialisation 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...
        }
    }
}

Aider 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 distantes 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 de signaler aux surfaces distantes les participants à l'appel, le participant actif peut également être mis à jour à l'aide de ParticipantExtension#updateActiveParticipant.

Les actions facultatives liées aux participants à l'appel sont également prises en charge. L'application peut utiliser ParticipantExtension#addRaiseHandSupport pour prendre en charge la notion de participants qui lèvent la main lors de l'appel et voir quels autres participants ont également la main levée.

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

Prise en charge du mode silencieux des appels

Le silence d'appel permet à un utilisateur de demander à l'application de couper le son sortant d'un appel sans couper physiquement le son du 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 sourdine global des appels mobiles en cours lorsqu'un appel VoIP est actif. Cela rend le masquage du son sortant moins sujet aux erreurs dans les scénarios multi-appels, tout en permettant d'utiliser des fonctionnalités utiles telles que les indications "Parlez-vous ?" lorsque l'utilisateur parle sans se rendre compte que le masquage du son est activé.

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

Icônes d'appel d'assistance

Une icône d'appel permet à l'application de spécifier une icône personnalisée représentant l'appel à afficher sur des surfaces distantes pendant l'appel. Cette icône peut également être mise à jour au cours du cycle de vie 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)
        }
    }