Am Beispiel von Habitica (einer Open-Source-App zum Festlegen von Gewohnheiten und zum Erreichen von Zielen, geschrieben in Kotlin) zeigt Vitala Gorbachev, Lösungsarchitektin bei Just AI, wie eine Sprachschnittstelle schnell und nahtlos in die Funktionalität jeder Anwendung integriert werden kann.

Lassen Sie uns zunächst diskutieren, warum die Sprachsteuerung einer mobilen Anwendung bequem ist. Beginnen wir mit dem Offensichtlichen.
- Wir müssen oft eine Anwendung verwenden, wenn unsere Hände beschäftigt sind: Kochen, Fahren, Tragen von Koffern, während mechanischer Arbeiten und so weiter.
- Die Stimme ist ein wesentliches Instrument für Menschen mit Sehbehinderungen.
Die Fälle sind bereits transparent, aber in Wirklichkeit ist alles noch einfacher: In einigen Fällen ist die Sprachwahl nur schneller ! Stellen Sie sich vor, Sie bestellen ein Flugticket mit dem Satz "Kaufen Sie mir ein Ticket für morgen für zwei nach Samara" anstelle eines langen Formulars. Gleichzeitig mit der Möglichkeit, dem Benutzer klärende Fragen zu stellen: abends oder nachmittags? mit oder ohne Gepäck?
Die Stimme ist nützlich, wenn wir das Szenario "Ausfüllen von Formularen" durchlaufen, und eignet sich zum Ausfüllen fast aller langen Formulare, für die eine bestimmte Menge an Informationen vom Benutzer erforderlich ist. Und solche Formulare sind in den meisten mobilen Anwendungen vorhanden.

Von links nach rechts: Prigorod RZD App, FatSecret Food Diary (Benutzer müssen mehrmals täglich ein Formular ausfüllen und aus Hunderten von Produkten auswählen), Korzhov Bäckerei App.
Aufgrund der Tatsache, dass Sprachassistenten heutzutage häufig in den Support-Chat eingeführt werden und sich von dort aus entwickeln, versuchen die meisten Unternehmen, die Funktionalität der Anwendung in den Chat zu übertragen. Füllen Sie die Waage auf, finden Sie etwas über ein Produkt oder eine Dienstleistung heraus ... Dies ist nicht immer bequem zu implementieren und bei Spracheingaben völlig kontraproduktiv, schon allein deshalb, weil die Spracherkennung oft nicht perfekt funktioniert.
Der richtige Ansatz besteht darin, den Assistenten nahtlos in die vorhandene Funktionalität der Anwendung zu integrieren, in deren Benutzeroberfläche das Formular ausgefüllt wird, sodass die Person einfach überprüfen kann, ob sie alles richtig gesagt hat, und auf OK klicken kann.Wir haben uns entschlossen zu zeigen, wie dies am Beispiel von Habitica möglich ist - dies ist eine Open-Source-Anwendung, die in fast reinem Kotlin geschrieben ist. "Habitika" ist perfekt für einen Fall mit einem Sprachassistenten - auch hier müssen Sie ein ziemlich umfangreiches Formular ausfüllen, um eine neue Aufgabe zu starten. Versuchen wir, diesen trostlosen Prozess durch einen Satz mit Leitfragen zu ersetzen.
Ich habe das Tutorial in zwei Teile geteilt. In diesem Artikel erfahren Sie, wie Sie einer mobilen Anwendung einen Sprachassistenten hinzufügen und ein Basisszenario implementieren (in unserem Fall ist dies ein vorgefertigtes Szenario zur Klärung der Wetter- und Zeitvorhersage - eine der beliebtesten Anfragen für Sprachassistenten weltweit). Im zweiten Artikel - der bald veröffentlicht wird - erfahren Sie, wie Sie bestimmte Bildschirme per Spracheingabe aufrufen und komplexe Abfragen in der Anwendung implementieren.
Was Sie brauchen, um zu arbeiten
SDK. Wir haben Aimybox als SDK zum Erstellen von Dialogschnittstellen verwendet. Standardmäßig bietet Aimybox ein Assistenten- SDK und eine lakonische und anpassbare Benutzeroberfläche (die bei Bedarf geändert werden kann). Gleichzeitig können Sie aus vorhandenen auswählen oder ein eigenes Modul als Engine für Erkennung , Synthese und NLP erstellen.
Grundsätzlich implementiert Aimybox die Voice Assistant-Architektur, standardisiert die Schnittstellen all dieser Module und organisiert ihre Interaktion auf die richtige Weise. Durch die Implementierung dieser Lösung können Sie die Zeit für die Entwicklung einer Sprachschnittstelle in Ihrer Anwendung erheblich verkürzen. Weitere Informationen zu Aimybox finden Sie hier oderhier .
Skript-Erstellungstool. Wir werden das Skript in JAICF schreiben (dies ist ein Open Source und ein völlig kostenloses Framework für die Entwicklung von Sprachanwendungen aus Just AI) und wir werden Absichten erkennen, die Caila (NLU-Dienst) in JAICP (Just AI Conversational Platform) verwenden. Ich werde Ihnen im nächsten Teil des Tutorials mehr darüber erzählen - wenn wir sie verwenden.
Smartphone. Für die Tests benötigen wir ein Android-Smartphone, auf dem wir Habitika ausführen und testen.
Verfahren
Zuerst gabeln wir "Habitika" (Release-Zweig) und suchen nach den Dateien, die für uns am wichtigsten sind. Ich habe die Android Studio IDE:
Find MainActivity .kt verwendet - wir werden dort Logik einbetten.
HabiticaBaseApplication .kt - dort werden wir Aimybox initialisieren.
Activity_main .xml - bettet dort das Schnittstellenelement ein.
AndroidManifest .xml - Die gesamte Struktur der Anwendung und ihre Berechtigungen werden dort gespeichert.
Gemäß den Anweisungen in der Rübe "Habitiki" benennen wir Habitica.properties.example und Habitica.resources.example um, entfernen Beispiele daraus, starten das Projekt in Firebase für die Anwendung und kopieren die Datei google-services.json in das Stammverzeichnis.
Wir starten die Anwendung, um zu überprüfen, ob die Assembly funktioniert. Voila!

Fügen wir zunächst die Aimybox-Abhängigkeiten hinzu.
implementation 'com.justai.aimybox:core:0.11.0'
implementation("com.justai.aimybox:components:0.1.8")
in Abhängigkeiten und
maven { url 'https://dl.bintray.com/aimybox/aimybox-android-sdk/' }
maven { url "https://dl.bintray.com/aimybox/aimybox-android-assistant/" }
in Repositories.
Fügen Sie direkt nach compileOptions die folgende Zeile hinzu, damit alles ordnungsgemäß funktioniert
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
Jetzt Berechtigungen.
Entfernen Sie Flags aus den Berechtigungen RECORD_AUDIO und MODIFY_AUDIO_SETTINGS in AndroidManifest .xml, damit die Optionen so aussehen.
<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"/>
Lassen Sie uns nun Aimybox in BaseApplication initialisieren.
Fügen Sie AimyboxProvider hinzu, wenn Sie die Klasse initialisieren.

Und wir machen die eigentliche Initialisierung.
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))
}
Anstelle von YOUR_KEY wird anschließend Ihr Code aus der Aimybox-Konsole angezeigt.
Jetzt binden wir das Snippet in mainActivity.kt ein. Fügen Sie das FrameLayout in activity_main.xml direkt unter dem frameLayout mit der ID bottom_navigation vor
<FrameLayout
android:id="@+id/assistant_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Fügen Sie in MainActivity selbst zunächst eine explizite Berechtigungsanforderung zu OnCreate hinzu
ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.RECORD_AUDIO), 1)
Und wenn Sie sie erhalten, fügen Sie dem obigen Rahmen ein Fragment hinzu.
@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()
}
Vergessen Sie nicht, OnBackPressed die Möglichkeit hinzuzufügen, den Assistenten nach dem Aufrufen zu beenden.
val assistantFragment = (supportFragmentManager.findFragmentById(R.id.assistant_container)
as? AimyboxAssistantFragment)
if (assistantFragment?.onBackPressed() != true) {
return
}
Fügen Sie außerdem Stile (styles.xml) in AppTheme hinzu
<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>
Und die einzelnen Stile sind gleich unten:
<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>
Lassen Sie uns überprüfen, ob ein Mikrofon hinzugefügt wurde. Wir starten die Anwendung.
Wir haben eine Reihe von Fehlern bezüglich falscher Syntax erhalten. Wir reparieren alles wie von der IDE empfohlen.
Arbeiten!

Aber das Mikrofon kriecht in die untere Navigation. Lassen Sie uns ein wenig erhöhen. Fügen Sie den obigen Stilen im CustomAssistantButtonTheme hinzu:
<item name="aimybox_buttonMarginBottom">72dp</item>
Besser!

Lassen Sie uns nun einen Assistenten dort anschließen und prüfen, ob er normal antwortet. Dafür benötigen wir die Aimybox-Konsole.
Beginnen wir damit, unter unserem Github-Konto auf app.aimybox.com zu gehen, ein neues Projekt zu erstellen, einige Fähigkeiten zu verbinden (ich habe DateTime für den Test verbunden) und zu versuchen, die entsprechenden Fragen im Assistenten zu stellen. Hier in den Einstellungen in der oberen rechten Ecke nehmen wir den apiKey, den wir anstelle von IHREM SCHLÜSSEL in createAimybox einfügen.
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))
}
Arbeiten!

Nur englischer Text, ändern wir die Begrüßungsnachricht in 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>
Hurra!

Hier ist ein Link zum Code-Repository.
Im nächsten Artikel über einen Assistenten für "Habitika" werde ich Ihnen erklären, wie Sie mit Ihrer Stimme nicht nur das Wetter herausfinden, sondern auch die Anwendung direkt steuern können - durch die Seiten navigieren und Gewohnheiten und Aufgaben hinzufügen.