Telecomunicazioni

La nuova libreria Android Telecom Jetpack consente di comunicare alla piattaforma stato in cui si trova la chiamata. Puoi trovare il codice sorgente e un'app di esempio su GitHub.

Dipendenze e autorizzazioni

Innanzitutto, apri il file build.gradle del modulo dell'app e aggiungi una dipendenza per il parametro Modulo telecomunicazioni androidx:

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

Nel file manifest dell'app, dichiara che l'app utilizza MANAGE_OWN_CALLS. autorizzazione:

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

Registra l'applicazione

Per comunicare ad Android la tua app, devi registrarla e le sue funzionalità. Questa funzionalità indica ad Android le funzionalità supportate dalla tua app, come videochiamate, chiamate streaming e chiamate in attesa. Queste informazioni sono importanti per consentire ad Android di configurare per interagire con le funzionalità dell'app.

 private val callsManager = CallsManager(context)

var capabilities: @CallsManager.Companion.Capability Int =
    CallsManager.CAPABILITY_BASELINE or
          CallsManager.CAPABILITY_SUPPORTS_CALL_STREAMING or
          CallsManager.CAPABILITY_SUPPORTS_VIDEO_CALLING

callsManager.registerAppWithTelecom(capabilities)

Integrazione della piattaforma

I due scenari di chiamata più comuni per qualsiasi applicazione di chiamata sono in entrata e chiamate in uscita. Per registrare correttamente l'indirizzo della chiamata e avvisa l'utente in modo appropriato con notifiche, utilizza le API indicate di seguito.

Registrare una chiamata

Questo esempio mostra come registrare una chiamata in arrivo:

companion object {
  const val APP_SCHEME = "MyCustomScheme"
  const val ALL_CALL_CAPABILITIES = (CallAttributes.SUPPORTS_SET_INACTIVE
    or CallAttributes.SUPPORTS_STREAM or CallAttributes.SUPPORTS_TRANSFER)

  const val INCOMING_NAME = "Luke"
  val INCOMING_URI: Uri = Uri.fromParts(APP_SCHEME, "", "")
  // Define all possible properties for CallAttributes
  val INCOMING_CALL_ATTRIBUTES =
    CallAttributes(
      INCOMING_NAME,
      INCOMING_URI,
      DIRECTION_INCOMING,
      CALL_TYPE_VIDEO_CALL,
      ALL_CALL_CAPABILITIES)
}

L'oggetto callAttributes può avere le seguenti proprietà:

  • displayName: nome del chiamante, riunione o sessione.
  • address: l'indirizzo della chiamata. Tieni presente che questa opzione può essere estesa a una riunione .
  • direction: la direzione della chiamata, ad esempio in arrivo o in uscita.
  • callType: informazioni relative ai dati trasmessi, ad esempio i video e audio.
  • callCapabilities: un oggetto che specifica le funzionalità della chiamata.

L'oggetto callCapabilities può avere le seguenti proprietà:

  • streaming: indica se la chiamata supporta lo streaming audio a un'altra persona Dispositivo con tecnologia Android.
  • transfer: indica se è possibile trasferire la chiamata.
  • hold: indica se la chiamata può essere messa in attesa.

Aggiungi una chiamata

Il metodo addCall() restituisce un'eccezione se il dispositivo non supporta telecom o se si è verificato un errore durante la configurazione della chiamata.

try {
    callsManager.addCall(
        INCOMING_CALL_ATTRIBUTES,
        onIsCallAnswered, // Watch needs to know if it can answer the call
        onIsCallDisconnected,
        onIsCallActive,
        onIsCallInactive
    ) {
        callControlScope = this
    }
}

Rispondere a una chiamata

Una volta effettuata una chiamata in arrivo, devi rispondere o rifiutare. Questo l'esame dimostra come rispondere a una chiamata:

when (answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
    is CallControlResult.Success -> {

    }

    is CallControlResult.Error -> {

    }
}

Se è in corso un'altra chiamata, answer() tornerà CallControlResult.Error, che indica il motivo per cui non è stato possibile rispondere alla chiamata. Nella in questo caso l'utente deve mettere in attesa l'altra chiamata.

Rifiutare una chiamata

Per rifiutare una chiamata, disconnettila con DisconnectCause.Rejected.

fun onRejectCall(){
    coroutineScope.launch {
        callControlScope?.let {
            it.disconnect(DisconnectCause(DisconnectCause.REJECTED))
        }
    }
}

Chiamata in uscita

Quando si effettua una chiamata in uscita, dopo che l'interlocutore remoto risponde, è necessario impostare il chiamata a active per comunicare alla piattaforma che la chiamata è in corso:

when (setActive()) {
    is CallControlResult.Success -> {
        onIsCallActive()
    }

    is CallControlResult.Error -> {
        updateCurrentCall {
            copy(errorCode = result.errorCode)
        }
    }
}

Mettere in attesa una chiamata

Se la tua app per le chiamate supporta le chiamate in attesa, usa setInActive per indicare al piattaforma su cui la chiamata non è attiva e che microfono e videocamera sono liberi può essere usato da altre app:

when (setInActive()) {
    is CallControlResult.Success -> {

    }

    is CallControlResult.Error -> {
        updateCurrentCall {
            copy(errorCode = result.errorCode)
        }
    }
}

Disconnetterti

Per disconnettere una chiamata, chiedi allo stack di Telecomunicazioni di disconnettersi fornendo un causa valida:

coroutineScope.launch {
    callControlScope?.disconnect(DisconnectCause(DisconnectCause.LOCAL))
}

Indirizza l'audio

Durante una chiamata, a volte gli utenti passano da un dispositivo all'altro, ad esempio uno speaker, auricolare o dispositivo Bluetooth. Utilizza availableEndpoints e currentCallEndpoint API per ottenere un elenco di tutti i dispositivi disponibili l'utente e il dispositivo attivo.

Questo esempio combina entrambi i flussi per creare un oggetto UI da mostrare all'utente elenco di dispositivi e quale è attivo:

availableEndpoint = combine(callControlScope.availableEndpoints,
    callControlScope.currentCallEndpoint) {
    availableDevices: List<CallEndpoint>, activeDevice : CallEndpoint ->
    availableDevices.map {
        EndPointUI(
            isActive = activeDevice.endpointName == it.endpointName, it
        )
    }
}

Per cambiare un dispositivo attivo, usa requestEndpointChange con CallEndpoint a cui vuoi passare.

coroutineScope.launch {
     callControlScope?.requestEndpointChange(callEndpoint)
}

Supporto in primo piano

La libreria Telecom include il supporto in primo piano. Questa libreria utilizza ConnectionService per i dispositivi con Android 13 e versioni precedenti. Per Android 14 e più in alto utilizza il microfono e la fotocamera in primo piano per supportare i servizi in primo piano. Scopri di più sui servizi in primo piano.

Nell'ambito dei requisiti in primo piano, l'applicazione deve pubblicare una notifica affinché gli utenti sappiano che l'applicazione è in esecuzione in primo piano.

Per assicurarti che l'esecuzione in primo piano della tua app sia prioritaria, crea una una volta registrata la chiamata sulla piattaforma. Priorità in primo piano viene rimosso quando la tua app termina la chiamata o quando la notifica non è più valida.

is TelecomCall.Registered -> {
    val notification = createNotification(call)
    notificationManager.notify(TELECOM_NOTIFICATION_ID, notification)
}

Supporto di Surface

Gli smartwatch hanno un'applicazione generica di ricezione degli endpoint. Questa applicazione fornisce l'utente con un'interfaccia di base come rispondere, rifiutare e disconnettersi chiamate. L'applicazione supporta queste azioni implementando funzioni lambda che indicano alla piattaforma che hai eseguito l'azione sul dispositivo.

Ogni funzione lambda scade dopo 5 secondi con una transazione non riuscita se le tue l'applicazione non risponde.

callsManager.addCall(
        attributes,
        onIsCallAnswered, // Watch/Auto need to know if they can answer the call
        onIsCallDisconnected,
        onIsCallActive,
        onIsCallInactive
    ) {
//Call Scope
}
/**
  *  Can the call be successfully answered??
  *  TIP: Check the connection/call state to see if you can answer a call
  *  Example you may need to wait for another call to hold.
  **/
val onIsCallAnswered: suspend(type: Int) -> Unit = {}

/**
  * Can the call perform a disconnect
  */
val onIsCallDisconnected: suspend (cause: DisconnectCause) -> Unit = {}

/**
  *  Check is see if you can make the call active.
  *  Other calls and state might stop us from activating the call
  */
val onIsCallActive: suspend () -> Unit = {
    updateCurrentCall {
    }
}

/**
  * Check to see if you can make the call inactivate
  */
val onIsCallInactive: suspend () -> Unit = {}