Desenvolvimento de aplicativos

Para implementar um Aplicativo de Interação de 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 IU de placa de voz.
  6. Implemente o reconhecimento de voz (deve incluir a implementação da API RecognitionService).
  7. Implemente a expressão (opcionalmente, você pode implementar a API TextToSpeech).
  8. Implementar o cumprimento de comandos. Veja este conteúdo em Cumprindo Comandos .

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

Crie um esqueleto VIA

Manifestos

Um aplicativo é detectado como um aplicativo 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 devem expor um serviço que estenda VoiceInteractionService , com um filtro de intenção 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 contendo 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 . Dado que 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

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

  • Inicialize quaisquer processos que devam ser mantidos em execução enquanto este VIA estiver ativo. Por exemplo, detecção de hotword.
  • Relata ações de voz suportadas (consulte Toque para ler do Voice Assistant ).
  • Inicie sessões de interação por voz na tela de bloqueio (proteção de teclado).

Na sua forma mais simples, uma implementação do 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 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 realizada. Uma única instância de sessão pode ser reutilizada para concluir diversas 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 possui um grande conjunto de métodos de retorno de chamada que são explicados nas seções a seguir. consulte a documentação de VoiceInteractionSession para obter 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 aplicativo é selecionado.

Para obter detalhes sobre a experiência do usuário recomendada e orientação visual, consulte Assistentes pré-carregados: orientação UX .

Configuração durante a troca de serviço de voz

Sempre é possível ao usuário selecionar um VIA que não tenha sido configurado corretamente. Isso pode acontecer porque:

  • O usuário ignorou totalmente o Assistente de Configuração ou ignorou a etapa de configuração da interação por voz.
  • O usuário selecionou um VIA diferente daquele configurado 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 automática de voz quando o usuário tenta usá-lo.

Nota : É fortemente 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 de usuário.

Lembrete de notificação

Um lembrete de notificação é uma forma não intrusiva de indicar a necessidade de configuração e de fornecer aos usuários a oportunidade de navegar 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

Resposta de voz

Este é o fluxo mais simples de implementar, iniciando uma expressão em um retorno de chamada VoiceInteractionSession#onShow() , explicando ao usuário o que precisa ser feito e, em seguida, perguntando a ele (se a configuração é permitida, dado o estado de restrição UX) se ele deseja iniciar o fluxo de configuração. Se a configuração não for possível no momento, explique também esta situação.

Configuração na primeira utilização

Sempre é possível que o usuário acione um VIA que não tenha sido configurado corretamente. Em tais casos:

  1. Informar verbalmente o usuário sobre esta situação (por exemplo, “Para funcionar corretamente, preciso que você execute algumas etapas…”).
  2. Se o mecanismo de restrições de UX permitir (consulte UX_RESTRICTIONS_NO_SETUP ), pergunte ao usuário se deseja iniciar o processo de configuração e, em seguida, abra a tela Configurações do VIA.
  3. Caso contrário (por exemplo, se o usuário estiver dirigindo), deixe uma notificação para que o usuário clique na opção quando for seguro fazê-lo.

Crie telas de configuração de interação por 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 .

Diretrizes gerais:

  • Os 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 para 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 IU. 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 regulares do Android. Se implementado, seu ponto de entrada deverá ser declarado em 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 fazer login (se o usuário não tiver concluído) ou oferecer uma opção de sair ou trocar de usuário, se necessário. Semelhante às telas de configuração descritas acima, essas telas devem:

  • Forneça a opção de retornar à 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 para 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 um 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 privilegiadas do sistema .
  • Permissões perigosas. Estas são permissões que um usuário deve conceder usando a caixa de diálogo PermissionsController. Os OEMs podem pré-conceder algumas dessas permissões ao VoiceInteractionService padrão. Mas dado que este padrão pode mudar de dispositivo para dispositivo, os aplicativos devem ser capazes de solicitar essas permissões quando necessário.
  • Outras permissões. Estas são todas as outras permissões que não requerem intervenção do usuário. Essas permissões são concedidas automaticamente pelo sistema.

Diante do exposto, a seção a seguir concentra-se 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 de configuração.

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

Solicite 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 de aplicativos .

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 ao sistema enviar notificações aos ouvintes registrados. Para saber se o VIA teve acesso a essas informações, os aplicativos podem:

Se esse acesso não for pré-concedido, o VIA deverá direcionar o usuário para a seção Acesso à Notificação das Configurações do Carro, usando uma combinação de declarações 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 de placa de voz

Quando uma VoiceInteractionSession recebe um retorno de chamada onShow() , ela pode apresentar uma UI de placa de voz. Para obter diretrizes visuais e de UX sobre a implementação de placas de voz, consulte Assistentes pré-carregados: orientação de UX .

Exibindo a placa de voz

Figura 6. Exibindo a placa de voz

Existem duas opções sobre como implementar esta IU:

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

Usar onCreateContentView()

Esta é a forma padrão de apresentar uma placa de voz. A classe base VoiceInteractionSession cria uma janela e gerencia seu ciclo de vida enquanto uma sessão de voz estiver ativa. Os aplicativos devem substituir VoiceInteractionSession#onCreateContentView() e retornar uma visualização anexada a essa janela assim que a sessão for criada. Esta visão deve inicialmente ser invisível. Quando uma interação de voz é iniciada, essa visualização deve ficar visível em VoiceInteractionSession#onShow() e, em seguida, 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, você pode querer ajustar VoiceInteractionSession#onComputeInsets() para levar em conta regiões obscuras de sua IU.

Usar startAssistantActivity()

Nesse caso, VoiceInteractionSession delega o manuseio da UI da placa de voz para uma atividade regular. Quando esta opção é usada, uma implementação 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 atividade da placa de voz usando VoiceInteractionSession#startAssistantActivity() . Este método inicia a UI com as configurações de janela e sinalizadores de atividade adequados.

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 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 ser capaz de passar esta solicitação para a atividade.

Importante. No setor automotivo, apenas atividades especialmente anotadas ou 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 esta atividade na chave systemActivityWhitelist do /packages/services/Car/service/res/values/config.xml . Para obter mais informações, consulte Diretrizes para distração do motorista .

Implementar reconhecimento de voz

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

Detecção de hotword DSP

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

A implementação do 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 recebe um retorno de chamada onAvailabilityChanged() com um dos seguintes valores possíveis:

  • STATE_HARDWARE_UNAVAILABLE . A capacidade DSP não está disponível no dispositivo. Neste caso, a detecção de hotword de software é usada.
  • STATE_HARDWARE_UNSUPPORTED . O suporte DSP não está disponível em geral, mas o DSP não oferece suporte a determinadas combinações de frase-chave e localidade. O aplicativo pode optar por usar Software Hotword Detection .
  • STATE_HARDWARE_ENROLLED . A detecção de palavras quentes está pronta e pode ser iniciada chamando o método startRecognition() .
  • STATE_HARDWARE_UNENROLLED . Um modelo adequado 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 está associado a 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 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 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). Neste caso, o reconhecimento de voz por software é a única alternativa. Para evitar interferir com 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 incluídos.

Gerenciar entrada de áudio e reconhecimento de voz

A entrada de áudio seria implementada usando a classe MediaRecorder . Para obter mais informações sobre como usar esta API, consulte Visão geral do MediaRecorder . Espera-se também que os serviços de interação por voz sejam implementações da classe RecognitionService . Qualquer aplicativo no sistema que exija reconhecimento de voz usa o para acessar esse recurso. Para fazer reconhecimento de voz e ter acesso ao microfone, os VIAs devem conter android.permission.RECORD_AUDIO . Espera-se que os aplicativos que acessam uma implementação RecognitionService também tenham 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 Compartilhando entrada de áudio .

Acesse a saída de áudio

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