Usando o exemplo do Habitica (um aplicativo de código aberto para corrigir hábitos e atingir metas, escrito em Kotlin) Vitalya Gorbachev, arquiteto de soluções da Just AI, mostra como integrar de maneira rápida e transparente uma interface de voz à funcionalidade de qualquer aplicativo.

Mas, primeiro, vamos discutir por que o controle de voz de um aplicativo móvel é conveniente. Vamos começar com o óbvio.
- Freqüentemente, precisamos usar um aplicativo quando nossas mãos estão ocupadas: cozinhando, dirigindo, carregando malas, durante o trabalho mecânico e assim por diante.
- A voz é uma ferramenta essencial para pessoas com deficiência visual.
Os casos já são transparentes, mas na realidade tudo é ainda mais simples: em alguns casos, a marcação por voz é apenas mais rápida ! Imagine - encomendar uma passagem aérea com uma frase "Compre-me uma passagem amanhã para dois para Samara" em vez de preencher um formulário longo. Ao mesmo tempo, com a possibilidade de fazer perguntas esclarecedoras ao usuário: à noite ou à tarde? com ou sem bagagem?
A voz é útil quando passamos pelo cenário de "preenchimento de formulários" e é conveniente para preencher quase todos os formulários longos que requerem uma certa quantidade de informações do usuário. E essas formas estão presentes na maioria dos aplicativos móveis.

Da esquerda para a direita: aplicativo Prigorod Russian Railways, diário alimentar FatSecret (os usuários precisam preencher um formulário várias vezes ao dia, escolhendo entre centenas de produtos), aplicativo de padaria Korzhov.
Devido ao fato de que hoje os assistentes de voz são frequentemente introduzidos no chat de suporte e se desenvolvem a partir daí, a maioria das empresas está tentando empurrar a funcionalidade do aplicativo para o chat. Completar o saldo, descobrir algo sobre um produto ou serviço ... Isso nem sempre é implementado de forma conveniente e, no caso de entrada de voz, é totalmente contraproducente, até porque o reconhecimento de voz muitas vezes não funciona perfeitamente.
A abordagem correta é integrar o assistente perfeitamente à funcionalidade existente do aplicativo, na interface em que o formulário será preenchido, para que a pessoa possa simplesmente verificar se disse tudo corretamente e clicar em OK.Decidimos mostrar como isso pode ser feito usando o exemplo do Habitica - este é um aplicativo de código aberto escrito em Kotlin quase puro. "Habitika" é perfeito para um caso com um assistente de voz - aqui, também, para iniciar uma nova tarefa, você precisa preencher um formulário bastante volumoso. Vamos tentar substituir esse processo enfadonho por uma frase com perguntas indutoras?
Eu dividi o tutorial em duas partes. Neste artigo, descobriremos como adicionar um assistente de voz a um aplicativo móvel e implementar um cenário básico (em nosso caso, este é um cenário pronto para esclarecer a previsão do tempo e do tempo - uma das solicitações mais populares para assistentes de voz no mundo). No segundo artigo - que será lançado em breve - aprenderemos como chamar certas telas por voz e implementar consultas complexas dentro do aplicativo.
O que você precisa para trabalhar
SDK. Usamos Aimybox como um SDK para construir interfaces de diálogo. Fora da caixa, Aimybox fornece um SDK assistente e uma interface de usuário lacônica e personalizável (que pode ser alterada se desejado). Ao mesmo tempo , você pode escolher entre os existentes ou criar seu próprio módulo como mecanismo de reconhecimento , síntese e PNL .
Basicamente, Aimybox implementa a arquitetura do assistente de voz, padronizando as interfaces de todos esses módulos e organizando sua interação da maneira certa. Assim, ao implementar essa solução, você pode reduzir significativamente o tempo de desenvolvimento de uma interface de voz em seu aplicativo. Você pode ler mais sobre o Aimybox aqui ouaqui .
Ferramenta de criação de scripts. Vamos escrever o script em JAICF (este é um framework de código aberto e totalmente gratuito para o desenvolvimento de aplicativos de voz do Just AI), e vamos reconhecer intents usando Caila (serviço NLU) em JAICP (Just AI Conversational Platform). Contarei mais sobre eles na próxima parte do tutorial - quando começarmos a usá-los.
Smartphone. Para os testes, precisamos de um smartphone Android, no qual iremos rodar e testar o Habitika.
Procedimento
Primeiro, bifurcamos "Habitika" (o branch Release) e procuramos os arquivos que são mais importantes para nós. Usei o IDE do Android Studio:
Encontre MainActivity .kt - vamos incorporar a lógica lá.
HabiticaBaseApplication .kt - lá iremos inicializar o Aimybox.
Activity_main .xml - incorpora o elemento de interface lá.
AndroidManifest .xml - toda a estrutura do aplicativo e suas permissões são armazenadas lá.
De acordo com as instruções do nabo "Habitiki", renomeamos habitica.properties.example e habitica.resources.example, removendo example deles, iniciar o projeto no firebase para o aplicativo e copiar o arquivo google-services.json para a raiz.
Lançamos o aplicativo para verificar se a montagem está funcionando. Voila!

Primeiro, vamos adicionar as dependências do Aimybox.
implementation 'com.justai.aimybox:core:0.11.0'
implementation("com.justai.aimybox:components:0.1.8")
nas dependências e
maven { url 'https://dl.bintray.com/aimybox/aimybox-android-sdk/' }
maven { url "https://dl.bintray.com/aimybox/aimybox-android-assistant/" }
em repositórios.
E adicione a seguinte linha logo após compileOptions para que tudo funcione corretamente
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
Agora, permissões.
Remover bandeiras de RECORD_AUDIO e MODIFY_AUDIO_SETTINGS permissões em AndroidManifest .xml de modo que as opções de olhar como este.
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>
Agora vamos inicializar o Aimybox em BaseApplication.
Adicione AimyboxProvider ao inicializar a classe.

E fazemos a inicialização real.
private fun createAimybox (context: Context): Aimybox {
val unitId = UUID.randomUUID().toString()
val textToSpeech = GooglePlatformTextToSpeech(context, Locale("Ru"))
val speechToText = GooglePlatformSpeechToText(context, Locale("Ru"))
val dialogApi = AimyboxDialogApi(
"YOUR KEY", unitId)
return Aimybox(Config.create(speechToText, textToSpeech, dialogApi))
}
Em vez de YOUR_KEY, seu código do console Aimybox será posteriormente.
Agora incorporamos o snippet em mainActivity.kt. Pré-insira o FrameLayout em activity_main.xml, logo abaixo do frameLayout com id bottom_navigation
<FrameLayout
android:id="@+id/assistant_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Na própria MainActivity, primeiro adicione uma solicitação de permissão explícita para OnCreate
ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.RECORD_AUDIO), 1)
E quando você os receber, adicione um fragmento ao quadro acima.
@SuppressLint("MissingPermission")
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
val fragmentManager = supportFragmentManager
val fragmentTransaction = fragmentManager.beginTransaction()
fragmentTransaction.add(R.id.assistant_container, AimyboxAssistantFragment())
fragmentTransaction.commit()
}
Não se esqueça de adicionar ao OnBackPressed a capacidade de sair do assistente após entrar nele.
val assistantFragment = (supportFragmentManager.findFragmentById(R.id.assistant_container)
as? AimyboxAssistantFragment)
if (assistantFragment?.onBackPressed() != true) {
return
}
Além disso, adicione aos estilos (styles.xml) no AppTheme
<item name="aimybox_assistantButtonTheme">@style/CustomAssistantButtonTheme</item>
<item name="aimybox_recognitionTheme">@style/CustomRecognitionWidgetTheme</item>
<item name="aimybox_responseTheme">@style/CustomResponseWidgetTheme</item>
<item name="aimybox_imageReplyTheme">@style/CustomImageReplyWidgetTheme</item>
<item name="aimybox_buttonReplyTheme">@style/CustomButtonReplyWidgetTheme</item>
E os estilos individuais estão logo abaixo:
<style name="CustomAssistantButtonTheme" parent="DefaultAssistantTheme.AssistantButton">
</style>
<style name="CustomRecognitionWidgetTheme" parent="DefaultAssistantTheme.Widget.Recognition">
</style>
<style name="CustomResponseWidgetTheme" parent="DefaultAssistantTheme.Widget.Response">
</style>
<style name="CustomButtonReplyWidgetTheme" parent="DefaultAssistantTheme.Widget.ButtonReply">
</style>
<style name="CustomImageReplyWidgetTheme" parent="DefaultAssistantTheme.Widget.ImageReply">
</style>
Vamos verificar se um microfone foi adicionado. Lançamos o aplicativo.
Encontramos vários erros de sintaxe incorreta. Corrigimos tudo conforme o IDE aconselha.
Trabalhando!

Mas o microfone está se infiltrando na navegação inferior. Vamos aumentar um pouco. Adicione aos estilos acima no CustomAssistantButtonTheme:
<item name="aimybox_buttonMarginBottom">72dp</item>
Melhor!

Agora vamos conectar um assistente aí e verificar se ele responde normalmente. Para isso, precisamos do console Aimybox.
Vamos começar acessando app.aimybox.com em nossa conta no github, fazer um novo projeto, conectar algumas habilidades (conectei DateTime para o teste) e tentar fazer as perguntas apropriadas no assistente. Aqui nas configurações, no canto superior direito, pegamos a apiKey, que inserimos em createAimybox ao invés de YOUR KEY.
private fun createAimybox (context: Context): Aimybox {
val unitId = UUID.randomUUID().toString()
val textToSpeech = GooglePlatformTextToSpeech(context)
val speechToText = GooglePlatformSpeechToText(context)
val dialogApi = AimyboxDialogApi(
"YOUR KEY", unitId)
return Aimybox(Config.create(speechToText, textToSpeech, dialogApi))
}
Trabalhando!

Apenas texto em inglês, vamos alterar a mensagem de boas-vindas em strings.constants.xml.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Prefs -->
<string name="SP_userID" translatable="false">UserID</string>
<string name="SP_APIToken" translatable="false">APIToken</string>
<string name="base_url" translatable="false">https://habitica.com</string>
<string name="initial_phrase">"! ?</string>
Hooray!

Aqui está um link para o repositório de código.
No próximo artigo sobre o assistente de "Habitika", direi como usar sua voz não apenas para saber o tempo, mas para controlar o aplicativo diretamente - navegar pelas páginas e adicionar hábitos e tarefas.