La libreria Core-Telecom
semplifica il processo di integrazione della tua applicazione di chiamata con la piattaforma Android fornendo un insieme solido e coerente di API
Se vuoi esplorare le implementazioni pratiche, puoi trovare applicazioni di esempio su GitHub:
- App di esempio leggera: un esempio minimo che dimostra l'utilizzo dell'API
Core-Telecom
. Ideale per comprendere rapidamente concetti fondamentali. - App di esempio completa (sviluppata dal team Core-Telecom): un'applicazione più completa che mostra le best practice e le funzionalità avanzate di Telecom. Si tratta di una risorsa eccellente per comprendere scenari di integrazione complessi.
Configura Core-Telecom
Aggiungi la dipendenza androidx.core:core-telecom
al file build.gradle
dell'app:
dependencies {
implementation ("androidx.core:core-telecom:1.0.0")
}
Dichiara l'autorizzazione MANAGE_OWN_CALLS
in AndroidManifest.xml
:
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
Registrazione dell'our app
Registra la tua app di chiamata con Android utilizzando CallsManager
per iniziare ad aggiungere chiamate al sistema. Al momento della registrazione, specifica le funzionalità della tua app (ad esempio, supporto audio e video):
val callsManager = CallsManager(context)
val capabilities: @CallsManager.Companion.Capability Int =
(CallsManager.CAPABILITY_BASELINE or
CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING)
callsManager.registerAppWithTelecom(capabilities)
Gestione chiamate
Utilizza le API Core-Telecom per creare e gestire il ciclo di vita di una chiamata.
Creare una chiamata
L'oggetto CallAttributesCompat
definisce le proprietà di una chiamata univoca,
che può avere le seguenti caratteristiche:
displayName
: nome del chiamante.address
: indirizzo di chiamata (ad esempio numero di telefono, link alla riunione).direction
: in entrata o in uscita.callType
: audio o video.callCapabilities
: supporta il trasferimento e la messa in attesa.
Di seguito è riportato un esempio di come creare una chiamata in arrivo:
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
)
}
Aggiungi una chiamata
Utilizza callsManager.addCall
con CallAttributesCompat
e i callback per aggiungere una nuova chiamata al sistema e gestire gli aggiornamenti delle piattaforme da remoto. Il codice callControlScope
all'interno del blocco addCall
consente principalmente all'app di gestire la transizione dello stato della chiamata e di ricevere aggiornamenti 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.
}
Rispondere a una chiamata
Rispondi a una chiamata in arrivo entro CallControlScope
:
when (val result = answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
is CallControlResult.Success -> { /* Call answered */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Rifiutare una chiamata
Rifiutare una chiamata utilizzando disconnect()
con DisconnectCause.REJECTED
all'interno di CallControlScope
:
disconnect(DisconnectCause(DisconnectCause.REJECTED))
Attivare una chiamata in uscita
Imposta una chiamata in uscita come attiva quando la parte remota risponde:
when (val result = setActive()) {
is CallControlResult.Success -> { /* Call active */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Mettere in attesa una chiamata
Usa setInactive()
per mettere una chiamata in attesa:
when (val result = setInactive()) {
is CallControlResult.Success -> { /* Call on hold */ }
is CallControlResult.Error -> { /* Handle error */ }
}
Disconnettere una chiamata
Per disconnettere una chiamata utilizzando disconnect()
con un DisconnectCause
:
disconnect(DisconnectCause(DisconnectCause.LOCAL))
Gestire gli endpoint audio delle chiamate
Osserva e gestisci gli endpoint audio utilizzando i Flow
currentCallEndpoint
,
availableEndpoints
e isMuted
all'interno del CallControlScope
fun observeAudioStateChanges(callControlScope: CallControlScope) {
with(callControlScope) {
launch { currentCallEndpoint.collect { /* Update UI */ } }
launch { availableEndpoints.collect { /* Update UI */ } }
launch { isMuted.collect { /* Handle mute state */ } }
}
}
Cambia il dispositivo audio attivo utilizzando requestEndpointChange()
:
coroutineScope.launch {
callControlScope.requestEndpointChange(callEndpoint)
}
Supporto in primo piano
La libreria utilizza ConnectionService
(Android 13, livello API 33 e versioni precedenti) o
foregroundtypes (Android 14, livello API 34 e versioni successive) per il supporto in primo piano.
Nell'ambito dei requisiti per l'esecuzione in primo piano, l'applicazione deve pubblicare una notifica per informare gli utenti che è in esecuzione in primo piano.
Per assicurarti che la tua app abbia la priorità di esecuzione in primo piano, crea una notification dopo aver aggiunto la chiamata con la piattaforma. La priorità in primo piano viene rimossa quando l'app termina la chiamata o la notifica non è più valida.
Scopri di più sui servizi in primo piano.
Assistenza remota per Surface
I dispositivi remoti (smartwatch, auricolari Bluetooth, Android Auto) sono in grado di gestire le chiamate senza interazione diretta con lo smartphone. L'app deve implementare callback lambda (onAnswerCall
, onSetCallDisconnected
, onSetCallActive
,
onSetCallInactive
) forniti a CallsManager.addCall
per gestire le azioni avviate da questi dispositivi.
Quando si verifica un'azione remota, viene invocato il lambda corrispondente.
Completamento riuscito degli indicatori lambda che indicano che il comando è stato elaborato. Se il comando non può essere eseguito, la funzione lambda deve generare un'eccezione.
L'implementazione corretta garantisce un controllo senza interruzioni delle chiamate su diversi dispositivi. Esegui test approfonditi con varie piattaforme remote.
Estensioni di chiamata
Oltre a gestire lo stato e il routing audio delle chiamate, la biblioteca supporta anche le estensioni di chiamata, ovvero funzionalità facoltative che la tua app può implementare per un'esperienza di chiamata più completa su piattaforme remote, come Android Auto. Queste funzionalità includono sale riunioni, silenziamento delle chiamate e altre icone di chiamata. Quando l'app implementa un'estensione, le informazioni fornite dall'app vengono sincronizzate con tutti i dispositivi connessi che supportano anche la visualizzazione di queste estensioni nella loro UI. Ciò significa che queste funzionalità saranno disponibili anche sui dispositivi remoti con cui gli utenti possono interagire.
Creare una chiamata con estensioni
Quando crei una chiamata, anziché utilizzare CallManager#addCall
, puoi utilizzare CallManager#addCallWithExtensions, che consente all'app di accedere a un ambito diverso chiamato ExtensionInitializationScope
. Questo ambito consente all'applicazione di inizializzare l'insieme di estensioni facoltative supportate. Inoltre, questo ambito fornisce un metodo aggiuntivo, onCall
,
che restituisce un CallControlScope
all'app al termine dello scambio delle funzionalità
dell'estensione e dell'inizializzazione.
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...
}
}
}
Supportare i partecipanti alla chiamata
Se la tua app supporta i partecipanti alle chiamate per riunioni o chiamate di gruppo, utilizza
addParticipantExtension
per dichiarare il supporto di questa estensione e
utilizza le API correlate per aggiornare le piattaforme remote quando cambiano i partecipanti.
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)
}
}
Oltre ad avvisare le piattaforme remote dei partecipanti alla chiamata,
anche il partecipante attivo può essere aggiornato utilizzando
ParticipantExtension#updateActiveParticipant
.
È inoltre supportato l'uso di azioni facoltative relative ai partecipanti alla chiamata.
L'app può utilizzare ParticipantExtension#addRaiseHandSupport
per supportare la funzionalità di alzata di mano dei partecipanti durante la chiamata e vedere quali altri partecipanti hanno alzato la mano.
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)
}
}
Supporta la funzionalità Silenzia chiamate
La disattivazione audio delle chiamate consente a un utente di richiedere all'app di disattivare l'audio in uscita di una chiamata senza disattivare fisicamente il microfono del dispositivo. Questa funzionalità viene gestita per chiamata, quindi Jetpack gestisce la complessità della gestione dello stato di disattivazione audio globale delle chiamate cellulari in corso mentre è attiva una chiamata VoIP. In questo modo, la disattivazione dell'audio in uscita è meno soggetta a errori in scenari con più chiamate, nonché consente di usufruire di funzionalità utili come le indicazioni "stai parlando?" quando l'utente parla senza rendersi conto che la disattivazione dell'audio in chiamata è attivata.
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)
}
}
Icone di chiamata all'assistenza
Un'icona di chiamata consente all'app di specificare un'icona personalizzata che rappresenta la chiamata da visualizzare sulle piattaforme remote durante la chiamata. Questa icona può essere aggiornata anche durante la chiamata.
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)
}
}