Desenvolvimento de aplicações

Para implementar um aplicativo de interação por voz (VIA), conclua estas etapas:

  1. Crie um esqueleto VIA.
  2. ( opcional ) Implemente um fluxo de configuração/login.
  3. ( opcional ) Implemente uma tela de Configurações.
  4. Declare as permissões necessárias no arquivo de manifesto.
  5. Implemente uma interface do usuário de placa de voz.
  6. Implemente o reconhecimento de voz (deve incluir a implementação da API RecognitionService).
  7. Implemente o enunciado (opcionalmente, você pode implementar a API TextToSpeech).
  8. Implemente o cumprimento de comandos. Veja este conteúdo em Cumprindo Comandos .

As seções a seguir descrevem como concluir cada etapa mencionada acima.

Criar um esqueleto VIA

Manifestos

Um aplicativo é 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:

  • VIAs devem expor um serviço que estenda VoiceInteractionService , com um filtro de intent para a ação VoiceInteractionService.SERVICE_INTERFACE ("android.service.voice.VoiceInteractionService") .
  • Este serviço deve conter a permissão de assinatura do sistema BIND_VOICE_INTERACTION .
  • Este serviço deve 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 obter 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 deve incluir o seguinte em seu 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 descreve o ciclo de vida de cada uma dessas entidades:

Ciclos de vida

Figura 1. Ciclos de vida

Como dito anteriormente, VoiceInteractionService é o ponto de entrada para um VIA. As principais responsabilidades deste serviço são:

  • Inicialize todos os processos que devem ser mantidos em execução enquanto esta VIA estiver ativa. Por exemplo, detecção de hotword.
  • Relata ações de voz suportadas (consulte Voice Assistant Tap-to-Read ).
  • Inicie sessões de interação por voz a partir da tela de bloqueio (proteção de teclado).

Em sua forma mais simples, uma implementação de VoiceInteractionService ficaria 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 lidar com o Voice Assistant Tap-to-Read . Um VoiceInteractionSessionService é usado pelo sistema para criar e interagir com um VoiceInteractionSession . Tem apenas uma responsabilidade, iniciar novas sessões quando solicitado.

public class MyVoiceInteractionSessionService extends VoiceInteractionSessionService {
    @Override
    public VoiceInteractionSession onNewSession(Bundle args) {
        return new MyVoiceInteractionSession(this);
    }
}

Finalmente, 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, 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 retorno de chamada que serão explicados nas seções a seguir. consulte a documentação para VoiceInterationSession 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 de voz (Configurações).
  • Na primeira inicialização quando o aplicativo é selecionado.

Para obter detalhes sobre a experiência de usuário recomendada e 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 tenha sido configurada corretamente. Isso pode acontecer porque:

  • O usuário ignorou completamente o Assistente de Configuração ou ignorou a etapa de configuração da interação por voz.
  • O usuário selecionou uma VIA diferente daquela configurada durante a integração do dispositivo.

De qualquer forma, um VoiceInteractionService tem várias maneiras de incentivar o usuário a concluir a configuração:

  • Lembrete de notificação.
  • Resposta de voz automática quando o usuário tenta usá-lo.

Nota : É altamente desencorajado apresentar um fluxo de configuração VIA sem uma solicitação explícita do usuário. Isso significa que os VIAs devem evitar a exibição automática de conteúdo na HU durante a inicialização do dispositivo ou como resultado de uma troca ou desbloqueio do usuário.

1. 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 possibilidade de navegação no fluxo de configuração do assistente.

Lembrete de notificação

Figura 2. Lembrete de notificação

Veja como esse fluxo funcionaria:

Fluxo de lembrete de notificação

Figura 3. Fluxo de lembrete de notificação

Note : dependendo das regras de restrição de experiência do usuário (consulte Diretrizes de distração do motorista ), os fluxos de configuração podem não ser permitidos durante a condução. Quando o usuário clica na notificação e a configuração não é permitida, o aplicativo deve usar um enunciado para explicar a situação ao usuário em vez de tentar exibir uma interface do usuário que será imediatamente bloqueada.

2. Resposta de voz

Este é o fluxo mais simples de implementar, iniciando um enunciado em um retorno de chamada VoiceInteractionSession#onShow() , explicando ao usuário o que precisa ser feito e perguntando a ele (se a configuração for permitida, dado o estado de restrição de UX) se ele deseja iniciar o fluxo de configuração. Se a configuração não for possível 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. Em tais casos:

  1. Informe verbalmente o usuário sobre essa situação (por exemplo, "Para funcionar corretamente, preciso que você conclua algumas etapas...").
  2. Se o mecanismo de restrições de UX permitir (consulte UX_RESTRICTIONS_NO_SETUP ), pergunte ao usuário se ele deseja iniciar o processo de configuração e abra a tela Configurações da VIA.
  3. Caso contrário (por exemplo, se o usuário estiver dirigindo), deixe uma notificação para o usuário clicar na opção quando for seguro fazê-lo.

Criando telas de configuração de interação de voz

As telas de configuração e login devem ser desenvolvidas como atividades regulares. Consulte as diretrizes visuais e de UX para o desenvolvimento de UI em Preloaded Assistants: UX Guidance .

Orientações gerais:

  • VIAs devem permitir que os usuários interrompam e retomem a configuração a qualquer momento.
  • A configuração não deve ser permitida se a restrição UX_RESTRICTIONS_NO_SETUP estiver em vigor. Para obter detalhes, consulte Diretrizes de Distração do Motorista .
  • As telas de configuração devem corresponder ao sistema de design de cada veículo. O layout geral da tela, ícones, cores e outros aspectos devem ser consistentes com o restante da interface do usuário. Consulte Personalização para obter detalhes.

Implementar uma tela de configurações

Integração de configurações

Figura 4. Integração de configurações

As telas de configurações são atividades normais do Android. Se implementado, seu ponto de entrada deve ser declarado no res/xml/interaction_service.xml como parte dos manifestos VIA (consulte Manifests ). A seção Configurações é um bom lugar para continuar a configuração e entrar (se o usuário não tiver concluído) ou oferecer uma opção de saída ou troca de usuário , se necessário. Semelhante às telas de configuração descritas acima, essas telas devem:

  • Forneça a opção de voltar para a tela anterior na pilha de telas (por exemplo, para Configurações do carro).
  • Não ser permitido durante a condução. Para obter detalhes, consulte Diretrizes de Distração do Motorista .
  • Combine cada sistema de design de veículo. Para obter detalhes, consulte Personalização .

Declare as permissões necessárias no arquivo de manifesto

As permissões exigidas por uma VIA podem ser divididas em três categorias:

  • Permissões de assinatura do sistema. Essas são permissões concedidas apenas a APKs pré-instalados e assinados pelo sistema. Os usuários não podem conceder essas permissões, apenas os OEMs podem concedê-las ao criar suas imagens de sistema. Para obter mais informações sobre como obter permissões de assinatura, consulte Conceder permissões com privilégios 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 conceder previamente algumas dessas permissões ao VoiceInteractionService padrão. Mas, como esse padrão pode mudar de dispositivo para dispositivo, os aplicativos devem poder solicitar essas permissões quando necessário.
  • Outras permissões. Essas são todas as outras permissões que não exigem intervenção do usuário. Essas permissões serão concedidas automaticamente pelo sistema.

Dado o exposto, a seção a seguir se concentrará apenas na solicitação de permissões perigosas. As permissões só devem ser solicitadas enquanto o usuário estiver nas telas de login ou configuração.

Se o aplicativo não tiver as permissões necessárias para operar, o fluxo recomendado é usar um enunciado de voz para explicar a situação ao usuário e uma notificação para fornecer uma affordance que o usuário pode usar para navegar de volta às telas de configurações do VIA . Para obter detalhes, consulte 1. Lembrete de notificação .

Solicitar permissões como parte da tela de configuração

Permissões perigosas são solicitadas usando o método regular ActivityCompat#requestPermission() (ou equivalente). Para obter detalhes sobre como solicitar permissões, consulte Solicitar permissões do aplicativo .

Solicitar permissões

Figura 5. Solicitar permissões

Permissão de Ouvinte de Notificação

Para implementar o fluxo TTR, os VIAs devem ser designados como ouvintes de notificação. Esta não é uma permissão em si, mas sim uma configuração que permite que o sistema envie notificações para ouvintes registrados. Para saber se o VIA teve acesso a essas informações, os aplicativos podem:

Caso esse acesso não seja pré-concedido, a VIA deve direcionar o usuário para a seção Acesso à Notificação das Configurações do Carro, utilizando uma combinação de enunciados e notificações. O código a seguir pode ser usado para abrir a seção apropriada do aplicativo 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 IU do Voice Plate

Quando uma VoiceInteractionSession recebe um retorno de chamada onShow() , ela pode apresentar uma interface do usuário de placa de voz. Para obter diretrizes visuais e de UX sobre a implementação da placa de voz, consulte Assistentes pré-carregados: Orientação de UX .

Exibindo a placa de voz

Figura 6. Exibindo a placa de voz

Há duas opções de como implementar essa IU:

  • Substituir VoiceInteractionSession#onCreateContentView()
  • Iniciar uma atividade usando VoiceInteractionSession#startAssistantActivity()

Usando onCreateContentView()

Esta é a maneira padrão de apresentar uma placa de voz. A classe base VoiceInteractionSession criará uma janela e gerenciará seu ciclo de vida enquanto uma sessão de voz estiver ativa. Os aplicativos devem substituir VoiceInteractionSession#onCreateContentView() e retornar uma visualização que será anexada a essa janela assim que a sessão for criada. Essa visão deve ser inicialmente invisível. Quando uma interação de voz é iniciada, essa visualização deve ficar visível em VoiceInteractionSession#onShow() e depois 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, convém ajustar VoiceInteractionSession#onComputeInsets() para considerar regiões obscuras de sua interface do usuário.

Usando startAssistantActivity()

Nesse caso, a VoiceInteractionSession delegará o manuseio da interface do usuário da placa de voz para uma atividade regular. Quando essa opção é usada, uma implementação de VoiceInteractionSession deve desabilitar a criação de sua janela de conteúdo padrão (consulte Usando onCreateContentView() ) no retorno de chamada onPrepareShow() . Em VoiceInteractionSession#onShow() , a sessão iniciaria a Activity plate usando VoiceInteractionSession#startAssistantActivity() . Esse método inicia a interface do usuário com as configurações apropriadas da janela e os sinalizadores de atividade.

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 esta atividade e a VoiceInteractionSession , pode ser necessário um conjunto de Intents internos ou ligação de serviço. Por exemplo, quando VoiceInteractionSession#onHide() é invocado, a sessão deve poder passar essa solicitação para a atividade.

Importante. No Automotive, apenas atividades especialmente anotadas ou atividades listadas na "lista de permissões" do UXR podem ser exibidas durante a condução. Isso também se aplica a atividades iniciadas com VoiceInteractionSession#startAssistantActivity() . Lembre-se de anotar sua atividade com <meta-data android:name="distractionOptimized" android:value="true"/> ou incluir essa atividade na chave systemActivityWhitelist de /packages/services/Car/service/res/values/config.xml arquivo /packages/services/Car/service/res/values/config.xml . Para obter mais informações, consulte Diretrizes de distração do motorista .

Implementar reconhecimento de voz

Nesta seção, você aprenderá a implementar o reconhecimento de voz por meio da detecção e reconhecimento de hotwords. Uma hotword é uma palavra de gatilho usada para iniciar uma nova consulta ou ação por voz. Por exemplo, "OK Google" ou "Ei Google".

Detecção de Hotword DSP

O Android fornece acesso a um detector de hotword sempre ativo no nível DSP por meio da classe AlwaysOnHotwordDetector . Isso fornece uma maneira conveniente de implementar a detecção de hotword com CPU baixa. A utilização desta funcionalidade está dividida em duas partes:

A implementação de VoiceInteractionService pode criar um detector de hotword usando VoiceInteractionService#createAlwaysOnHotwordDetector() , passando uma frase-chave e localidade que desejam usar para detecção. Como resultado, o aplicativo receberá um retorno de chamada onAvailabilityChanged() com um dos seguintes valores possíveis:

  • STATE_HARDWARE_UNAVAILABLE . O recurso DSP não está disponível no dispositivo. Nesse caso, a detecção de hotword do software será usada.
  • STATE_HARDWARE_UNSUPPORTED . O suporte DSP não está disponível em geral, mas o DSP não oferece suporte a uma determinada combinação de frase-chave e localidade. O aplicativo pode optar por usar o Software Hotword Detection .
  • STATE_HARDWARE_ENROLLED . A detecção de hot word está pronta e pode ser iniciada chamando o método startRecognition() .
  • STATE_HARDWARE_UNENROLLED . Um modelo de som para a frase-chave solicitada não está disponível, mas o registro é 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 será associado a um AlwaysOnHotwordDetector . A detecção de hotword DSP pode não estar disponível em todos os dispositivos. Os desenvolvedores VIA devem verificar os recursos de hardware usando o método getDspModuleProperties() . Para obter um código de amostra que mostra como registrar modelos de som, consulte VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java . Consulte Captura simultânea sobre reconhecimento de hotword simultâneo.

Detecção de Hotword de Software

Conforme indicado acima, a detecção de hotword DSP pode não estar disponível em todos os dispositivos (por exemplo, o emulador Android não fornece emulação DSP). Nesse caso, o reconhecimento de voz por software é a única alternativa. Para evitar interferência em outros aplicativos que possam precisar de acesso ao microfone, os VIAs devem acessar a entrada de áudio usando:

Ambas as constantes são @hide e estão disponíveis apenas para aplicativos agrupados.

Gerenciando entrada de áudio e reconhecimento de voz

A entrada de áudio seria implementada usando a API MediaRecorder . Para obter mais informações sobre como usar essa API, consulte a Visão geral do MediaRecorder . Espera-se também que os serviços de interação de voz sejam implementações do RecognitionService . Qualquer aplicativo no sistema que exija reconhecimento de voz usará a API SpeechRecognizer para acessar esse recurso. Para fazer o reconhecimento de voz e ter acesso ao microfone, as VIAs devem possuir android.permission.RECORD_AUDIO . Espera-se que os aplicativos que acessam uma implementação RecognitionService também detenham essa permissão.

Antes do Android 10, o acesso ao microfone era concedido a apenas um aplicativo por vez (com exceção da detecção de hotword, veja acima). A partir do Android 10, o acesso ao microfone pode ser compartilhado. Para obter mais informações, consulte Compartilhamento de entrada de áudio .

Acessando a saída de áudio

Quando o VIA estiver pronto para fornecer respostas verbais, é importante seguir este próximo conjunto de diretrizes: