Una app de llamadas permite a los usuarios usar su dispositivo para recibir o realizar llamadas de audio o videollamadas. Estas apps usan su propia interfaz de usuario para las llamadas en lugar de usar la interfaz predeterminada de la app de teléfono, como se muestra en la siguiente captura de pantalla.
El framework de Android incluye el paquete android.telecom
, que contiene clases que te ayudan a crear una app de llamadas según el framework de las telecomunicaciones. Si creas tu app de acuerdo con el framework de telecomunicaciones, obtendrás los siguientes beneficios:
- Tu app interoperará correctamente con el subsistema nativo de telecomunicaciones del dispositivo.
- Tu app interoperará correctamente con otras apps de llamadas que también cumplan con las disposiciones del framework.
- El framework ayuda a tu app a administrar el enrutamiento de audio y video.
- El framework ayuda a tu app a determinar si sus llamadas tienen foco.
Permisos y declaraciones de manifiesto
En el manifiesto de tu app, declara que tu app usa
MANAGE_OWN_CALLS
permiso, como se muestra en el siguiente ejemplo:
<manifest … >
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS"/>
</manifest>
Puedes obtener más información para declarar permisos de apps en la sección Permisos.
Debes declarar un servicio que especifique la clase que implementa la clase ConnectionService
en tu app. El subsistema de telecomunicaciones requiere que el servicio declare el permiso BIND_TELECOM_CONNECTION_SERVICE
para poder vincularse a él. En el siguiente ejemplo, se muestra cómo declarar el servicio en el manifiesto de la app:
<service android:name="com.example.MyConnectionService"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>
Puedes obtener más información para declarar los componentes de la app, incluidos los servicios, en la sección Componentes de la app.
Cómo implementar el servicio de conexión
Tu app de llamadas debe proporcionar una implementación de la clase ConnectionService
a la que se pueda vincular el subsistema de telecomunicaciones.
Tu implementación de ConnectionService
debe anular los siguientes métodos:
onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)
El subsistema de telecomunicaciones llama a este método en respuesta a la llamada que hace tu app a
placeCall(Uri, Bundle)
para crear una nueva llamada saliente. Tu app muestra una instancia nueva de la implementación de la claseConnection
(para obtener más información, consulta la sección Cómo implementar la conexión) para representar la nueva llamada saliente. Puedes personalizar aún más la conexión saliente si realizas las siguientes acciones:- Tu app debe llamar al método
setConnectionProperties(int)
con la constantePROPERTY_SELF_MANAGED
como argumento para indicar que la conexión se originó desde una app de llamadas. - Si tu app admite las llamadas en espera, llama al método
setConnectionCapabilities(int)
y establece el argumento en el valor de la máscara binaria de las constantesCAPABILITY_HOLD
yCAPABILITY_SUPPORT_HOLD
. - Para establecer el nombre del emisor, usa el método
setCallerDisplayName(String, int)
pasando la constantePRESENTATION_ALLOWED
como el parámetroint
a fin de indicar que se debe mostrar el nombre del emisor. - Para asegurarte de que la llamada saliente tenga el estado de video apropiado, llama al método
setVideoState(int)
del objetoConnection
y envía el valor que muestra el métodogetVideoState()
del objetoConnectionRequest
.
- Tu app debe llamar al método
onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)
El subsistema de telecomunicaciones llama a este método cuando tu app llama al método
placeCall(Uri, Bundle)
y no se puede realizar la llamada saliente. En respuesta a esta situación, tu app debe informar al usuario (por ejemplo, mediante un cuadro de alerta o un aviso) que no se pudo realizar la llamada saliente. Es posible que tu app no pueda realizar llamadas si, antes de hacerlo, hay una llamada de emergencia o algún otro tipo de llamada en curso que no se puede poner en espera en otra app.onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)
El subsistema de telecomunicaciones llama a este método cuando tu app llama al método
addNewIncomingCall(PhoneAccountHandle, Bundle)
para informar al sistema sobre una nueva llamada entrante en la app. Esta muestra una nueva instancia de tu implementación deConnection
(para obtener más información, consulta la sección Cómo implementar la conexión) para representar la nueva llamada entrante. Puedes personalizar aún más la conexión entrante si realizas las siguientes acciones:- Tu app debe llamar al método
setConnectionProperties(int)
con la constantePROPERTY_SELF_MANAGED
como argumento para indicar que la conexión se originó desde una app de llamadas. - Si tu app admite las llamadas en espera, llama al método
setConnectionCapabilities(int)
y establece el argumento en el valor de la máscara binaria de las constantesCAPABILITY_HOLD
yCAPABILITY_SUPPORT_HOLD
. - Para establecer el nombre del emisor, usa el método
setCallerDisplayName(String, int)
pasando la constantePRESENTATION_ALLOWED
como el parámetroint
a fin de indicar que se debe mostrar el nombre del emisor. - Para especificar el número de teléfono o la dirección de la llamada entrante, usa el método
setAddress(Uri, int)
del objetoConnection
. - Para asegurarte de que la llamada saliente tenga el estado de video apropiado, llama al método
setVideoState(int)
del objetoConnection
y envía el valor que muestra el métodogetVideoState()
del objetoConnectionRequest
.
- Tu app debe llamar al método
onCreateIncomingConnectionFailed(PhoneAccountHandle, ConnectionRequest)
El subsistema de telecomunicaciones llama a este método cuando tu app llama al método
addNewIncomingCall(PhoneAccountHandle, Bundle)
para informar al sistema de telecomunicaciones sobre una nueva llamada entrante, pero esta no está permitida (para obtener más información, consulta la sección Restricciones de llamadas). Tu app debe rechazar de manera silenciosa la llamada entrante y, opcionalmente, publicar una notificación para informar al usuario sobre la llamada perdida.
Cómo implementar la conexión
Tu app debe crear una subclase de Connection
para representar las llamadas en ella. Debes anular los siguientes métodos en tu implementación:
onShowIncomingCallUi()
El subsistema de telecomunicaciones llama a este método cuando agregas una nueva llamada entrante tu app debería mostrar su IU de llamada entrante.
onCallAudioStateChanged(CallAudioState)
El subsistema de telecomunicaciones llama a este método para informar a tu app que la ruta o el modo de audio actual ha cambiado. Se lo llama en respuesta al cambio que hizo tu app en el modo audio con el método
setAudioRoute(int)
. También se puede llamar a este método si el sistema cambia la ruta del audio (por ejemplo, cuando se desconectan los auriculares Bluetooth).onHold()
El subsistema de telecomunicaciones llama a este método cuando quiere poner una llamada en espera. En respuesta a esta solicitud, tu app debe retener la llamada y, luego, invocar el método
setOnHold()
para informar al sistema que la llamada está en espera. El subsistema de telecomunicaciones puede llamar a este método cuando un servicio en llamada, como Android Auto, muestra que tu llamada desea retransmitir la solicitud de un usuario para poner la llamada en espera. El subsistema de telecomunicaciones también llama a este método si el usuario hace que una llamada esté activa en otra app. Para obtener más información sobre los servicios en llamada, consulta la información sobreInCallService
.onUnhold()
El subsistema de telecomunicaciones llama a este método cuando desea reanudar una llamada que se puso en espera. Una vez que tu app ha reanudado la llamada, debe invocar el método
setActive()
para informar al sistema que la llamada ya no está en espera. El subsistema de telecomunicaciones puede llamar a este método cuando un servicio en llamada, como Android Auto, muestra que tu llamada desea retransmitir una solicitud para reanudar la llamada. Para obtener más información sobre los servicios en llamada, consulta la información sobreInCallService
.onAnswer()
El subsistema de telecomunicaciones llama a este método para informarle a tu app que se debe contestar una llamada entrante. Una vez que la app respondió la llamada, debe invocar el método
setActive()
para informar al sistema que se contestó la llamada. El subsistema de telecomunicaciones puede llamar a este método cuando tu app agrega una nueva llamada entrante y ya hay una llamada saliente en curso en otra app que no se puede poner en espera. En estos casos, el subsistema de telecomunicaciones muestra la IU de la llamada entrante en nombre de tu app. El framework proporciona un método con sobrecarga que brinda compatibilidad para especificar el estado de video en el cual se contesta la llamada. Para obtener más información, consulta la información sobreonAnswer(int)
.onReject()
El subsistema de telecomunicaciones llama a este método cuando quiere rechazar una llamada entrante. Una vez que la app ha rechazado la llamada, debe llamar a
setDisconnected(DisconnectCause)
y especificarREJECTED
como el parámetro. Luego, tu app debe llamar al métododestroy()
para informar al sistema que la app procesó la llamada. El subsistema de telecomunicaciones llama a este método cuando el usuario rechaza una llamada entrante de tu app.onDisconnect()
El subsistema de telecomunicaciones llama a este método cuando quiere desconectar una llamada. Una vez que la llamada ha finalizado, la app debe llamar al método
setDisconnected(DisconnectCause)
y especificarLOCAL
como parámetro para indicar que se solicitud de usuario hizo que la llamada se desconectara. Entonces, tu app debe llamar al métododestroy()
para informar al subsistema de telecomunicaciones que la app procesó la llamada. El sistema puede llamar a este método después de que el usuario interrumpe una llamada a través de otro servicio en llamada, como Android Auto. El sistema también llama a este método cuando tu llamada debe interrumpirse para permitir que se realice otra llamada; por ejemplo, si el usuario desea realizar una llamada de emergencia. Para obtener más información sobre los servicios en llamada, consulta la información sobreInCallService
.
Cómo controlar situaciones de llamadas comunes
Utilizar la API de ConnectionService
en tu flujo de llamadas implica interactuar con las otras clases del paquete android.telecom
. En las siguientes secciones, se describen situaciones de llamadas comunes y cómo tu app debe controlarlas usando las API.
Cómo contestar las llamadas entrantes
El flujo para controlar las llamadas entrantes cambia si hay llamadas en otras apps. El motivo de la diferencia en los flujos es que el framework de las telecomunicaciones debe establecer algunas restricciones cuando hay llamadas activas en otras apps a fin de garantizar un entorno estable para todas las apps de llamadas en el dispositivo. Para obtener más información, consulta la sección Restricciones de llamadas.
No hay llamadas activas en otras apps
Para contestar llamadas entrantes cuando no hay llamadas activas en otras apps, sigue estos pasos:
- Tu app recibe una nueva llamada entrante utilizando sus mecanismos habituales.
- Usa el método
addNewIncomingCall(PhoneAccountHandle, Bundle)
para informar al subsistema de telecomunicaciones sobre la nueva llamada entrante. - El subsistema de telecomunicaciones se vincula a la implementación de
ConnectionService
de tu app y solicita una nueva instancia de la claseConnection
, que representa la nueva llamada entrante a través del métodoonCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)
. - El subsistema de telecomunicaciones informa a tu app que debe mostrar su interfaz de usuario de llamada entrante con el método
onShowIncomingCallUi()
. - Tu app muestra su IU de llamada entrante mediante una notificación con un intent asociado en pantalla completa. Para obtener más información, consulta la información sobre
onShowIncomingCallUi()
. - Llama al método
setActive()
si el usuario acepta la llamada entrante, o llama asetDisconnected(DisconnectCause)
y especificaREJECTED
como el parámetro seguido de una llamada al métododestroy()
si el usuario rechaza la llamada entrante.
Llamadas activas en otras apps que no se pueden poner en espera
Para contestar llamadas entrantes cuando hay llamadas activas en otras apps que no se pueden poner en espera, sigue estos pasos:
- Tu app recibe una nueva llamada entrante utilizando sus mecanismos habituales.
- Usa el método
addNewIncomingCall(PhoneAccountHandle, Bundle)
para informar al subsistema de telecomunicaciones sobre la nueva llamada entrante. - El subsistema de telecomunicaciones se vincula a la implementación de
ConnectionService
de tu app y solicita una nueva instancia del objetoConnection
, que representa la nueva llamada entrante a través del métodoonCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)
. - El subsistema de telecomunicaciones muestra la IU correspondiente para tu llamada entrante.
- Si el usuario acepta la llamada, el subsistema de telecomunicaciones llama al método
onAnswer()
. Debes llamar al métodosetActive()
para indicar al subsistema de telecomunicaciones que la llamada se estableció. - Si el usuario rechaza la llamada, el subsistema de telecomunicaciones llama al método
onReject()
. Debes llamar al métodosetDisconnected(DisconnectCause)
y definirREJECTED
como el parámetro seguido de una llamada al métododestroy()
.
Cómo realizar llamadas salientes
En el flujo para realizar una llamada saliente, existe la posibilidad de que no se pueda realizar debido a restricciones que impone el framework de las telecomunicaciones. Para obtener más información, consulta la sección Restricciones de llamadas.
Para realizar una llamada saliente, sigue estos pasos:
- El usuario inicia una llamada saliente dentro de tu app.
- Usa el método
placeCall(Uri, Bundle)
para informar al subsistema de telecomunicaciones sobre la nueva llamada saliente. Ten en cuenta las siguientes consideraciones para los parámetros del método:- El parámetro
Uri
representa la dirección a la que se realiza la llamada. Para números de teléfono normales, usa el esquema de URItel:
. - El parámetro
Bundle
te permite brindar información sobre tu app de llamadas si agregas el objetoPhoneAccountHandle
de la app alEXTRA_PHONE_ACCOUNT_HANDLE
adicional. Tu app debe proporcionar el objetoPhoneAccountHandle
a cada llamada saliente. - El parámetro
Bundle
también te permite especificar si la llamada saliente incluye video; establece el valorSTATE_BIDIRECTIONAL
en elEXTRA_START_CALL_WITH_VIDEO_STATE
adicional. Ten en cuenta que, de forma predeterminada, el subsistema de telecomunicaciones enruta las videollamadas al altavoz.
- El parámetro
- El subsistema de telecomunicaciones se vincula con la implementación de
ConnectionService
de tu app. - Si esta no puede realizar una llamada saliente, el subsistema de telecomunicaciones llama al método
onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)
para informar a la app que no es posible hacer la llamada en ese momento. Tu app debe informar al usuario que no se puede realizar la llamada. - Si tu app puede realizar la llamada saliente, el subsistema de telecomunicaciones llama al método
onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)
. La app debe mostrar una instancia de tu claseConnection
para representar la nueva llamada saliente. Si quieres obtener más información sobre las propiedades que debes establecer en la conexión, consulta Cómo implementar el servicio de conexión. - Cuando se haya establecido la llamada saliente, llama al método
setActive()
para informar al subsistema de telecomunicaciones que la llamada está activa.
Finalizar una llamada
Para finalizar una llamada, sigue estos pasos:
- Llama a
setDisconnected(DisconnectCause)
enviandoLOCAL
como parámetro si el usuario finalizó la llamada oREMOTE
si la otra parte finalizó la llamada. - Llama al método
destroy()
.
Restricciones de llamadas
A fin de garantizar una experiencia de llamada consistente y sencilla para tus usuarios, el framework de las telecomunicaciones aplica algunas restricciones a la hora de administrar las llamadas en el dispositivo. Supongamos que el usuario instaló dos apps de llamadas que implementan la API autoadministrada de ConnectionService
, FooTalk y BarTalk. En este caso, se aplican las siguientes restricciones:
En dispositivos que ejecutan el nivel de API 27 o versiones anteriores, solo una app puede mantener una llamada en curso en cualquier momento. Esta restricción significa que, mientras un usuario está en una llamada mediante la app FooTalk, la app BarTalk no puede iniciar ni recibir llamadas.
En los dispositivos que ejecutan el nivel de API 28 o versiones posteriores, si FooTalk y BarTalk declaran los permisos de
CAPABILITY_SUPPORT_HOLD
yCAPABILITY_HOLD
, el usuario puede mantener más de una llamada en curso si alterna entre las apps para iniciar o responder otra llamada.Si el usuario participa en llamadas administradas regulares (por ejemplo, con la app de Teléfono integrada), no puede llevar a cabo llamadas que se originaron a partir de apps de llamadas. Esto significa que, si el usuario está en una llamada normal usando su proveedor de telefonía celular, tampoco puede estar en una llamada de FooTalk o BarTalk de forma simultánea.
El subsistema de telecomunicaciones desconecta las llamadas de tu app si el usuario realiza una llamada de emergencia.
Tu app no puede recibir ni realizar llamadas mientras el usuario está en una llamada de emergencia.
Si contestas una llamada entrante en tu app mientras hay una llamada en curso en la otra app de llamadas, la llamada en curso finalizará. Tu app no debería mostrar su interfaz de usuario común de llamada entrante. El framework de las telecomunicaciones muestra la interfaz de usuario de llamada entrante y comunica al usuario que contestar la nueva llamada finalizará la que está en curso. Esto significa que, si el usuario está en una llamada de FooTalk, y la app de BarTalk recibe una llamada entrante, el framework de las telecomunicaciones informará al usuario que tiene una nueva llamada entrante de BarTalk y que responder la llamada de BarTalk finalizará su llamada de FooTalk.