您的位置:首页 > 编程语言 > Go语言

将语音搜索集成到Google Now中

2015-12-10 21:19 615 查看
原文标题:Use Voice Search to integrate with Google Now

原文链接:http://antonioleiva.com/voice_search_google_now/

原文作者:Antonio Leiva(http://antonioleiva.com/about/

原文发布:2015-10-14

Android最棒的能力之一就是可以将我们的APP以不同的方式集成到它的生态链中。APP相互之间可以进行“交流”,这给予我们极大的灵活性来创建独特的应用体验。

集成Google应用就是一个典型的例子。有很多不同的特性可以帮助我们提升APP的知名度,如APP索引或一组强大的语音功能

语音搜索

尽管,在APP中实现语音搜索的过程与任何其他语音功能类似,但是,在这篇文章中,我还是会聚焦在怎样在APP中实现语音搜索功能。你也可以在Play Music中尝试这个例子:

好,Google在Play Music中,搜索Beatles

这句指令将在Play Music APP中开启搜索Beatles。

怎样实现语音搜索

当语音搜索启动后,我们的APP将收到一个查询文字的Intent,我们必须捕捉和分析这段文字。所以,这第一部分是指定哪个Activity接收这条Intent:

<activity android:name=".MainActivity" android:launchMode="singleTask" >
<intent-filter>
<action android:name="com.google.android.gms.actions.SEARCH_ACTION"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>


这个动作(action)称为com.google.android.gms.actions.SEARCH_ACTION,所以我们说MainActivity是处理这类Intent。另外,我用singleTask启动模式,这样MainActivity就仅仅创建一次。否则,每次收到这条Intent就要创建一个新的Activity实例。

接下来的一步是要在MainActivity内进行处理它。当我们用singleTask模式时,在Activity的两个不同位置上可以接收Intent:首先是用getIntent()创建Activity,接着是onNewIntent方法中。这样就要创建一个处理方法在需要它的时候调用它:

private static final String ACTION_VOICE_SEARCH = "com.google.android.gms.actions.SEARCH_ACTION";
  ...
private void handleVoiceSearch(Intent intent) {
if (intent != null && ACTION_VOICE_SEARCH.equals(intent.getAction())) {
String query = intent.getStringExtra(SearchManager.QUERY);
setSearchViewVisible(true);
searchView.setQuery(query, true);
}
}


这个方法检查Intent是否为空(null)或者是否是在收到查询文字前要检测的动作,它是Intent内部的额外动作。查询的额外动作关键字是SearchManager.QUERY。

在searchView后,设置查询,提交执行查询。其方法是onNewIntent:

@Override protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
handleVoiceSearch(intent);
}


UI也准备好(在我们的例子中,当菜单弹出时,我们访问SearchView),你稍后将看到。

在我的例子中,UI是基于工具栏(Toolbar)内部的SearchView。你可以在前面的文章中看到怎样实现SearchView,不过我还是稍作解释怎样做。首先,产生菜单动作(menu action):

<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_search"
android:title="@string/action_search"
android:icon="@drawable/ic_search"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="ifRoom" />
</menu>


然后,在菜单弹出时,你请求SearchView:

@Override public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);

MenuItem searchItem = menu.findItem(R.id.action_search);
searchView = (SearchView) MenuItemCompat.getActionView(searchItem);

searchView.setOnSearchClickListener(new View.OnClickListener() {
@Override public void onClick(View v) {
setSearchViewVisible(true);
}
});

searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override public boolean onQueryTextSubmit(String query) {
Toast.makeText(MainActivity.this, query, Toast.LENGTH_LONG).show();
searchView.clearFocus();
return true;
}

@Override public boolean onQueryTextChange(String newText) {
return false;
}
});

handleVoiceSearch(getIntent());

return true;
}

private void setSearchViewVisible(boolean visible) {

if (searchView.isIconified() == visible) {
searchView.setIconified(!visible);
}

if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(visible);
}
}


怎样尝试

由于APP需要发布到Play商店中,Google Now才能检测到APP,所以我们还不能直接从Google Now尝试这个例子。但是,我们可以用ADB来调试它。这条命令是:

adb shell am start -a com.google.android.gms.actions.SEARCH_ACTION -e query searchquery app_package

对于从我的代码库下载的例子,就可以这样做:

adb shell am start -a com.google.android.gms.actions.SEARCH_ACTION -e query VoiceSearch com.antonioleiva.googlenowsearch

在APP完全关闭和启动时,都可以尝试本例了。这样,就可以测试两种可能的途径了。

附加说明:Kotlin语言的实现

你可能知道,因为我认为Kotlin语言可以非常好替代Java语言,Kotlin可以使我们的代码更简洁、可读性更好,所以这些天讨论了许多Kotlin语言的特性。作为例子,我将简化onCreateOptionsMenu,你可以在同一个代码库中找到完整的代码。

实现扩展函数的能力可以帮助我们减少冗长代码。例如,可以为Menu创建一个扩展函数,基于action id找到ActionView,返回层级视图:

inline fun <reified T : View?> Menu.findCompatActionView(actionRes: Int): T {
val searchItem = findItem(actionRes)
return MenuItemCompat.getActionView(searchItem) as T
}


现在可以这样做:

searchView = menu.findCompatActionView(R.id.action_search)


另一个扩展函数可以以清晰的方式帮助我们编写OueryTextListener:

fun SearchView.onQueryText(submit: (String) -> Boolean = { false }, textChange: (String) -> Boolean = { false }) {

this.setOnQueryTextListener(object : SearchView.OnQueryTextListener {

override fun onQueryTextSubmit(query: String): Boolean = submit(query)

override fun onQueryTextChange(newText: String): Boolean = textChange(newText)

})
}


这个函数接收一对函数,一个用于侦听器中的每个方法,并给出它们的默认值。这样我们仅需要定义我们要用的。如果我们仅仅要第一个函数(对于第二个我们用默认的),现在我就可以这样做:

searchView.onQueryText ({
longToast(it)
searchView.clearFocus()
true
})


最终,这个函数就是这样的:

override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.main, menu)

searchView = menu.findCompatActionView(R.id.action_search)
searchView.setOnSearchClickListener { setSearchViewVisible(true) }

searchView.onQueryText ({
longToast(it)
searchView.clearFocus()
true
})

intent?.let { handleVoiceSearch(it) }

return true
}


如你所见,如果在这个位置上(例如:在一个常规activity创建上),Intent可以为null,就必须在使用它之前检查它是否为null。用let函数,可以避免if条件的创建,在对象调用时不为null,就可只进入对象内部。

如果你对Kotlin有兴趣,可以搜索Kotlin文章,或购买我编写的《Android开发者的Kotlin》一书。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: