如何将语音助手嵌入任何移动应用程序。我们使用Habitica的示例进行拆卸

您是否认为如果具有语音控制功能,许多移动应用程序会更加方便吗?不,这与在技术支持聊天中与银行助理进行对话无关。基本上,通过应用程序进行语音导航或以对话模式填写表格就足够了。



Just AI的解决方案架构师Vitalya Gorbachev以Habitica(用于养成习惯和实现目标的开源应用程序,以Kotlin编写)为例,展示了如何将语音界面快速无缝地集成到任何应用程序的功能中。





但是首先,让我们讨论一下为什么移动应用程序的语音控制很方便?让我们从显而易见的地方开始。



  • 我们经常在忙碌的时候使用该应用程序:做饭,开车,搬运行李箱,机械作业等。
  • 语音是视障人士的必备工具。


情况已经是透明的,但实际上一切都更加简单:在某些情况下,语音拨号只是更快想象一下-订购一张带有“给我买一张明天要去萨马拉的两张明天的机票”的短语,而不是一张长表格。同时,可以询问用户澄清问题:在晚上还是在下午?有没有行李?

当我们经历“表单填充”场景时,语音非常有用,并且对于填写几乎任何需要用户提供一定数量信息的长表单都很方便。大多数移动应用程序中都存在此类形式。






从左至右:Prigorod RZD应用程序,FatSecret食品日记(用户每天必须多次填写表格,从数百种产品中进行选择),Korzhov面包店应用程序。



由于当今常常将语音助手引入支持聊天中,并且从那里开始发展,因此大多数公司都在尝试将应用程序的功能推入聊天中。充裕地平衡,找到有关产品或服务的信息...这并不总是很方便地实现,并且在语音输入的情况下,这仅仅是适得其反,即使仅仅是因为语音识别通常不能完美地起作用。

正确的方法是将助手无缝地集成到应用程序的现有功能中,在该功能中将填写表单,以便该人员可以简单地检查自己是否已正确说了一切,然后单击“确定”。
我们决定以Habitica为例说明如何做到这一点-这是一个几乎用纯Kotlin编写的开源应用程序。 “ Habitika”非常适合带有语音助手的案件-在这里,为了开始一项新任务,您还需要填写大量表格。让我们尝试用一个带有主要问题的短语代替这个沉闷的过程吗?



我已将本教程分为两部分。在本文中,我们将探讨如何在移动应用程序中添加语音助手并实现基本方案(在我们的案例中,这是一个用于澄清天气和时间预报的现成方案-世界上最流行的语音助手要求之一)。在第二篇文章(即将发布)中,我们将学习如何通过语音调用某些屏幕以及如何在应用程序内实现复杂的查询。



你需要做什么



SDK。我们将Aimybox用作构建对话框界面的SDK。开箱即用,Aimybox提供了一个辅助SDK和简洁的可定制UI(可以根据需要更改)。同时您可以从现有模块中进行选择也可以创建自己的模块作为识别综合NLP的引擎



基本上,Aimybox实现了语音助手的体系结构,标准化了所有这些模块的接口,并以正确的方式组织了它们的交互。因此,通过实施此解决方案,您可以大大减少在应用程序中开发语音接口的时间。您可以在此处阅读有关Aimybox的更多信息在这里



脚本创建工具。我们将使用JAICF(这是一个用于从Just AI开发语音应用程序的开源且完全免费的框架)编写脚本,并且我们将JAICP(仅AI对话平台)中使用Caila(NLU服务)来识别意图在本教程的下一部分中-当我们开始使用它们时,我将详细介绍它们。手机。对于测试,我们需要一个Android智能手机,我们将在其上运行和测试Habitika。







程序



首先,我们分叉“ Habitika”(发行分支)并查找对我们最重要的文件。我使用了Android Studio IDE:



查找MainActivity .kt-我们将在其中嵌入逻辑。



HabiticaBaseApplication .kt-我们将在其中初始化Aimybox。



Activity_main .xml-在其中嵌入接口元素。



AndroidManifest .xml-应用程序的整个结构及其权限存储在此处。



根据“ Habitiki”萝卜中的说明,我们将habitica.properties.example和habitica.resources.example重命名,从中删除示例,在firebase中启动该应用程序的项目,并将google-services.json文件复制到根目录。



我们启动该应用程序以检查程序集是否正常工作。瞧!



图片



首先,让我们添加Aimybox依赖项。



implementation 'com.justai.aimybox:core:0.11.0'
    implementation("com.justai.aimybox:components:0.1.8")


在依赖和



    maven { url 'https://dl.bintray.com/aimybox/aimybox-android-sdk/' }
    maven { url "https://dl.bintray.com/aimybox/aimybox-android-assistant/" }
 


在存储库中。



并在compileOptions之后添加以下行,以便一切正常



    kotlinOptions {
        jvmTarget = JavaVersion.VERSION_1_8.toString()
    }


现在的权限。



AndroidManifest .xml中的RECORD_AUDIOMODIFY_AUDIO_SETTINGS权限删除标志,以便选项如下所示。




    <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"/>


现在,让我们在BaseApplication中初始化Aimybox。初始化类时



添加AimyboxProvider



图片



然后我们进行实际的初始化。



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))
    }


随后,您将从Aimybox控制台获取的代码代替YOUR_KEY。



现在,我们将片段嵌入mainActivity.kt中。在ID为bottom_navigation的frameLayout的正下方预插入Activity_main.xml中的FrameLayout



<FrameLayout
                                android:id="@+id/assistant_container"
                                android:layout_width="match_parent"
                                android:layout_height="match_parent"/>


在MainActivity本身中,首先向OnCreate添加一个明确的权限请求

        ActivityCompat.requestPermissions(this, arrayOf(android.Manifest.permission.RECORD_AUDIO), 1)


当您收到它们时,在上面的框架中添加一个片段。



    @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()
    }


不要忘了添加到OnBackPressed,在输入助手后即可退出助手。



        val assistantFragment = (supportFragmentManager.findFragmentById(R.id.assistant_container)
                as? AimyboxAssistantFragment)
        if (assistantFragment?.onBackPressed() != true) {
            return
        }


此外,在AppTheme中添加样式(styles.xml)



<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>


各个样式如下:



 <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>


让我们检查是否已添加麦克风。我们启动该应用程序。



关于语法错误,我们遇到了很多错误。我们会按照IDE的建议修复所有问题。



加工!



图片



但是麦克风正潜入底部导航。让我们提高一点。在CustomAssistantButtonTheme中添加以上样式:



        <item name="aimybox_buttonMarginBottom">72dp</item>


更好!



图片



现在,让我们在那里连接一个助手,并检查他是否正常应答。为此,我们需要Aimybox控制台。



让我们从github帐户下的app.aimybox.com开始,创建一个新项目,连接一些技能(我连接了DateTime以进行测试),然后尝试在助手中提出适当的问题。在此处的设置中,在右上角,我们使用apiKey,将其插入到createAimybox中,而不是您的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))
    }


加工!



图片



仅英文文本,让我们在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>


万岁!



图片



这是代码存储库链接



在下一篇关于“ Habitika”助手的文章中,我将告诉您如何使用声音不仅查找天气,而且可以直接控制应用程序-浏览页面并添加习惯和任务。



All Articles