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 :
- Exemple d'application légère : exemple minimal illustrant l'utilisation de l'API
Core-Telecom. Idéal pour comprendre rapidement les concepts fondamentaux. - Exemple d'application complète (développée par l'équipe Core-Telecom) : application plus riche en fonctionnalités qui présente les fonctionnalités Telecom avancées et les bonnes pratiques. Il s'agit d'une excellente ressource pour comprendre les scénarios d'intégration complexes.
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 ou 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")
return CallAttributesCompat(
displayName = callerName,
address = addressUri,
direction = CallAttributesCompat.DIRECTION_INCOMING,
callType = if (isVideoCall) {
CallAttributesCompat.CALL_TYPE_VIDEO_CALL
} else {
CallAttributesCompat.CALL_TYPE_AUDIO_CALL
},
callCapabilities = CallAttributesCompat.SUPPORTS_SET_INACTIVE
)
}
Ajouter un appel
Utilisez callsManager.addCall avec CallAttributesCompat et des rappels pour ajouter un nouvel appel au système et gérer les mises à jour de la surface à distance. L'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 les CallControlScope :
when (val result = answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
is CallControlResult.Success -> { /* Call answered */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Refuser un appel
Rejetez un appel à l'aide de disconnect() avec DisconnectCause.REJECTED dans CallControlScope :
disconnect(DisconnectCause(DisconnectCause.REJECTED))
Activer un appel sortant
Définissez un appel sortant sur "actif" une fois que le correspondant distant 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 */ }
}
Déconnecter un appel
Pour raccrocher 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 CallControlScope. N'utilisez pas les API AudioManager#setCommunicationDevice ni AudioManager#startBluetoothSco pour gérer les itinéraires 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 le périphérique audio actif à l'aide de requestEndpointChange() :
coroutineScope.launch {
callControlScope.requestEndpointChange(callEndpoint)
}
Assistance de premier plan
La bibliothèque utilise ConnectionService sur Android 13 (niveau d'API 33) ou version antérieure, ou des types de services de premier plan sur Android 14 (niveau d'API 34) ou version ultérieure, pour la prise en charge du premier plan.
Pour que les appels restent actifs lorsque votre application est en arrière-plan, hébergez CallsManager dans un Service de premier plan (tel que LifecycleService) et déclarez le type de service de premier plan phoneCall dans votre AndroidManifest.xml :
<service
android:name=".TelecomVoipService"
android:foregroundServiceType="phoneCall" />
Conformément aux exigences concernant le premier plan, votre application doit publier une notification NotificationCompat.CallStyle pour informer les utilisateurs qu'un appel est actif au premier plan. Pour vous assurer que votre application bénéficie d'une priorité d'exécution au premier plan, promouvez votre service au premier plan à l'aide de startForeground une fois que vous avez ajouté l'appel avec la plate-forme :
startForeground(
notificationId,
notification,
ServiceInfo.FOREGROUND_SERVICE_TYPE_PHONE_CALL
)
En savoir plus sur les services de premier plan
Assistance Surface à distance
Les appareils à distance (smartwatches, 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 initiées par ces appareils.
Lorsqu'une action à distance se produit, le lambda correspondant est invoqué.
La réussite de la fonction 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 correcte assure un contrôle des appels fluide sur différents appareils. Testez minutieusement avec différentes surfaces de télécommande.
Extensions d'appel
En plus de gérer l'état des appels et le routage audio, 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 distantes, 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 UI. Cela signifie que ces fonctionnalités seront également disponibles sur les appareils à distance pour que les utilisateurs puissent interagir avec.
Créer un appel avec des extensions
Lorsque vous créez un appel, au lieu d'utiliser CallsManager.addCall, vous pouvez utiliser CallsManager.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 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...
}
}
}
Participants à l'appel d'assistance
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 compatibilité avec 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 d'informer les surfaces distantes des 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 levant la main pendant 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)
}
}
Couper le son d'un appel d'assistance
La mise en sourdine des appels permet à un utilisateur de demander à l'application de couper le son 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 désactivation du son global des appels mobiles en cours lorsqu'un appel VoIP est actif. Cela permet de réduire le risque d'erreur lors de la désactivation du son sortant dans les scénarios à appels multiples, tout en offrant des fonctionnalités utiles telles que les indications "Parlez-vous ?" lorsque l'utilisateur parle sans se rendre compte que le son est désactivé.
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 de l'assistance
Une icône d'appel permet à l'application de spécifier une icône personnalisée représentant l'appel à afficher sur les surfaces distantes pendant l'appel. Cette icône peut également être modifiée 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 des appels système
Vous pouvez ajouter les appels VoIP de votre application au journal d'appels du système pour qu'ils s'affichent dans le clavier du système et que les utilisateurs puissent rappeler à partir de là. Pour en savoir plus, consultez Historique des appels unifié.