Criar um aplicativo de chamada

Um app de chamadas permite que os usuários recebam ou façam chamadas de áudio ou videochamadas no dispositivo. Os apps de chamadas usam a própria interface do usuário para as chamadas em vez de usar a interface padrão do app Telefone, conforme mostrado na captura de tela a seguir.

Exemplo de um app de chamadas
Exemplo de um app de chamadas usando a própria interface do usuário

O framework do Android inclui o pacote android.telecom, que contém classes que ajudam a criar um app de chamadas de acordo com o framework de telecomunicações. A criação do seu app de acordo com o framework de telecomunicações oferece os seguintes benefícios:

  • Interoperação correta do app com o subsistema de telecomunicações nativo no dispositivo.
  • Interoperação correta do app com outros apps de chamadas que também aderem ao framework.
  • O framework ajuda o app a gerenciar o roteamento de áudio e vídeo.
  • O framework ajuda o app a determinar se as chamadas têm foco.

Declarações e permissões do manifesto

No manifesto do app, declare que ele usa a permissão MANAGE_OWN_CALLS, conforme mostrado no exemplo a seguir:

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

Para ver mais informações sobre como declarar permissões do app, consulte Permissões.

Você precisa declarar um serviço que especifica a classe que implementa a classe ConnectionService no seu app. O subsistema de telecomunicações requer que o serviço declare a permissão BIND_TELECOM_CONNECTION_SERVICE para ser capaz de se vincular a ele. O exemplo a seguir mostra como declarar o serviço no manifesto do aplicativo:

<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>

Para ver mais informações sobre a declaração de componentes de app, incluindo serviços, consulte Componentes do app.

Implementar o serviço de conexão

Seu app de chamadas precisa fornecer uma implementação da classe ConnectionService à qual o subsistema de telecomunicações pode se vincular. A implementação ConnectionService precisa modificar os seguintes métodos:

onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest)

Para realizar uma nova chamada, o subsistema de telecomunicações chama esse método em resposta à chamada de placeCall(Uri, Bundle) pelo app. Seu app retorna uma nova instância da implementação da classe Connection (para ver mais informações, consulte Implementar a conexão) para representar a nova chamada realizada. Você pode personalizar ainda mais a conexão de saída realizando as seguintes ações:

onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

O subsistema de telecomunicações chama esse método quando seu app chama o placeCall(Uri, Bundle), e a chamada não pode ser realizada. Em resposta a essa situação, seu app precisa informar ao usuário (por exemplo, usando uma caixa de alerta ou um aviso) que não foi possível realizar a chamada. Talvez seu app não consiga fazer uma chamada se houver uma chamada de emergência em andamento ou se houver outra chamada em andamento em outro app que não puder ser colocada em espera antes da sua chamada poder ser realizada.

onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest)

O subsistema de telecomunicações chama esse método quando seu app chama o método addNewIncomingCall(PhoneAccountHandle, Bundle) para informar ao sistema sobre uma nova chamada recebida no seu app. Seu app retorna uma nova instância da implementação Connection (para ver mais informações, consulte Implementar a conexão) para representar a nova chamada recebida. Você pode personalizar ainda mais a conexão de entrada realizando as seguintes ações:

onCreateIncomingConnectionFailed(PhoneAccountHandle, ConnectionRequest)

O subsistema de telecomunicações chama esse método quando seu app chama o método addNewIncomingCall(PhoneAccountHandle, Bundle) para informar ao sistema de telecomunicações sobre uma nova chamada recebida, mas a chamada recebida não é permitida. Para ver mais informações, consulte Restrições de chamadas. Seu app precisa rejeitar silenciosamente a chamada recebida e, opcionalmente, postar uma notificação para informar ao usuário sobre a chamada perdida.

Implementar a conexão

Seu app precisa criar uma subclasse de Connection para representar as chamadas no seu app. É necessário modificar os seguintes métodos na implementação:

onShowIncomingCallUi()

O subsistema de telecomunicações chama esse método quando uma chamada recebida é adicionada e seu app precisa mostrar a IU relacionada.

onCallAudioStateChanged(CallAudioState)

O subsistema de telecomunicações chama esse método para informar ao app que a rota ou modo de áudio atual mudou. Ele é chamado em resposta à mudança do modo áudio no seu app com o método setAudioRoute(int). Esse método também poderá ser chamado se o sistema mudar a rota de áudio, por exemplo, quando um fone de ouvido Bluetooth for desconectado.

onHold()

O subsistema de telecomunicações chama esse método para colocar uma chamada em espera. Em resposta a essa solicitação, seu app precisa reter a chamada e invocar o método setOnHold() para informar ao sistema que a chamada está sendo retida. O subsistema de telecomunicações pode chamar esse método quando um serviço em chamada, como o Android Auto, que está mostrando a chamada quer redirecionar uma solicitação de usuário para colocar a chamada em espera. O subsistema de telecomunicações também chamará esse método se o usuário fizer uma chamada em outro app. Para mais informações sobre os serviços de chamada recebida, consulte InCallService.

onUnhold()

O subsistema de telecomunicações chama esse método para retomar uma chamada que foi colocada em espera. Depois que seu app retomar a chamada, ele precisará invocar o método setActive() para informar ao sistema que a chamada não está mais em espera. O subsistema de telecomunicações pode chamar esse método quando um serviço de chamada que está mostrando a chamada, como o Android Auto, quer redirecionar uma solicitação para retomar a chamada. Para ver mais informações sobre os serviços de chamada, consulte InCallService.

onAnswer()

O subsistema de telecomunicações chama esse método para informar ao seu app que uma chamada recebida precisa ser atendida. Assim que seu app atender à chamada, ele precisará invocar o método setActive() para informar ao sistema que a chamada foi atendida. O subsistema de telecomunicações pode chamar esse método quando seu app adiciona uma nova chamada recebida e já há uma chamada em andamento em outro app que não pode ser colocada em espera. O subsistema de telecomunicações exibe a IU de chamada recebida em nome do seu app nessas situações. O framework fornece um método sobrecarregado que permite especificar o estado do vídeo em que a chamada será atendida. Para ver mais informações, consulte onAnswer(int).

onReject()

O subsistema de telecomunicações chama esse método para rejeitar uma chamada recebida. Depois que seu app rejeitar a chamada, ele precisará chamar setDisconnected(DisconnectCause) e especificar REJECTED como o parâmetro. Seu app precisa chamar o método destroy() para informar ao sistema que ele processou a chamada. O subsistema de telecomunicações chama esse método quando o usuário rejeita uma chamada recebida no seu app.

onDisconnect()

O subsistema de telecomunicações chama esse método para desconectar uma chamada. Depois que a chamada for encerrada, seu app precisará chamar o método setDisconnected(DisconnectCause) e especificar LOCAL como o parâmetro para indicar que uma solicitação do usuário fez com que a chamada fosse desconectada. Seu app precisa chamar o método destroy() para informar ao subsistema de telecomunicações que o app processou a chamada. O sistema poderá chamar esse método quando o usuário desconectar uma chamada por meio de outro serviço, como o Android Auto. O sistema também chama esse método quando sua chamada precisa ser desconectada para permitir que outra seja feita, por exemplo, se o usuário quiser fazer uma chamada de emergência. Para ver mais informações sobre os serviços de chamada, consulte InCallService.

Lidar com situações comuns de chamada

O uso da API ConnectionService no seu fluxo de chamadas envolve a interação com as outras classes no pacote android.telecom. As seções a seguir descrevem situações comuns de chamada e como seu app precisa usar as APIs para lidar com elas.

Atender as chamadas recebidas

O fluxo para lidar com as chamadas recebidas muda de acordo com a presença de chamadas em outros apps . A razão para a diferença nos fluxos é que o framework de telecomunicações precisa estabelecer algumas restrições quando há chamadas ativas em outros apps para garantir um ambiente estável para todos os apps de chamada no dispositivo. Para ver mais informações, consulte Restrições de chamada.

Nenhuma chamada ativa em outros apps

Para atender chamadas recebidas quando não houver chamadas ativas em outros apps, siga estas etapas:

  1. Seu app recebe uma nova chamada usando os mecanismos habituais.
  2. Use o método addNewIncomingCall(PhoneAccountHandle, Bundle) para informar ao subsistema de telecomunicações sobre a nova chamada recebida.
  3. O subsistema de telecomunicações será vinculado à implementação de ConnectionService no app e solicitará uma nova instância da classe Connection que representa a nova chamada recebida usando o método onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest).
  4. O subsistema de telecomunicações informa ao seu app que ele precisa mostrar a interface do usuário de chamada recebida usando o método onShowIncomingCallUi().
  5. Seu app mostra a IU de entrada usando uma notificação com uma intent de tela cheia associada. Para mais informações, consulte onShowIncomingCallUi().
  6. Chame o método setActive() se o usuário aceitar a chamada recebida. Se o usuário rejeitar a chamada, chame setDisconnected(DisconnectCause) especificando REJECTED como o parâmetro seguido por uma chamada para o método destroy().

Chamadas ativas em outros apps que não podem ser colocadas em espera

Para atender a chamadas recebidas quando houver chamadas ativas em outros apps que não possam ser colocadas em espera, siga estas etapas:

  1. Seu app recebe uma nova chamada usando os mecanismos habituais.
  2. Use o método addNewIncomingCall(PhoneAccountHandle, Bundle) para informar ao subsistema de telecomunicações sobre a nova chamada recebida.
  3. O subsistema de telecomunicações será vinculado à implementação de ConnectionService do seu app e solicitará uma nova instância do objeto Connection que representa a nova chamada recebida usando o método onCreateIncomingConnection(PhoneAccountHandle, ConnectionRequest).
  4. O subsistema de telecomunicações exibe a IU de chamada recebida para sua chamada.
  5. Se o usuário aceitar a chamada, o subsistema de telecomunicações chamará o método onAnswer(). Você precisa chamar o método setActive() para indicar ao subsistema de telecomunicações que a chamada está conectada.
  6. Se o usuário rejeitar a chamada, o subsistema de telecomunicações chamará o método onReject(). Você precisa chamar o método setDisconnected(DisconnectCause) especificando REJECTED como o parâmetro seguido por uma chamada para o método destroy().

Realizar chamadas

O fluxo para realizar uma chamada envolve lidar com a impossibilidade de fazer a chamada devido a restrições impostas pelo framework de telecomunicações. Para mais informações, consulte Restrições de chamada.

Para realizar uma chamada, siga estas etapas:

  1. O usuário inicia uma chamada realizada dentro do seu app.
  2. Use o método placeCall(Uri, Bundle) para informar ao subsistema de telecomunicações sobre a nova chamada realizada. Faça as seguintes considerações para os parâmetros do método:
    • O parâmetro Uri representa o endereço de destino da chamada. Para números de telefone normais, use o esquema de URI tel:.
    • O parâmetro Bundle permite que você forneça informações sobre seu app de chamadas adicionando o objeto PhoneAccountHandle do seu app ao EXTRA_PHONE_ACCOUNT_HANDLE extra. Seu app precisa fornecer o objeto PhoneAccountHandle a todas as chamadas realizadas.
    • O Bundle também permite que você especifique se a chamada realizada inclui vídeo, especificando o valor STATE_BIDIRECTIONAL no EXTRA_START_CALL_WITH_VIDEO_STATE extra. Considere que, por padrão, o subsistema de telecomunicações direciona as videochamadas para o viva-voz.
  3. O subsistema de telecomunicações é vinculado à implementação de ConnectionService do seu app.
  4. Se o app não conseguir realizar uma chamada, o subsistema de telecomunicações chamará o método onCreateOutgoingConnectionFailed(PhoneAccountHandle, ConnectionRequest) para informar ao app que a chamada não pode ser feita naquele momento. Seu app precisa informar ao usuário que a chamada não pode ser feita.
  5. Se o app conseguir realizar a chamada, o subsistema de telecomunicações chamará o método onCreateOutgoingConnection(PhoneAccountHandle, ConnectionRequest). Seu app precisa retornar uma instância da classe Connection para representar a nova chamada realizada. Para ver mais informações sobre as propriedades que precisam ser definidas na conexão, consulte Implementar o serviço de conexão.
  6. Quando a chamada realizada for conectada, chame o método setActive() para informar ao subsistema de telecomunicações que a chamada está ativa.

Finalizar uma chamada

Para finalizar uma chamada, siga estas etapas:

  1. Chame setDisconnected(DisconnectCause) enviando LOCAL como o parâmetro se o usuário tiver finalizado a chamada ou envie REMOTE como o parâmetro se a outra parte tiver finalizado a chamada.
  2. Chame o método destroy().

Restrições de chamada

Para garantir uma experiência de chamada consistente e simples para os usuários, o framework de telecomunicações impõe algumas restrições para o gerenciamento de chamadas no dispositivo. Por exemplo, considere que o usuário instalou dois apps de chamada que implementam a API ConnectionService autogerenciada, o FooTalk e o BarTalk. Nesse caso, as seguintes restrições se aplicam:

  • Em dispositivos no nível da API 27 ou anterior, somente um app pode manter uma chamada em andamento a qualquer momento. Essa restrição significa que, enquanto um usuário tem uma chamada em andamento usando o app FooTalk, o app BarTalk não pode iniciar ou receber uma nova chamada.

    Em dispositivos nível da API 28 ou mais recente, se tanto o FooTalk quanto o BarTalk declararem as permissões CAPABILITY_SUPPORT_HOLD e CAPABILITY_HOLD, o usuário poderá manter mais de uma chamada em andamento alternando entre os apps para iniciar ou atender outra chamada.

  • Se o usuário estiver em chamadas gerenciadas normais, por exemplo, usando o app Telefone integrado, ele não poderá estar em chamadas originadas de apps de chamada. Isso significa que, se o usuário estiver em uma chamada normal usando a operadora de celular, ele não poderá estar em uma chamada do FooTalk ou BarTalk simultaneamente.

  • O subsistema de telecomunicações desconectará as chamadas do seu app se o usuário fizer uma chamada de emergência.

  • Seu app não pode receber ou fazer chamadas enquanto o usuário estiver em uma chamada de emergência.

  • Se houver uma chamada em andamento no outro app de chamadas quando seu app receber uma chamada, atender a chamada recebida finalizará todas as chamadas em andamento no outro app. Seu app não pode exibir a interface do usuário de chamada recebida. O framework de telecomunicações exibe a interface do usuário da chamada recebida e informa que atender à nova chamada finalizará as chamadas em andamento. Isso significa que se o usuário estiver em uma chamada do FooTalk e o app BarTalk receber uma chamada, o framework de telecomunicações informará que ele tem uma nova chamada recebida do BarTalk e que atender à chamada do BarTalk finalizará a chamada do FooTalk.