A nova biblioteca Android Telecom Jetpack facilita dizer à plataforma o que em que a chamada está. Você pode encontrar o código-fonte e um app de exemplo em GitHub.
Dependências e permissões
Primeiro, abra o arquivo build.gradle do módulo do app e adicione uma dependência para o Módulo de telecomunicações do androidx:
dependencies {
implementation ("androidx.core:core-telecom:1.0.0-alpha02")
}
No manifesto do app, declare que ele usa o MANAGE_OWN_CALLS
.
permissão:
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
Registrar o aplicativo
Para informar ao Android sobre seu app, é necessário registrá-lo e os recursos dele. Informa ao Android quais recursos seu app oferece suporte, como videochamadas, chamadas fazer streaming e reter ligações. Estas informações são importantes para que o Android possa configurar para funcionar com os recursos do seu 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)
Integração com a plataforma
Os dois cenários de chamada mais comuns para qualquer aplicativo de chamada são e chamadas realizadas. Para registrar corretamente a direção da chamada e notificar adequadamente o usuário com notificações, usar as APIs abaixo.
Registrar uma ligação
Este exemplo demonstra como registrar uma ligação recebida:
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)
}
O objeto callAttributes
pode ter as seguintes propriedades:
displayName
: o nome do autor da chamada, reunião ou sessão.address
: o endereço da chamada. Observação: esse recurso pode ser estendido a uma reunião .direction
: a direção da chamada, como recebida ou realizada.callType
: informações relacionadas aos dados sendo transmitidos, como vídeo e áudio.callCapabilities
: um objeto que especifica os recursos da chamada.
O objeto callCapabilities
pode ter as seguintes propriedades:
streaming
: indica se a chamada oferece suporte ao streaming de áudio para outra Dispositivo Android.transfer
: indica se a chamada pode ser transferida.hold
: indica se a chamada pode ser colocada em espera.
Adicionar uma ligação
O método addCall()
retornará uma exceção se o dispositivo não oferecer suporte
telecomunicações ou se ocorreu um erro ao configurar a chamada.
try {
callsManager.addCall(
INCOMING_CALL_ATTRIBUTES,
onIsCallAnswered, // Watch needs to know if it can answer the call
onIsCallDisconnected,
onIsCallActive,
onIsCallInactive
) {
callControlScope = this
}
}
Atender uma chamada
Ao receber uma chamada, você precisa atender ou rejeitá-la. Isso exemplo demonstra como atender a uma ligação:
when (answer(CallAttributesCompat.CALL_TYPE_AUDIO_CALL)) {
is CallControlResult.Success -> {
}
is CallControlResult.Error -> {
}
}
Se outra chamada estiver em andamento, answer()
vai retornar
CallControlResult.Error
, que informa por que não foi possível atender a chamada. Em
Nesse caso, o usuário precisa colocar a outra chamada em espera.
Rejeitar uma chamada
Para rejeitar uma chamada, desconecte-a do DisconnectCause.Rejected
.
fun onRejectCall(){
coroutineScope.launch {
callControlScope?.let {
it.disconnect(DisconnectCause(DisconnectCause.REJECTED))
}
}
}
Chamada realizada
Ao realizar uma chamada, depois que o participante remoto atender, você deverá definir o chame active para informar à plataforma que a chamada está em andamento:
when (setActive()) {
is CallControlResult.Success -> {
onIsCallActive()
}
is CallControlResult.Error -> {
updateCurrentCall {
copy(errorCode = result.errorCode)
}
}
}
Colocar uma chamada em espera
Se seu app de chamadas oferecer suporte à retenção de chamadas, use setInActive
para dizer ao
plataforma em que sua chamada não está ativa e que o microfone e a câmera são livres para
ser usados por outros apps:
when (setInActive()) {
is CallControlResult.Success -> {
}
is CallControlResult.Error -> {
updateCurrentCall {
copy(errorCode = result.errorCode)
}
}
}
Desconectar
Para desconectar uma chamada, informe a pilha de telecomunicações para desconectar, fornecendo um causa válida:
coroutineScope.launch {
callControlScope?.disconnect(DisconnectCause(DisconnectCause.LOCAL))
}
Rotear áudio
Durante uma chamada, os usuários às vezes alternam entre dispositivos, como alto-falantes,
minifone de ouvido ou dispositivo Bluetooth. Use os métodos availableEndpoints
e
currentCallEndpoint
APIs para receber uma lista de todos os dispositivos disponíveis para o
usuário e qual dispositivo está ativo.
Este exemplo combina os dois fluxos para criar um objeto de IU que mostra ao usuário uma lista de dispositivos e qual deles está ativo:
availableEndpoint = combine(callControlScope.availableEndpoints,
callControlScope.currentCallEndpoint) {
availableDevices: List<CallEndpoint>, activeDevice : CallEndpoint ->
availableDevices.map {
EndPointUI(
isActive = activeDevice.endpointName == it.endpointName, it
)
}
}
Para mudar um dispositivo ativo, use o requestEndpointChange
com o
CallEndpoint
que você quer usar.
coroutineScope.launch {
callControlScope?.requestEndpointChange(callEndpoint)
}
Suporte em primeiro plano
A biblioteca Telecom é compatível com primeiro plano. Esta biblioteca usa
ConnectionService
para dispositivos com o Android 13 e versões anteriores. Para o Android 14 e
maior, ele usará o microfone e a câmera primeiros tipos para que
oferecer suporte a serviços em primeiro plano. Saiba mais sobre os serviços em primeiro plano.
Como parte dos requisitos de primeiro plano, o aplicativo precisa postar uma notificação para que os usuários saibam que o aplicativo está sendo executado em primeiro plano.
Para garantir que o app receba a prioridade de execução em primeiro plano, crie uma assim que registrar a chamada com a plataforma. Prioridade em primeiro plano é removido quando o app encerra a chamada ou quando a notificação não é mais válidos.
is TelecomCall.Registered -> {
val notification = createNotification(call)
notificationManager.notify(TELECOM_NOTIFICATION_ID, notification)
}
Suporte da plataforma
Os relógios têm um aplicativo receptor de endpoint genérico. Esse aplicativo oferece o usuário com uma interface básica, como atender, rejeitar e desconectar chamadas. O aplicativo oferece suporte a essas ações implementando funções lambda. que informam à plataforma que você realizou a ação no dispositivo.
Cada função lambda expira após cinco segundos com uma transação com falha aplicativo não responde.
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 = {}