Para implementar um aplicativo de interação de voz (VIA, na sigla em inglês), siga estas etapas:
- Crie um esqueleto VIA.
- (opcional) Implemente um fluxo de configuração/login.
- (opcional) Implemente uma tela de configurações.
- Declarar as permissões necessárias no arquivo de manifesto.
- Implemente uma interface de placa de voz.
- Implementar reconhecimento de voz (deve incluir a implementação da API RecognitionService).
- Implemente a expressão (opcionalmente, você pode implementar a API TextToSpeech).
- Implementar o fulfillment de comandos. Ver esse 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 com interação por voz é detectado quando o seguinte é incluídas 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 estenda
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 (link em inglês)
<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
inclua 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 já mencionamos, VoiceInteractionService
é o ponto de entrada.
para um VIA. As principais responsabilidades desse serviço são:
- Inicializar todos os processos que devem permanecer em execução pelo tempo este VIA é o ativo. Por exemplo, detecção de hotword.
- Informa comandos de voz compatíveis (consulte Toque para ler do Assistente de voz).
- Iniciar sessões de interação por voz na tela de bloqueio (teclado).
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ário para lidar com
Toque para ler do Assistente de voz.
Um VoiceInteractionSessionService é usado pelo sistema para criar e
interagir com uma VoiceInteractionSession. Ele só tem uma responsabilidade,
para iniciar novas sessões quando solicitado.
public class MyVoiceInteractionSessionService extends VoiceInteractionSessionService { @Override public VoiceInteractionSession onNewSession(Bundle args) { return new MyVoiceInteractionSession(this); } }
Por fim, em VoiceInteractionSession, a maior parte do trabalho é
o que seria feito. Uma única instância de sessão pode ser reutilizada para concluir várias
interações do usuário. No AAOS, existe um auxiliar CarVoiceInteractionSession
.
ajudando a implementar algumas das funcionalidades exclusivas do setor automotivo.
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 ver 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 do serviço de interação por voz (Configurações).
- Na primeira inicialização, quando o app é selecionado.
Para detalhes sobre a experiência do usuário recomendada e a orientação visual, consulte Assistentes pré-carregados: orientações sobre a UX.
Configurar durante a troca do serviço de voz
O usuário sempre pode selecionar um VIA que não foi configurada. Isso pode acontecer pelos seguintes motivos:
- O usuário pulou o assistente de configuração ou ignorou a voz. para a etapa de configuração da interação.
- O usuário selecionou um VIA diferente daquele configurado no dispositivo integração.
De qualquer forma, uma VoiceInteractionService
tem várias maneiras de incentivar o usuário
para concluir a configuração:
- Lembrete de notificação.
- Resposta automática de voz quando o usuário tentar usá-la.
Observação: não é recomendável apresentar um fluxo de configuração por VIA. sem uma solicitação explícita do usuário. Isso significa que os VIAs devem evitar automaticamente exibir conteúdo na HU durante a inicialização do dispositivo ou como resultado de uma troca de usuário ou desbloquear.
Lembrete de notificação
Um lembrete de notificação é uma forma não intrusiva de indicar a necessidade de configuração, e fornecer aos usuários affordance para navegar pela configuração do Assistente fluxo
Figura 2. Lembrete de notificação
Veja como esse fluxo funcionaria:
Figura 3. Fluxo do lembrete de notificação
Responder por voz
Este é o fluxo mais simples de implementar, iniciando um enunciado em
um callback VoiceInteractionSession#onShow()
, explicando ao usuário o que
precisa ser feito e, em seguida, pergunte a eles (se a configuração é permitida, dado o estado de restrição de UX)
se quiserem iniciar o fluxo de configuração. Se não for possível fazer a configuração no momento, explique
sua situação também.
Configurar no primeiro uso
O usuário sempre pode acionar um VIA que não foi devidamente configurada. 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_INSTALL), pergunte ao usuário se ele quer iniciar o e abra a tela "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 a UX e diretrizes visuais para o desenvolvimento de IU em Assistentes pré-carregados: orientações sobre a UX.
Diretrizes gerais:
- Os VIAs devem 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 Diretrizes sobre distrações do motorista. - As telas de configuração precisam corresponder ao sistema de design de cada veículo. Tela geral o layout, os ícones, as cores e outros aspectos devem 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 da VIA.
manifestos (consulte
Manifestos).
A seção "Configurações" é um bom lugar para continuar a configuração e o login, caso o usuário não tenha concluído
ou ofereça uma opção de sair ou trocar de usuário, se necessário. Semelhante à configuração
descritas acima, essas telas devem:
- Oferecer a opção de voltar à tela anterior na pilha de telas (por exemplo, em "Configurações do carro").
- Não são permitidos ao dirigir. Confira mais detalhes em Diretrizes contra distrações do motorista.
- Combine o sistema de design de cada veículo. Para mais detalhes, consulte Personalização.
Declarar as permissões necessárias no arquivo de manifesto
As permissões exigidas por um VIA podem ser divididas em três categorias:
- Permissões de assinatura do sistema. Estas são as permissões concedido apenas a APKs pré-instalados assinados pelo sistema. Os usuários não podem conceder essas permissões, somente OEMs podem concedê-las ao criar as imagens do sistema. Para mais informações sobre como conseguir permissões de assinatura, consulte Conceder permissões privilegiadas do sistema.
- Permissões perigosas. Essas são permissões que um usuário deve conceder usando a caixa de diálogo PermissionsController. Os OEMs podem pré-conceder algumas dessas para o VoiceInteractionService padrão. Mas, como esse padrão pode mudar de dispositivo para dispositivo, os apps poderão solicitá-las e receber permissões quando necessário.
- Outras permissões. Essas são todas as outras permissões que não exigem a 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 na solicitação permissões perigosas. As permissões só devem ser solicitadas enquanto o usuário estiver nas telas de login ou de configuração.
Se o app não tem as permissões necessárias para funcionar, o fluxo recomendado é usar uma expressão de voz para explicar a situação ao usuário e uma notificação para fornecer uma affordance que o usuário possa usar para volte para as telas de configurações de VIA. Veja mais detalhes em 1. Lembrete de notificação.
Solicitar permissões como parte da tela de configuração
As permissões perigosas são solicitadas usando o método ActivityCompat#requestPermission()
normal (ou equivalente). Para detalhes sobre como solicitar permissões, consulte
Solicite permissões do app.
Figura 5. Solicitar permissões
Permissão do listener de notificações
Para implementar o fluxo do TTR, os VIAs precisam ser designados como listener de notificações. Não se trata apenas de uma permissão, mas de uma configuração que permite que o sistema envie notificações para os listeners. Para saber se o VIA recebeu acesso a essas informações, aplicativos podem:
- (Opcional) Verifique se há listeners de notificações com antecedência usando
CarAssistUtils#assistantIsNotificationListener()
: Isso pode ser feito, por exemplo, durante o fluxo de configuração. - (Obrigatório) Reagir ao processar
CarVoiceInteractionSession#onShow()
com uma açãoVOICE_ACTION_HANDLE_EXCEPTION
e a exceçãoEXCEPTION_NOTIFICATION_LISTENER_PERMISSIONS_MISSING
.
Se esse acesso não for concedido previamente, o VIA deverá direcionar o usuário para o Seção "Acesso a notificações" das configurações do carro, usando uma combinação de enunciados e notificações. O código a seguir pode ser usado para abrir a seção apropriada do app 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 diretrizes visuais e de UX sobre a implementação de placas de voz,consulte
Assistentes pré-carregados: orientações sobre a UX.
Figura 6. Exibindo a placa de voz
Há duas opções sobre como implementar essa interface:
- Substituir
VoiceInteractionSession#onCreateContentView()
- Iniciar uma atividade usando
VoiceInteractionSession#startAssistantActivity()
Usar onCreateContentView()
Esta é a forma padrão de apresentar uma placa de voz. O VoiceInteractionSession
a classe de base cria uma janela e gerencia o ciclo de vida dela enquanto uma solicitação de
sessão está ativa. Os apps precisam substituir VoiceInteractionSession#onCreateContentView()
e retorna uma visualização anexada a essa janela assim que a sessão é
criados. Inicialmente, essa visualização precisa ficar invisível. Quando uma interação por voz é iniciada,
esta visualização deve ficar visível em VoiceInteractionSession#onShow()
e ficará invisível 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, talvez você queira ajustar VoiceInteractionSession#onComputeInsets()
para considerar as regiões obscurecidas da interface.
Usar startAssistantActivity()
Nesse caso, VoiceInteractionSession
delega o gerenciamento da voz.
placa de vídeo para uma atividade regular. Quando essa opção é usada, um VoiceInteractionSession
implementação precisa desativar a criação da janela de conteúdo padrão (consulte Como usar onCreateContentView()) no onPrepareShow()
o retorno de chamada. Às VoiceInteractionSession#onShow()
, a sessão iniciaria a voz
atividade da placa usando VoiceInteractionSession#startAssistantActivity()
. Isso
inicia a IU com as configurações de janela e sinalizações de atividade 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
, um conjunto de intents internas ou vinculação de serviço pode ser
obrigatórios. Por exemplo, quando VoiceInteractionSession#onHide()
é invocado,
deve ser capaz de passar essa solicitação à atividade.
Importante: No Automotive, especialmente anotados
atividades ou atividades relacionadas na "lista de permissões" da UXR podem ser exibidas enquanto
dirigir. Isso se aplica a atividades iniciadas
VoiceInteractionSession#startAssistantActivity()
também. Lembre-se de
anotar sua atividade com <meta-data
android:name="distractionOptimized" android:value="true"/>
ou incluir este
atividade na tecla systemActivityWhitelist
do /packages/services/Car/service/res/values/config.xml
. Para mais informações, consulte Motorista
Diretrizes sobre distrações.
Implementar reconhecimento de voz
Nesta seção, você vai aprender a implementar reconhecimento de voz por meio da detecção e reconhecimento de hotwords. Uma hotword é uma palavra de acionamento usada para iniciar uma nova consulta ou ação por voz. Por exemplo, "Ok Google" ou "Ok Google".
Detecção de hotword da DSP
O Android fornece acesso a um detector de hotword sempre ativado no nível de DSP ao
em AlwaysOnHotwordDetector
.
de implementar a detecção de hotword com pouca CPU. 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 hotwords usando
VoiceInteractionService#createAlwaysOnHotwordDetector()
,
passando uma frase-chave e a localidade que eles querem usar para detecção. Como resultado,
o app recebe um onAvailabilityChanged()
com um dos seguintes valores possíveis:
STATE_HARDWARE_UNAVAILABLE
: O recurso DSP não está disponível no dispositivo. Nesse caso, é usada a detecção de hotword do software.STATE_HARDWARE_UNSUPPORTED
: Em geral, o suporte a DSP não está disponível, mas O DSP não é compatível com determinada combinação de localidade e frase-chave. O app pode optar por usar Detecção de hotwords de software.STATE_HARDWARE_ENROLLED
: A detecção de hotwords está pronta e pode ser iniciada chamando o métodostartRecognition()
.STATE_HARDWARE_UNENROLLED
: Não há um modelo de som para a frase-chave solicitada disponíveis, mas é possível fazer a inscrição.
O registro de modelos de som para detecção de hotword pode ser feito usando IVoiceInteractionManagerService#updateKeyphraseSoundModel()
.
Vários modelos podem ser registrados no sistema ao mesmo tempo, mas apenas um
modelo está associado a um AlwaysOnHotwordDetector
.
A detecção de hotword do DSP pode não estar disponível em todos os dispositivos. VIA desenvolvedores
precisa verificar os recursos de hardware usando getDspModuleProperties()
.
. Para o exemplo de código que mostra
como registrar modelos de som, consulte VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java
.
Consulte Captura simultânea referente a
reconhecimento de hotword simultâneo.
Detecção de hotword do software
Como indicado acima, a detecção de hotword do DSP pode não estar disponível em todas (por exemplo, o Android Emulator não oferece emulação DSP). Nesse caso, o reconhecimento de voz por software é a única alternativa. Para evitar a interferência em outros aplicativos que possam precisar de acesso ao microfone, os VIAs devem acessar a entrada de áudio usando:
- A captura de áudio precisa usar MediaRecorder.AudioSource.HOTPalavra.
- Mantenha a permissão
android.Manifest.permission.CAPTURE_AUDIO_HOTWORD
.
Essas duas constantes são @hide
e estão disponíveis apenas para apps em pacote.
Gerenciar a entrada de áudio e o reconhecimento de voz
A entrada de áudio seria implementada usando a classe MediaRecorder.
Para saber mais sobre como usar essa API, consulte a documentação do MediaRecorder
Visão geral. Os serviços de interação de voz também devem ter RecognitionService
implementações de classe. Qualquer aplicativo do sistema que exija reconhecimento de voz usa o
para acessar esse recurso. Para fazer reconhecimento de voz e ter acesso ao microfone, os VIAs
precisa conter android.permission.RECORD_AUDIO
.
Apps que acessam um RecognitionService
devem conter essa permissão também.
Antes do Android 10, o acesso ao microfone era concedido a apenas um app por vez (com exceção da detecção de hotword, consulte acima). A partir do Android 10, o acesso ao microfone pode ser compartilhado. Para mais informações, consulte Compartilhamento Entrada de áudio.
Acessar saída de áudio
Quando o VIA estiver pronto para dar respostas verbais, é importante siga as próximas 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 cumprir comandos de mídia) enquanto o áudio deles é removido.