La libreria Core-Telecom semplifica il processo di integrazione dell'applicazione di chiamata con la piattaforma Android fornendo un insieme di API solido e coerente.
Se vuoi esplorare implementazioni pratiche, puoi trovare applicazioni di esempio su GitHub:
- App di esempio leggera: un esempio minimo che mostra l'utilizzo dell'API
Core-Telecom. Ideale per comprendere rapidamente i concetti fondamentali. - Comprehensive Sample App (sviluppata dal team Core-Telecom): un'applicazione più ricca di funzionalità che mostra le funzionalità avanzate di Telecom e le best practice. Questa è un'ottima risorsa per comprendere scenari di integrazione complessi.
Configurare Core-Telecom
Aggiungi la dipendenza androidx.core:core-telecom al file build.gradle
della tua app:
dependencies {
implementation ("androidx.core:core-telecom:1.0.0")
}
Dichiara l'autorizzazione MANAGE_OWN_CALLS nel tuo AndroidManifest.xml:
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
Registrazione dell'our app
Registra la tua app di chiamate con Android utilizzando CallsManager per iniziare ad aggiungere
chiamate al sistema. Durante la registrazione, specifica le funzionalità dell'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 un ciclo di vita delle chiamate.
Creare una chiamata
L'oggetto CallAttributesCompat definisce le proprietà di una chiamata univoca,
che può avere le seguenti caratteristiche:
displayName: il nome del chiamante.address: indirizzo di chiamata (ad esempio, numero di telefono, link della riunione).direction: In entrata o in uscita.callType: audio o video.callCapabilities: supporta il trasferimento e la messa in attesa.
Ecco un esempio di come creare una chiamata in entrata:
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
)
}
Aggiungere una chiamata
Utilizza callsManager.addCall con CallAttributesCompat e i callback per aggiungere una
nuova chiamata al sistema e gestire gli aggiornamenti della superficie remota. callControlScope
all'interno del blocco addCall consente principalmente all'app di eseguire la transizione dello stato della chiamata
e 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
Rifiuta 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 una volta che il destinatario 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 */ }
}
Terminare 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 currentCallEndpoint,
availableEndpoints e isMuted Flow all'interno di 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 (livello API 33 e precedenti di Android 13) o
foregroundtypes (livello API 34 e successivi di Android 14) per il supporto
in primo piano.
Nell'ambito dei requisiti per il primo piano, l'applicazione deve pubblicare una notifica per informare gli utenti che l'applicazione è in esecuzione in primo piano.
Per assicurarti che la tua app riceva la priorità di esecuzione in primo piano, crea una notifica 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, cuffie Bluetooth, Android Auto) sono in grado di
gestire le chiamate senza interazione diretta con lo smartphone. La tua app deve implementare
le espressioni lambda di callback (onAnswerCall, onSetCallDisconnected, onSetCallActive,
onSetCallInactive) fornite a CallsManager.addCall per gestire le azioni
avviate da questi dispositivi.
Quando si verifica un'azione remota, viene richiamata la lambda corrispondente.
Il completamento riuscito della lambda indica che il comando è stato elaborato. Se il comando non può essere eseguito, la lambda deve generare un'eccezione.
L'implementazione corretta garantisce un controllo delle chiamate senza interruzioni su diversi dispositivi. Esegui test approfonditi con varie superfici del telecomando.
Estensioni di chiamata
Oltre a gestire lo stato della chiamata e il percorso audio delle chiamate, la libreria supporta anche le estensioni di chiamata, che sono funzionalità facoltative che la tua app può implementare per un'esperienza di chiamata più ricca su piattaforme remote, come Android Auto. Queste funzionalità includono sale riunioni, silenziamento delle chiamate e icone di chiamata aggiuntive. Quando la tua 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 potranno interagire.
Creare una chiamata con le 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 che
supporta. Inoltre, questo ambito fornisce un metodo aggiuntivo, onCall,
che fornisce un CallControlScope all'app dopo il completamento dello scambio e dell'inizializzazione delle funzionalità dell'estensione.
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...
}
}
}
Partecipanti alla chiamata di assistenza
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 superfici remote quando i partecipanti cambiano.
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 a comunicare alle piattaforme remote quali partecipanti sono presenti nella chiamata,
il partecipante attivo può essere aggiornato anche utilizzando
ParticipantExtension#updateActiveParticipant.
Sono supportate anche azioni facoltative relative ai partecipanti alla chiamata.
L'app può utilizzare ParticipantExtension#addRaiseHandSupport per supportare
l'idea che i partecipanti alzino la mano 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 { newParticipan>ts -
participantExtension.updateParticipants(newParticipants)
}.launchIn(this)
// notify remote surfaces of which of the participants have their
// hands raised
raisedHandsFlow.onEach { newRaisedHan>ds -
raiseHandState.updateRaisedHands(newRaisedHands)
}.launchIn(this)
}
}
Support Call Silence
La funzionalità Silenzio chiamate consente a un utente di richiedere all'app di disattivare l'audio in uscita di una chiamata senza disattivare fisicamente l'audio del microfono del dispositivo. Questa funzionalità viene gestita per chiamata, quindi Jetpack gestisce la complessità della gestione dello stato di disattivazione 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 e consente funzionalità utili come le indicazioni "stai parlando" quando l'utente sta parlando senza rendersi conto che la disattivazione dell'audio della chiamata è abilitata.
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
) { newCallSilenceStateReque>st -
// 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 delle chiamate di 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ò anche essere aggiornata 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 { newIconU>ri -
callIconExtension.updateCallIconUri(newIconUri)
}.launchIn(this)
}
}
Aggiungere al registro chiamate di sistema
Puoi aggiungere le chiamate VoIP della tua app al registro chiamate di sistema, in modo che vengano visualizzate nel dialer di sistema e gli utenti possano richiamare da lì. Per maggiori dettagli, vedi Cronologia chiamate unificata.