Para implementar um aplicativo de interação por voz (VIA), siga estas etapas:
- Crie um esqueleto de VIA.
- (opcional) Implemente um fluxo de configuração/entrada.
- (opcional) Implemente uma tela de configurações.
- Declare as permissões necessárias no arquivo de manifesto.
- Implemente uma interface de placa de voz.
- Implemente o reconhecimento de voz (precisa incluir a implementação da API RecognitionService).
- Implementar a frase (você também pode implementar a API TextToSpeech).
- Implementar o fulfillment de comandos. Consulte este conteúdo em Como atender comandos.
As seções a seguir descrevem como concluir cada etapa mencionada acima.
Criar um esqueleto de VIA
Manifestos
Um app é detectado como um com interação por voz quando o seguinte é incluído no manifesto:
AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.myvoicecontrol"> ... <application ... > <service android:name=".MyInteractionService" android:label="@string/app_name" android:permission="android.permission.BIND_VOICE_INTERACTION" android:process=":interactor"> <meta-data android:name="android.voice_interaction" android:resource="@xml/interaction_service" /> <intent-filter> <action android:name= "android.service.voice.VoiceInteractionService" /> </intent-filter> </service> </application> </manifest>
Neste exemplo:
- Os VIAs precisam expor um serviço que estende
VoiceInteractionService
, com um filtro de intent para a açãoVoiceInteractionService.SERVICE_INTERFACE ("android.service.voice.VoiceInteractionService")
. - Esse serviço precisa ter a permissão de assinatura do sistema
BIND_VOICE_INTERACTION
. - Esse serviço precisa incluir um arquivo de metadados
android.voice_interaction
para conter o seguinte:res/xml/interaction_service.xml
<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android" android:sessionService= "com.example.MyInteractionSessionService" android:recognitionService= "com.example.MyRecognitionService" android:settingsActivity= "com.example.MySettingsActivity" android:supportsAssist="true" android:supportsLaunchVoiceAssistFromKeyguard="true" android:supportsLocalInteraction="true" />
Para detalhes sobre cada campo, consulte R.styleable#VoiceInteractionService
.
Como todos os VIAs também são serviços de reconhecimento de voz, você também precisa
incluir o seguinte no manifesto:
AndroidManifest.xml
<manifest ...> <uses-permission android:name="android.permission.RECORD_AUDIO"/> <application ...> ... <service android:name=".RecognitionService" ...> <intent-filter> <action android:name="android.speech.RecognitionService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> <meta-data android:name="android.speech" android:resource="@xml/recognition_service" /> </service> </application> </manifest>
Os serviços de reconhecimento de voz também exigem os seguintes metadados:
res/xml/recognition_service.xml
<recognition-service xmlns:android="http://schemas.android.com/apk/res/android" android:settingsActivity="com.example.MyRecognizerSettingsActivity" />
VoiceInteractionService, VoiceInteractionSessionService e VoiceInteractionSession
O diagrama a seguir mostra o ciclo de vida de cada uma dessas entidades:
Figura 1. Lifecycle
Como mencionado anteriormente, VoiceInteractionService
é o ponto de entrada
de uma VIA. As principais responsabilidades desse serviço são:
- Inicialize todos os processos que precisam ser mantidos em execução enquanto esta VIA for a ativa. Por exemplo, a detecção de hotword.
- Informa os comandos de voz compatíveis (consulte Toque para ler do Google Assistente).
- Iniciar sessões de interação por voz na tela de bloqueio (proteção por senha).
Na forma mais simples, uma implementação do VoiceInteractionService seria assim:
public class MyVoiceInteractionService extends VoiceInteractionService { private static final List<String> SUPPORTED_VOICE_ACTIONS = Arrays.asList( CarVoiceInteractionSession.VOICE_ACTION_READ_NOTIFICATION, CarVoiceInteractionSession.VOICE_ACTION_REPLY_NOTIFICATION, CarVoiceInteractionSession.VOICE_ACTION_HANDLE_EXCEPTION ); @Override public void onReady() { super.onReady(); // TODO: Setup hotword detector } @NonNull @Override public Set<String> onGetSupportedVoiceActions( @NonNull Set<String> voiceActions) { Set<String> result = new HashSet<>(voiceActions); result.retainAll(SUPPORTED_VOICE_ACTIONS); return result; } ... }
A implementação de VoiceInteractionService#onGetSupportedVoiceActions()
é
necessária para processar
o Toque para ler do Google Assistente.
Um VoiceInteractionSessionService é usado pelo sistema para criar e
interagir com uma VoiceInteractionSession. Ele tem apenas uma responsabilidade,
que é iniciar novas sessões quando solicitado.
public class MyVoiceInteractionSessionService extends VoiceInteractionSessionService { @Override public VoiceInteractionSession onNewSession(Bundle args) { return new MyVoiceInteractionSession(this); } }
Por fim, uma VoiceInteractionSession é onde a maior parte do trabalho
seria feita. Uma única instância de sessão pode ser reutilizada para concluir várias
interações do usuário. No AAOS, há uma CarVoiceInteractionSession
auxiliar,
que ajuda a implementar algumas das funcionalidades exclusivas do automóvel.
public class MyVoiceInteractionSession extends CarVoiceInteractionSession { public InteractionSession(Context context) { super(context); } @Override protected void onShow(String action, Bundle args, int showFlags) { closeSystemDialogs(); // TODO: Unhide UI and update UI state // TODO: Start processing audio input } ... }
VoiceInteractionSession
tem um grande conjunto de métodos de callback que são
explicados nas seções a seguir. Consulte a documentação de VoiceInteractionSession
para conferir uma lista completa.
Implementar um fluxo de configuração/login
A configuração e o login podem ocorrer:
- Durante a integração do dispositivo (assistente de configuração).
- Durante a troca de serviço de interação por voz (Configurações).
- Na primeira vez que o app é selecionado.
Para saber mais sobre a experiência do usuário recomendada e a orientação visual, consulte Assistentes pré-carregados: orientação de UX.
Configuração durante a troca de serviço de voz
Sempre é possível que o usuário selecione uma VIA que não foi configurada corretamente. Isso pode acontecer porque:
- O usuário ignorou o assistente de configuração por completo ou a etapa de configuração de interação por voz.
- O usuário selecionou uma VIA diferente da configurada durante a integração do dispositivo.
De qualquer forma, uma VoiceInteractionService
tem várias maneiras de incentivar o usuário
a concluir a configuração:
- Lembrete de notificação.
- Resposta automática por voz quando o usuário tenta usá-la.
Observação: não é recomendado apresentar um fluxo de configuração da VIA sem uma solicitação explícita do usuário. Isso significa que as VIAs não devem mostrar conteúdo automaticamente no HU durante a inicialização do dispositivo ou como resultado de uma troca ou desbloqueio de usuário.
Lembrete de notificação
Um lembrete de notificação é uma maneira não intrusiva de indicar a necessidade de configuração e fornecer aos usuários uma capacidade de navegar no fluxo de configuração do Google Assistente.
Figura 2. Lembrete de notificação
Confira como esse fluxo funciona:
Figura 3. Fluxo de lembrete de notificação
Responder por voz
Esse é o fluxo mais simples de implementar, iniciando uma declaração em
um callback VoiceInteractionSession#onShow()
, explicando ao usuário o que
precisa ser feito e perguntando se ele quer iniciar o fluxo de configuração (se a configuração for permitida de acordo com o estado da restrição de UX). Se não for possível fazer a configuração no momento, explique essa
situação também.
Configuração no primeiro uso
Sempre é possível que o usuário acione uma VIA que não foi configurada corretamente. Nesses casos:
- Informe verbalmente o usuário sobre essa situação (por exemplo, "Para funcionar corretamente, preciso que você conclua algumas etapas… ").
- Se o mecanismo de restrições de UX permitir (consulte UX_RESTRICTIONS_NO_SETUP), pergunte ao usuário se ele quer iniciar o processo de configuração e abra a tela de configurações do VIA.
- Caso contrário (por exemplo, se o usuário estiver dirigindo), deixe uma notificação para que ele clique na opção quando for seguro.
Criar telas de configuração de interação por voz
As telas de configuração e login precisam ser desenvolvidas como atividades normais. Consulte as diretrizes de UX e visuais para o desenvolvimento da interface em Assistentes pré-carregados: orientação de UX.
Diretrizes gerais:
- As VIAs precisam permitir que os usuários interrompam e retomem a configuração a qualquer momento.
- A configuração não será permitida se a restrição
UX_RESTRICTIONS_NO_SETUP
estiver em vigor. Para mais detalhes, consulte as Diretrizes sobre distração do motorista. - As telas de configuração precisam corresponder ao sistema de design de cada veículo. O layout geral da tela, os ícones, as cores e outros aspectos precisam ser consistentes com o restante da interface. Consulte Personalização para mais detalhes.
Implementar uma tela de configurações
Figura 4. Integração de configurações
As telas de configurações são atividades normais do Android. Se implementado, o ponto de entrada
precisa ser declarado no res/xml/interaction_service.xml
como parte dos manifestos
VIA (consulte
Manifestos).
A seção "Configurações" é um bom lugar para continuar a configuração e o login (se o usuário não tiver concluído
essas etapas) ou oferecer uma opção de sair ou trocar de usuário, se necessário. Assim como as telas de configuração
descritas acima, essas telas precisam:
- Ofereça a opção de sair de volta à tela anterior na pilha de telas (por exemplo, para as configurações do carro).
- Não é permitido ao dirigir. Para mais detalhes, consulte as Diretrizes sobre distrações do motorista.
- Combine cada sistema de design de veículo. Para mais detalhes, consulte Personalização.
Declarar as permissões necessárias no arquivo de manifesto
As permissões necessárias para uma VIA podem ser divididas em três categorias:
- Permissões de assinatura do sistema. Essas permissões são concedidas apenas a APKs pré-instalados e assinados pelo sistema. Os usuários não podem conceder essas permissões. Somente os OEMs podem fazer isso ao criar as imagens do sistema. Para mais informações sobre como receber permissões de assinatura, consulte Conceder permissões privilegiadas do sistema.
- Permissões perigosas. Estas são as permissões que um usuário precisa conceder usando a caixa de diálogo PermissionsController. Os OEMs podem conceder antecipadamente algumas dessas permissões ao VoiceInteractionService padrão. No entanto, como esse padrão pode mudar de dispositivo para dispositivo, os apps precisam solicitar essas permissões quando necessário.
- Outras permissões. São todas as outras permissões que não exigem intervenção do usuário. Essas permissões são concedidas automaticamente pelo sistema.
Considerando o exposto acima, a seção a seguir se concentra apenas em solicitar permissões perigosas. As permissões só podem ser solicitadas enquanto o usuário está nas telas de login ou de configuração.
Se o app não tiver as permissões necessárias para operar, o fluxo recomendado é usar uma frase de voz para explicar a situação ao usuário e uma notificação para fornecer uma capacidade que o usuário possa usar para voltar às telas de configurações do VIA. Para mais detalhes, consulte 1. Lembrete de notificação.
Solicitar permissões como parte da tela de configurações
As permissões perigosas são solicitadas usando o método ActivityCompat#requestPermission()
regular (ou equivalente). Para mais detalhes sobre como solicitar permissões, consulte
Solicitar permissões do app.
Figura 5. Solicitar permissões
Permissão de listener de notificações
Para implementar o fluxo de TTR, as VIAs precisam ser designadas como um listener de notificação. Essa não é uma permissão em si, mas uma configuração que permite que o sistema envie notificações para listeners registrados. Para saber se o VIA recebeu acesso a essas informações, os apps podem:
- (Opcional) Use
CarAssistUtils#assistantIsNotificationListener()
para verificar se há listeners de notificação com antecedência. Isso pode ser feito, por exemplo, durante o fluxo de configuração. - (Obrigatório) Reagir ao processamento do
CarVoiceInteractionSession#onShow()
com a açãoVOICE_ACTION_HANDLE_EXCEPTION
e a exceçãoEXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING
.
Se esse acesso não for concedido previamente, o VIA vai direcionar o usuário à seção "Acesso à notificação" das configurações do carro usando uma combinação de frases e notificações. O código abaixo pode ser usado para abrir a seção adequada do app de configurações:
private void requestNotificationListenerAccess() { Intent intent = new Intent(Settings .ACTION_NOTIFICATION_LISTENER_SETTINGS); intent.putExtra(Settings.EXTRA_APP_PACKAGE, getPackageName()); startActivity(intent); }
Implementar uma interface de placa de voz
Quando um VoiceInteractionSession
recebe um callback onShow()
,
ele pode apresentar uma interface de placa de voz. Para conferir as diretrizes de UX e visuais sobre a implementação de placa de voz,consulte
Assistentes pré-carregados: orientação de UX.
Figura 6. Como mostrar o ícone de voz
Há duas opções para implementar essa interface:
- Substituir
VoiceInteractionSession#onCreateContentView()
- Iniciar uma atividade usando
VoiceInteractionSession#startAssistantActivity()
Usar onCreateContentView()
Essa é a forma padrão de apresentar uma placa de voz. A classe base VoiceInteractionSession
cria uma janela e gerencia o ciclo de vida dela enquanto uma sessão
de voz estiver ativa. Os apps precisam substituir VoiceInteractionSession#onCreateContentView()
e retornar uma visualização anexada a essa janela assim que a sessão for
criada. Essa visualização precisa ser inicialmente invisível. Quando uma interação por voz começar,
essa visualização será mostrada em VoiceInteractionSession#onShow()
e depois oculta novamente em VoiceInteractionSession#onHide()
.
public class MyVoiceInteractionSession extends CarVoiceInteractionSession { private View mVoicePlate; … @Override public View onCreateContentView() { mVoicePlate = inflater.inflate(R.layout.voice_plate, null); … } @Override protected void onShow(String action, Bundle args, int showFlags) { // TODO: Update UI state to "listening" mVoicePlate.setVisibility(View.VISIBLE); } @Override public void onHide() { mVoicePlate.setVisibility(View.GONE); } … }
Ao usar esse método, ajuste VoiceInteractionSession#onComputeInsets()
para considerar as regiões obscurecidas da interface.
Usar startAssistantActivity()
Nesse caso, VoiceInteractionSession
delega o processamento da interface
da placa de voz a uma atividade regular. Quando essa opção é usada, uma implementação de VoiceInteractionSession
precisa desativar a criação da janela de conteúdo padrão (consulte Usar onCreateContentView()) no callback
onPrepareShow()
. Em VoiceInteractionSession#onShow()
, a sessão inicia a atividade
de placa de voz usando VoiceInteractionSession#startAssistantActivity()
. Esse
método inicia a IU com as flags de atividade e as configurações de janela adequadas.
public class MyVoiceInteractionSession extends CarVoiceInteractionSession { … @Override public void onPrepareShow(Bundle args, int showFlags) { super.onPrepareShow(args, showFlags); setUiEnabled(false); } @Override protected void onShow(String action, Bundle args, int showFlags) { closeSystemDialogs(); Intent intent = new Intent(getContext(), VoicePlateActivity.class); intent.putExtra(VoicePlateActivity.EXTRA_ACTION, action); intent.putExtra(VoicePlateActivity.EXTRA_ARGS, args); startAssistantActivity(intent); } … }
Para manter uma comunicação entre essa atividade e o
VoiceInteractionSession
, pode ser necessário um conjunto de intents internas ou vinculação de serviço. Por exemplo, quando VoiceInteractionSession#onHide()
é invocado, a
sessão precisa transmitir essa solicitação para a atividade.
Importante. No Automotive, apenas atividades
com anotações especiais ou listadas na "lista de permissões" do UXR podem ser mostradas durante
a direção. Isso também se aplica a atividades iniciadas com
VoiceInteractionSession#startAssistantActivity()
. Não se esqueça de
anotar sua atividade com <meta-data
android:name="distractionOptimized" android:value="true"/>
ou incluir essa
atividade na chave systemActivityWhitelist
do arquivo
/packages/services/Car/service/res/values/config.xml
. Para mais informações, consulte as Diretrizes
sobre distrações do motorista.
Implementar o reconhecimento de voz
Nesta seção, você vai aprender a implementar o reconhecimento de voz pela detecção e reconhecimento de palavras-chave. Um gatilho de voz é uma palavra usada para iniciar uma nova consulta ou ação por voz. Por exemplo, "Ok Google" ou "Ei Google".
Detecção de hotword da DSP
O Android oferece acesso a um detector de hotword sempre ativado no nível do DSP
por meio do AlwaysOnHotwordDetector
.
maneira de implementar a detecção de hotword com CPU baixa. O uso dessa funcionalidade é
dividido em duas partes:
- Instanciação de um
AlwaysOnHotwordDetector
. - Registro de um modelo de som de detecção de hotword.
A implementação do VoiceInteractionService pode criar um detector de palavras-chave usando
VoiceInteractionService#createAlwaysOnHotwordDetector()
,
transmitindo uma frase-chave e uma localidade que ele quer usar para a detecção. Como resultado, o
app recebe um callback onAvailabilityChanged()
com um dos seguintes valores possíveis:
STATE_HARDWARE_UNAVAILABLE
. O recurso de DSP não está disponível no dispositivo. Nesse caso, a detecção de hotword do software é usada.STATE_HARDWARE_UNSUPPORTED
. O suporte a DSP não está disponível em geral, mas o DSP não oferece suporte a uma determinada combinação de palavras-chave e localidade. O app pode usar a detecção de hotword de software.STATE_HARDWARE_ENROLLED
. A detecção de hotword está pronta e pode ser iniciada chamando o métodostartRecognition()
.STATE_HARDWARE_UNENROLLED
. Um modelo de som para a frase-chave solicitada não está disponível, mas a inscrição é possível.
A inscrição de modelos de som de detecção de hotword pode ser feita usando IVoiceInteractionManagerService#updateKeyphraseSoundModel()
.
Vários modelos podem ser registrados no sistema em um determinado momento, mas apenas um
modelo é associado a um AlwaysOnHotwordDetector
.
A detecção de hotword do DSP pode não estar disponível em todos os dispositivos. Os desenvolvedores da VIA
precisam verificar os recursos de hardware usando o método
getDspModuleProperties()
. Para conferir um exemplo de código que mostra
como registrar modelos de som, consulte VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java
.
Consulte Captura simultânea sobre
o reconhecimento de hotword simultâneo.
Detecção de hotword do software
Como indicado acima, a detecção de palavras-chave do DSP pode não estar disponível em todos os dispositivos. Por exemplo, o emulador do Android não oferece emulação de DSP. Nesse caso, o reconhecimento de voz do software é a única alternativa. Para evitar interferências em outros apps que possam precisar de acesso ao microfone, as VIAs precisam acessar a entrada de áudio usando:
- A captura de áudio precisa usar MediaRecorder.AudioSource.HOTWORD.
- A permissão
android.Manifest.permission.CAPTURE_AUDIO_HOTWORD
.
Ambas as constantes são @hide
e estão disponíveis apenas para apps agrupados.
Gerenciar entrada de áudio e reconhecimento de voz
A entrada de áudio seria implementada usando a classe MediaRecorder.
Para mais informações sobre como usar essa API, consulte a Visão geral do
MediaRecorder. Os serviços de interação por voz também precisam ser implementações de classe
RecognitionService
. Qualquer app no sistema que exija reconhecimento de voz usa o
para acessar esse recurso. Para fazer o reconhecimento de voz e ter acesso ao microfone, as VIAs
precisam manter android.permission.RECORD_AUDIO
.
Os apps que acessam uma implementação
RecognitionService
também precisam ter essa permissão.
Antes do Android 10, o acesso ao microfone era concedido a apenas um app por vez, com exceção da detecção de palavras-chave, consulte acima. A partir do Android 10, o acesso ao microfone pode ser compartilhado. Para mais informações, consulte Compartilhar entrada de áudio.
Acessar a saída de áudio
Quando o VIA estiver pronto para fornecer respostas verbais, é importante seguir este conjunto de diretrizes:
- Ao solicitar a seleção de áudio ou gerenciar a saída de áudio, o app
precisa usar
AudioAttributes#USAGE_ASSISTANT
eAudioAttributes#CONTENT_TYPE_SPEECH
como atributos de áudio. - Durante o reconhecimento de fala, a seleção de áudio precisa ser solicitada com
AudioManage#AUDIOFOCUS_GAIN_TRANSIENT_EXCLUSIVE
. Alguns apps de mídia podem não reagir corretamente aos comandos de mídia (consulte Como atender comandos de mídia) enquanto o foco de áudio é removido.