Desenvolvimento de aplicações

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/entrada.
  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 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 de 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 estende 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 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 . 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

Como afirmado 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 este VIA estiver ativo. Por exemplo, detecção de hotword.
  • Relata ações de voz suportadas (consulte Toque para ler do assistente de voz ).
  • Inicie sessões de interação por voz na tela de bloqueio (keyguard).

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 assistente de voz Tap-to-Read . Um VoiceInteractionSessionService é usado pelo sistema para criar e interagir com um VoiceInteractionSession . Ele só tem 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 feito. Uma única instância de sessão pode ser reutilizada para concluir várias interações do usuário. No AAOS, existe um helper CarVoiceInteractionSession , ajudando a implementar algumas das funcionalidades exclusivas automotivas.

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. veja a documentação para VoiceInteractionSession uma lista completa.

Implementar um fluxo de configuração/entrada

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 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 que o usuário selecione 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 o onboarding do equipamento.

De qualquer forma, um VoiceInteractionService tem várias maneiras de encorajar 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 funcionalidade para 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

Note : dependendo das regras de restrição de UX (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 IU 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, em seguida, perguntando (se a configuração for permitida devido ao 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 um VIA que não tenha sido configurado corretamente. Em tais casos:

  1. Informe verbalmente o usuário sobre esta situação (por exemplo, "Para funcionar corretamente, preciso que você conclua algumas etapas … ").
  2. Se o mecanismo de restrições 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.

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

As telas de configuração e login devem ser desenvolvidas como atividades regulares . Consulte a UX e as diretrizes visuais para o desenvolvimento da interface do usuário em Assistentes pré-carregados: Orientação de UX .

Diretrizes 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. 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 regulares do Android. Se implementado, seu ponto de entrada deve 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 entrar (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 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 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 do sistema. Para obter mais informações sobre como obter permissões de assinatura, consulte Conceder permissões com privilégios de sistema .
  • Permissões perigosas. Estas são as permissões que um usuário deve conceder usando a caixa de diálogo PermissionsController. Os OEMs podem pré-conceder algumas dessas permissões para o 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 requerem intervenção do usuário. Essas permissões serão concedidas automaticamente pelo sistema.

Diante do 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 um recurso 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 de aplicativos .

Solicitar permissões

Figura 5. Solicitar permissões

Permissão do 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 cadastrados. Para saber se o VIA teve acesso a essas informações, os aplicativos podem:

Se esse acesso não for pré-concedido, o VIA deve direcionar o usuário para a seção Notification Access 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 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 interface de usuário do Voice Plate

Quando uma VoiceInteractionSession recebe um retorno de chamada onShow() , ela pode apresentar uma IU 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 sobre como implementar essa IU:

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

Usando onCreateContentView()

Esta é a forma 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 exibição que será 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 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 contabilizar regiões obscuras de sua interface do usuário.

Usando startAssistantActivity()

Nesse caso, a VoiceInteractionSession delegará a manipulação da IU da placa de voz para uma Activity 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() . Esse método inicia a interface do usuário com as configurações de janela adequadas e 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 Intenções internas ou vinculação de serviço. Por exemplo, quando VoiceInteractionSession#onHide() é chamado, a sessão deve ser capaz de passar essa solicitação para a atividade.

Importante. No Automotive, apenas as atividades anotadas especialmente ou as 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 esta atividade na chave systemActivityWhitelist do 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á como 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 "Hey 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 CPU baixa. A utilização desta funcionalidade está 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 eles 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 . A capacidade DSP não está disponível no dispositivo. Nesse caso, a detecção de hotword de software será usada.
  • STATE_HARDWARE_UNSUPPORTED . O suporte a DSP não está disponível em geral, mas o DSP não oferece suporte a determinada frase-chave e combinação de 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 a inscrição é possível.

O registro de modelos de som de detecção de hotword pode ser feito usando IVoiceInteractionManagerService#updateKeyphraseSoundModel() . Vários modelos podem ser cadastrados 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 o código de exemplo que mostra como registrar modelos de som, consulte VoiceEnrollment/src/com/android/test/voiceenrollment/EnrollmentUtil.java . Consulte Captura simultânea em relação ao reconhecimento de hotwords simultâneos.

Software Detecção de Hotword

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 software de reconhecimento de voz é 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 por voz sejam implementações 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, 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 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: